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