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