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