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