• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 // Assembler to produce x86 instructions. Somewhat influenced by V8 assembler.
18 
19 #ifndef BERBERIS_ASSEMBLER_X86_32_H_
20 #define BERBERIS_ASSEMBLER_X86_32_H_
21 
22 #include <type_traits>  // std::is_same
23 
24 #include "berberis/assembler/x86_32_and_x86_64.h"
25 
26 namespace berberis {
27 
28 namespace x86_32 {
29 
30 class Assembler : public x86_32_and_x86_64::Assembler<Assembler> {
31  public:
32   using BaseAssembler = x86_32_and_x86_64::Assembler<Assembler>;
33   using FinalAssembler = Assembler;
34 
Assembler(MachineCode * code)35   explicit Assembler(MachineCode* code) : BaseAssembler(code) {}
36 
37   static constexpr Register no_register{0x80};
38   static constexpr Register eax{0};
39   static constexpr Register ecx{1};
40   static constexpr Register edx{2};
41   static constexpr Register ebx{3};
42   static constexpr Register esp{4};
43   static constexpr Register ebp{5};
44   static constexpr Register esi{6};
45   static constexpr Register edi{7};
46 
47   static constexpr XMMRegister xmm0{0};
48   static constexpr XMMRegister xmm1{1};
49   static constexpr XMMRegister xmm2{2};
50   static constexpr XMMRegister xmm3{3};
51   static constexpr XMMRegister xmm4{4};
52   static constexpr XMMRegister xmm5{5};
53   static constexpr XMMRegister xmm6{6};
54   static constexpr XMMRegister xmm7{7};
55 
56   static constexpr YMMRegister no_ymm_register{0x80};
57   static constexpr YMMRegister ymm0{0};
58   static constexpr YMMRegister ymm1{1};
59   static constexpr YMMRegister ymm2{2};
60   static constexpr YMMRegister ymm3{3};
61   static constexpr YMMRegister ymm4{4};
62   static constexpr YMMRegister ymm5{5};
63   static constexpr YMMRegister ymm6{6};
64   static constexpr YMMRegister ymm7{7};
65 
66   // Macroassembler uses these names to support both x86-32 and x86-64 modes.
67   static constexpr Register gpr_a{0};
68   static constexpr Register gpr_c{1};
69   static constexpr Register gpr_d{2};
70   static constexpr Register gpr_b{3};
71   static constexpr Register gpr_s{4};
72 
73 // Instructions.
74 #include "berberis/assembler/gen_assembler_x86_32-inl.h"  // NOLINT generated file!
75 
76   // Unhide Decl(Mem) hidden by Decl(Reg).
77   using BaseAssembler::Decl;
78 
79   // Unhide Decw(Mem) hidden by Decw(Reg).
80   using BaseAssembler::Decw;
81 
82   // Unhide Incl(Mem) hidden by Incl(Reg).
83   using BaseAssembler::Incl;
84 
85   // Unhide Incw(Mem) hidden by Incw(Reg).
86   using BaseAssembler::Incw;
87 
88   // Unhide Movb(Reg, Reg) hidden by special versions below.
89   using BaseAssembler::Movb;
90 
91   // Movb in 32-bit mode has certain optimizations not available in x86-64 mode
Movb(Register dest,const Operand & src)92   constexpr void Movb(Register dest, const Operand& src) {
93     if (IsAccumulator(dest) && src.base == no_register && src.index == no_register) {
94       EmitInstruction<0xA0>(src.disp);
95     } else {
96       BaseAssembler::Movb(dest, src);
97     }
98   }
99 
Movb(const Operand & dest,Register src)100   constexpr void Movb(const Operand& dest, Register src) {
101     if (dest.base == no_register && dest.index == no_register && IsAccumulator(src)) {
102       EmitInstruction<0xA2>(dest.disp);
103     } else {
104       BaseAssembler::Movb(dest, src);
105     }
106   }
107 
108   // Unhide Movw(Reg, Reg) hidden by special versions below.
109   using BaseAssembler::Movw;
110 
111   // Movw in 32-bit mode has certain optimizations not available in x86-64 mode
Movw(Register dest,const Operand & src)112   constexpr void Movw(Register dest, const Operand& src) {
113     if (IsAccumulator(dest) && src.base == no_register && src.index == no_register) {
114       EmitInstruction<0x66, 0xA1>(src.disp);
115     } else {
116       BaseAssembler::Movw(dest, src);
117     }
118   }
119 
Movw(const Operand & dest,Register src)120   constexpr void Movw(const Operand& dest, Register src) {
121     if (dest.base == no_register && dest.index == no_register && IsAccumulator(src)) {
122       EmitInstruction<0x66, 0xA3>(dest.disp);
123     } else {
124       BaseAssembler::Movw(dest, src);
125     }
126   }
127 
128   // Unhide Movl(Reg, Reg) hidden by special versions below.
129   using BaseAssembler::Movl;
130 
131   // Movl in 32-bit mode has certain optimizations not available in x86-64 mode
Movl(Register dest,const Operand & src)132   constexpr void Movl(Register dest, const Operand& src) {
133     if (IsAccumulator(dest) && src.base == no_register && src.index == no_register) {
134       EmitInstruction<0xA1>(src.disp);
135     } else {
136       BaseAssembler::Movl(dest, src);
137     }
138   }
139 
Movl(const Operand & dest,Register src)140   constexpr void Movl(const Operand& dest, Register src) {
141     if (dest.base == no_register && dest.index == no_register && IsAccumulator(src)) {
142       EmitInstruction<0xA3>(dest.disp);
143     } else {
144       BaseAssembler::Movl(dest, src);
145     }
146   }
147 
148   // Unhide Vmov*(Mem, Reg) hidden by Vmov*(Reg, Reg).
149   using BaseAssembler::Vmovapd;
150   using BaseAssembler::Vmovaps;
151   using BaseAssembler::Vmovdqa;
152   using BaseAssembler::Vmovdqu;
153   using BaseAssembler::Vmovq;
154   using BaseAssembler::Vmovsd;
155   using BaseAssembler::Vmovss;
156 
157   // TODO(b/127356868): decide what to do with these functions when cross-arch assembler is used.
158 
159 #if defined(__i386__)
160 
161   // Unside Call(Reg), hidden by special version below.
162   using BaseAssembler::Call;
163 
Call(const void * target)164   void Call(const void* target) {
165     Emit8(0xe8);
166     Emit32(0xcccccccc);
167     // Set last 4 bytes to displacement from current pc to 'target'.
168     AddRelocation(
169         pc() - 4, RelocationType::RelocAbsToDisp32, pc(), reinterpret_cast<intptr_t>(target));
170   }
171 
172   // Unside Jcc(Label), hidden by special version below.
173   using BaseAssembler::Jcc;
174 
175   // Make sure only type void* can be passed to function below, not Label* or any other pointer.
176   template <typename T>
177   auto Jcc(Condition cc, T* target) -> void = delete;
178 
179   template <typename T>
180   auto Jcc(Condition cc, T target)
181       -> std::enable_if_t<std::is_integral_v<T> && sizeof(uintptr_t) < sizeof(T)> = delete;
182 
Jcc(Condition cc,uintptr_t target)183   void Jcc(Condition cc, uintptr_t target) {
184     if (cc == Condition::kAlways) {
185       Jmp(target);
186       return;
187     } else if (cc == Condition::kNever) {
188       return;
189     }
190     CHECK_EQ(0, static_cast<uint8_t>(cc) & 0xF0);
191     Emit8(0x0F);
192     Emit8(0x80 | static_cast<uint8_t>(cc));
193     Emit32(0xcccccccc);
194     // Set last 4 bytes to displacement from current pc to 'target'.
195     AddRelocation(pc() - 4, RelocationType::RelocAbsToDisp32, pc(), bit_cast<intptr_t>(target));
196   }
197 
Jcc(Condition cc,const void * target)198   void Jcc(Condition cc, const void* target) { Jcc(cc, bit_cast<uintptr_t>(target)); }
199 
200   // Unside Jmp(Reg), hidden by special version below.
201   using BaseAssembler::Jmp;
202 
203   // Make sure only type void* can be passed to function below, not Label* or any other pointer.
204   template <typename T>
205   auto Jmp(T* target) -> void = delete;
206 
207   template <typename T>
208   auto Jmp(T target)
209       -> std::enable_if_t<std::is_integral_v<T> && sizeof(uintptr_t) < sizeof(T)> = delete;
210 
Jmp(uintptr_t target)211   void Jmp(uintptr_t target) {
212     Emit8(0xe9);
213     Emit32(0xcccccccc);
214     // Set last 4 bytes to displacement from current pc to 'target'.
215     AddRelocation(pc() - 4, RelocationType::RelocAbsToDisp32, pc(), bit_cast<intptr_t>(target));
216   }
217 
Jmp(const void * target)218   void Jmp(const void* target) { Jmp(bit_cast<uintptr_t>(target)); }
219 
220 #endif  // defined(__i386__)
221 
222  private:
223   Assembler() = delete;
224   Assembler(const Assembler&) = delete;
225   Assembler(Assembler&&) = delete;
226   void operator=(const Assembler&) = delete;
227   void operator=(Assembler&&) = delete;
228   using DerivedAssemblerType = Assembler;
229 
Accumulator()230   static Register Accumulator() { return eax; }
IsAccumulator(Register reg)231   static bool IsAccumulator(Register reg) { return reg == eax; }
232 
233   // Check if a given type is "a register with size" (for EmitInstruction).
234   template <typename ArgumentType>
235   struct IsRegister {
236     static constexpr bool value =
237         std::is_same_v<ArgumentType, Register8Bit> || std::is_same_v<ArgumentType, Register32Bit>;
238   };
239 
240   // Check if a given type is "a memory operand with size" (for EmitInstruction).
241   template <typename ArgumentType>
242   struct IsMemoryOperand {
243     static constexpr bool value = std::is_same_v<ArgumentType, Memory32Bit>;
244   };
245 
246   // Check if a given type is "a memory operand with size" (for EmitInstruction).
247   template <typename ArgumentType>
248   struct IsLabelOperand {
249     static constexpr bool value = std::is_same_v<ArgumentType, Label32Bit>;
250   };
251 
252   template <typename... ArgumentsType>
EmitRex(ArgumentsType...)253   void EmitRex(ArgumentsType...) {
254     // There is no REX in 32-bit mode thus we don't need to do anything here.
255   }
256 
257   template <typename RegisterType>
IsSwapProfitable(RegisterType,RegisterType)258   [[nodiscard]] static bool IsSwapProfitable(RegisterType /*rm_arg*/, RegisterType /*vex_arg*/) {
259     // In 32bit mode swapping register to shorten VEX prefix is not possible.
260     return false;
261   }
262 
263   template <uint8_t byte1,
264             uint8_t byte2,
265             uint8_t byte3,
266             bool reg_is_opcode_extension,
267             typename... ArgumentsTypes>
EmitVex(ArgumentsTypes...arguments)268   void EmitVex(ArgumentsTypes... arguments) {
269     constexpr auto registers_count = kCountArguments<IsRegister, ArgumentsTypes...>;
270     constexpr auto operands_count = kCountArguments<IsMemoryOperand, ArgumentsTypes...>;
271     constexpr auto labels_count = kCountArguments<IsLabelOperand, ArgumentsTypes...>;
272     constexpr auto vvvv_parameter = 2 - reg_is_opcode_extension - operands_count - labels_count;
273     int vvvv = 0;
274     if constexpr (registers_count > vvvv_parameter) {
275       vvvv = ArgumentByType<vvvv_parameter, IsRegister>(arguments...).num_;
276     }
277     // Note that ¬R is always 1 in x86-32 mode but it's not set in JSON.
278     // This means that 2nd byte of 3-byte vex is always the same in 32bit mode (but 3rd byte of
279     // unfolded version and 2nd byte of folded one may still need to handle vvvv argument).
280     if (byte1 == 0xC4 && byte2 == 0b0'0'0'00001 && (byte3 & 0b1'0000'0'00) == 0) {
281       Emit16((0x80c5 | (byte3 << 8) | 0b0'1111'0'00'00000000) ^ (vvvv << 11));
282     } else {
283       Emit8(byte1);
284       // Note that ¬R/¬X/¬B are always 1 in x86-32 mode. But they are specified as 0 in JSON.
285       Emit16(((byte2 | 0b111'00000) | (byte3 << 8) | 0b0'1111'000'00000000) ^ (vvvv << 11));
286     }
287   }
288 
289   template <typename ArgumentType>
EmitRegisterInOpcode(uint8_t opcode,ArgumentType argument)290   void EmitRegisterInOpcode(uint8_t opcode, ArgumentType argument) {
291     Emit8(opcode | argument.num_);
292   }
293 
294   template <typename ArgumentType1, typename ArgumentType2>
EmitModRM(ArgumentType1 argument1,ArgumentType2 argument2)295   void EmitModRM(ArgumentType1 argument1, ArgumentType2 argument2) {
296     Emit8(0xC0 | (argument1.num_ << 3) | argument2.num_);
297   }
298 
299   template <typename ArgumentType>
EmitModRM(uint8_t opcode_extension,ArgumentType argument)300   void EmitModRM(uint8_t opcode_extension, ArgumentType argument) {
301     CHECK_LE(opcode_extension, 7);
302     Emit8(0xC0 | (opcode_extension << 3) | argument.num_);
303   }
304 
305   template <typename ArgumentType>
EmitOperandOp(ArgumentType argument,Operand operand)306   void EmitOperandOp(ArgumentType argument, Operand operand) {
307     EmitOperandOp(static_cast<int>(argument.num_), operand);
308   }
309 
310   template <size_t kImmediatesSize, typename ArgumentType>
EmitRipOp(ArgumentType argument,const Label & label)311   void EmitRipOp(ArgumentType argument, const Label& label) {
312     EmitRipOp<kImmediatesSize>(static_cast<int>(argument.num_), label);
313   }
314 
315   // Emit the ModR/M byte, and optionally the SIB byte and
316   // 1- or 4-byte offset for a memory operand.  Also used to encode
317   // a three-bit opcode extension into the ModR/M byte.
318   constexpr void EmitOperandOp(int num_ber, const Operand& addr);
319   // Helper functions to handle various ModR/M and SIB combinations.
320   // Should *only* be called from EmitOperandOp!
321   constexpr void EmitIndexDispOperand(int reg, const Operand& addr);
322   template <typename ArgType, void (AssemblerBase::*)(ArgType)>
323   constexpr void EmitBaseIndexDispOperand(int base_modrm_and_sib, const Operand& addr);
324   // Emit ModR/M for rip-addressig.
325   template <size_t kImmediatesSize>
326   constexpr void EmitRipOp(int num_, const Label& label);
327 
328   friend BaseAssembler;
329 };
330 
331 // This function looks big, but when we are emitting Operand with fixed registers
332 // (which is the most common case) all "if"s below are calculated statically which
333 // makes effective size of that function very small.
334 //
335 // But for this to happen function have to be inline and in header.
EmitOperandOp(int num_ber,const Operand & addr)336 constexpr inline void Assembler::EmitOperandOp(int num_ber, const Operand& addr) {
337   // Additional info (register num_ber, etc) is limited to 3 bits.
338   CHECK_LE(unsigned(num_ber), 7);
339 
340   // Reg field must be shifted by 3 bits.
341   int reg = num_ber << 3;
342 
343   // On x86 %esp cannot be index, only base.
344   CHECK(addr.index != esp);
345 
346   // If base is not %esp and we don't have index, then we don't have SIB byte.
347   // All other cases have "ModR/M" and SIB bytes.
348   if (addr.base != esp && addr.index == no_register) {
349     // If we have base register then we could use the same logic as for other common cases.
350     if (addr.base != no_register) {
351       EmitBaseIndexDispOperand<uint8_t, &Assembler::Emit8>(addr.base.num_ | reg, addr);
352     } else {
353       Emit8(0x05 | reg);
354       Emit32(addr.disp);
355     }
356   } else if (addr.index == no_register) {
357     // Note: when ModR/M and SIB are used "no index" is encoded as if %esp is used in place of
358     // index (that's why %esp couldn't be used as index - see check above).
359     EmitBaseIndexDispOperand<int16_t, &Assembler::Emit16>(0x2004 | (addr.base.num_ << 8) | reg,
360                                                           addr);
361   } else if (addr.base == no_register) {
362     EmitIndexDispOperand(reg, addr);
363   } else {
364     EmitBaseIndexDispOperand<int16_t, &Assembler::Emit16>(
365         0x04 | (addr.scale << 14) | (addr.index.num_ << 11) | (addr.base.num_ << 8) | reg, addr);
366   }
367 }
368 
EmitIndexDispOperand(int reg,const Operand & addr)369 constexpr inline void Assembler::EmitIndexDispOperand(int reg, const Operand& addr) {
370   // We only have index here, no base, use SIB but put %ebp in "base" field.
371   Emit16(0x0504 | (addr.scale << 14) | (addr.index.num_ << 11) | reg);
372   Emit32(addr.disp);
373 }
374 
375 template <typename ArgType, void (AssemblerBase::* EmitBase)(ArgType)>
EmitBaseIndexDispOperand(int base_modrm_and_sib,const Operand & addr)376 constexpr inline void Assembler::EmitBaseIndexDispOperand(int base_modrm_and_sib,
377                                                           const Operand& addr) {
378   if (addr.disp == 0 && addr.base != ebp) {
379     // We can omit zero displacement only if base isn't %ebp
380     (this->*EmitBase)(base_modrm_and_sib);
381   } else if (IsInRange<int8_t>(addr.disp)) {
382     // If disp could it in byte then use byte-disp.
383     (this->*EmitBase)(base_modrm_and_sib | 0x40);
384     Emit8(addr.disp);
385   } else {
386     // Otherwise use full-disp.
387     (this->*EmitBase)(base_modrm_and_sib | 0x80);
388     Emit32(addr.disp);
389   }
390 }
391 
392 }  // namespace x86_32
393 
394 }  // namespace berberis
395 
396 #endif  // BERBERIS_ASSEMBLER_X86_32_H_
397