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