File: | out/../deps/v8/src/diagnostics/x64/disasm-x64.cc |
Warning: | line 2777, column 3 Value stored to 'outp' is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | // Copyright 2011 the V8 project authors. All rights reserved. |
2 | // Use of this source code is governed by a BSD-style license that can be |
3 | // found in the LICENSE file. |
4 | |
5 | #include <cassert> |
6 | #include <cinttypes> |
7 | #include <cstdarg> |
8 | #include <cstdio> |
9 | |
10 | #if V8_TARGET_ARCH_X641 |
11 | |
12 | #include "src/base/compiler-specific.h" |
13 | #include "src/base/lazy-instance.h" |
14 | #include "src/base/memory.h" |
15 | #include "src/base/strings.h" |
16 | #include "src/base/v8-fallthrough.h" |
17 | #include "src/codegen/x64/fma-instr.h" |
18 | #include "src/codegen/x64/register-x64.h" |
19 | #include "src/codegen/x64/sse-instr.h" |
20 | #include "src/common/globals.h" |
21 | #include "src/diagnostics/disasm.h" |
22 | |
23 | namespace disasm { |
24 | |
25 | enum OperandType { |
26 | UNSET_OP_ORDER = 0, |
27 | // Operand size decides between 16, 32 and 64 bit operands. |
28 | REG_OPER_OP_ORDER = 1, // Register destination, operand source. |
29 | OPER_REG_OP_ORDER = 2, // Operand destination, register source. |
30 | // Fixed 8-bit operands. |
31 | BYTE_SIZE_OPERAND_FLAG = 4, |
32 | BYTE_REG_OPER_OP_ORDER = REG_OPER_OP_ORDER | BYTE_SIZE_OPERAND_FLAG, |
33 | BYTE_OPER_REG_OP_ORDER = OPER_REG_OP_ORDER | BYTE_SIZE_OPERAND_FLAG, |
34 | // XMM registers/operands can be mixed with normal operands. |
35 | OPER_XMMREG_OP_ORDER, |
36 | XMMREG_OPER_OP_ORDER, |
37 | XMMREG_XMMOPER_OP_ORDER, |
38 | XMMOPER_XMMREG_OP_ORDER, |
39 | }; |
40 | |
41 | //------------------------------------------------------------------ |
42 | // Tables |
43 | //------------------------------------------------------------------ |
44 | struct ByteMnemonic { |
45 | int b; // -1 terminates, otherwise must be in range (0..255) |
46 | OperandType op_order_; |
47 | const char* mnem; |
48 | }; |
49 | |
50 | static const ByteMnemonic two_operands_instr[] = { |
51 | {0x00, BYTE_OPER_REG_OP_ORDER, "add"}, |
52 | {0x01, OPER_REG_OP_ORDER, "add"}, |
53 | {0x02, BYTE_REG_OPER_OP_ORDER, "add"}, |
54 | {0x03, REG_OPER_OP_ORDER, "add"}, |
55 | {0x08, BYTE_OPER_REG_OP_ORDER, "or"}, |
56 | {0x09, OPER_REG_OP_ORDER, "or"}, |
57 | {0x0A, BYTE_REG_OPER_OP_ORDER, "or"}, |
58 | {0x0B, REG_OPER_OP_ORDER, "or"}, |
59 | {0x10, BYTE_OPER_REG_OP_ORDER, "adc"}, |
60 | {0x11, OPER_REG_OP_ORDER, "adc"}, |
61 | {0x12, BYTE_REG_OPER_OP_ORDER, "adc"}, |
62 | {0x13, REG_OPER_OP_ORDER, "adc"}, |
63 | {0x18, BYTE_OPER_REG_OP_ORDER, "sbb"}, |
64 | {0x19, OPER_REG_OP_ORDER, "sbb"}, |
65 | {0x1A, BYTE_REG_OPER_OP_ORDER, "sbb"}, |
66 | {0x1B, REG_OPER_OP_ORDER, "sbb"}, |
67 | {0x20, BYTE_OPER_REG_OP_ORDER, "and"}, |
68 | {0x21, OPER_REG_OP_ORDER, "and"}, |
69 | {0x22, BYTE_REG_OPER_OP_ORDER, "and"}, |
70 | {0x23, REG_OPER_OP_ORDER, "and"}, |
71 | {0x28, BYTE_OPER_REG_OP_ORDER, "sub"}, |
72 | {0x29, OPER_REG_OP_ORDER, "sub"}, |
73 | {0x2A, BYTE_REG_OPER_OP_ORDER, "sub"}, |
74 | {0x2B, REG_OPER_OP_ORDER, "sub"}, |
75 | {0x30, BYTE_OPER_REG_OP_ORDER, "xor"}, |
76 | {0x31, OPER_REG_OP_ORDER, "xor"}, |
77 | {0x32, BYTE_REG_OPER_OP_ORDER, "xor"}, |
78 | {0x33, REG_OPER_OP_ORDER, "xor"}, |
79 | {0x38, BYTE_OPER_REG_OP_ORDER, "cmp"}, |
80 | {0x39, OPER_REG_OP_ORDER, "cmp"}, |
81 | {0x3A, BYTE_REG_OPER_OP_ORDER, "cmp"}, |
82 | {0x3B, REG_OPER_OP_ORDER, "cmp"}, |
83 | {0x63, REG_OPER_OP_ORDER, "movsxl"}, |
84 | {0x84, BYTE_REG_OPER_OP_ORDER, "test"}, |
85 | {0x85, REG_OPER_OP_ORDER, "test"}, |
86 | {0x86, BYTE_REG_OPER_OP_ORDER, "xchg"}, |
87 | {0x87, REG_OPER_OP_ORDER, "xchg"}, |
88 | {0x88, BYTE_OPER_REG_OP_ORDER, "mov"}, |
89 | {0x89, OPER_REG_OP_ORDER, "mov"}, |
90 | {0x8A, BYTE_REG_OPER_OP_ORDER, "mov"}, |
91 | {0x8B, REG_OPER_OP_ORDER, "mov"}, |
92 | {0x8D, REG_OPER_OP_ORDER, "lea"}, |
93 | {-1, UNSET_OP_ORDER, ""}}; |
94 | |
95 | static const ByteMnemonic zero_operands_instr[] = { |
96 | {0xC3, UNSET_OP_ORDER, "ret"}, {0xC9, UNSET_OP_ORDER, "leave"}, |
97 | {0xF4, UNSET_OP_ORDER, "hlt"}, {0xFC, UNSET_OP_ORDER, "cld"}, |
98 | {0xCC, UNSET_OP_ORDER, "int3"}, {0x60, UNSET_OP_ORDER, "pushad"}, |
99 | {0x61, UNSET_OP_ORDER, "popad"}, {0x9C, UNSET_OP_ORDER, "pushfd"}, |
100 | {0x9D, UNSET_OP_ORDER, "popfd"}, {0x9E, UNSET_OP_ORDER, "sahf"}, |
101 | {0x99, UNSET_OP_ORDER, "cdq"}, {0x9B, UNSET_OP_ORDER, "fwait"}, |
102 | {0xAB, UNSET_OP_ORDER, "stos"}, {0xA4, UNSET_OP_ORDER, "movs"}, |
103 | {0xA5, UNSET_OP_ORDER, "movs"}, {0xA6, UNSET_OP_ORDER, "cmps"}, |
104 | {0xA7, UNSET_OP_ORDER, "cmps"}, {-1, UNSET_OP_ORDER, ""}}; |
105 | |
106 | static const ByteMnemonic call_jump_instr[] = {{0xE8, UNSET_OP_ORDER, "call"}, |
107 | {0xE9, UNSET_OP_ORDER, "jmp"}, |
108 | {-1, UNSET_OP_ORDER, ""}}; |
109 | |
110 | static const ByteMnemonic short_immediate_instr[] = { |
111 | {0x05, UNSET_OP_ORDER, "add"}, {0x0D, UNSET_OP_ORDER, "or"}, |
112 | {0x15, UNSET_OP_ORDER, "adc"}, {0x1D, UNSET_OP_ORDER, "sbb"}, |
113 | {0x25, UNSET_OP_ORDER, "and"}, {0x2D, UNSET_OP_ORDER, "sub"}, |
114 | {0x35, UNSET_OP_ORDER, "xor"}, {0x3D, UNSET_OP_ORDER, "cmp"}, |
115 | {-1, UNSET_OP_ORDER, ""}}; |
116 | |
117 | static const char* const conditional_code_suffix[] = { |
118 | "o", "no", "c", "nc", "z", "nz", "na", "a", |
119 | "s", "ns", "pe", "po", "l", "ge", "le", "g"}; |
120 | |
121 | enum InstructionType { |
122 | NO_INSTR, |
123 | ZERO_OPERANDS_INSTR, |
124 | TWO_OPERANDS_INSTR, |
125 | JUMP_CONDITIONAL_SHORT_INSTR, |
126 | REGISTER_INSTR, |
127 | PUSHPOP_INSTR, // Has implicit 64-bit operand size. |
128 | MOVE_REG_INSTR, |
129 | CALL_JUMP_INSTR, |
130 | SHORT_IMMEDIATE_INSTR |
131 | }; |
132 | |
133 | enum Prefixes { |
134 | ESCAPE_PREFIX = 0x0F, |
135 | OPERAND_SIZE_OVERRIDE_PREFIX = 0x66, |
136 | ADDRESS_SIZE_OVERRIDE_PREFIX = 0x67, |
137 | VEX3_PREFIX = 0xC4, |
138 | VEX2_PREFIX = 0xC5, |
139 | LOCK_PREFIX = 0xF0, |
140 | REPNE_PREFIX = 0xF2, |
141 | REP_PREFIX = 0xF3, |
142 | REPEQ_PREFIX = REP_PREFIX |
143 | }; |
144 | |
145 | struct InstructionDesc { |
146 | const char* mnem; |
147 | InstructionType type; |
148 | OperandType op_order_; |
149 | bool byte_size_operation; // Fixed 8-bit operation. |
150 | }; |
151 | |
152 | class InstructionTable { |
153 | public: |
154 | InstructionTable(); |
155 | const InstructionDesc& Get(byte x) const { return instructions_[x]; } |
156 | |
157 | private: |
158 | InstructionDesc instructions_[256]; |
159 | void Clear(); |
160 | void Init(); |
161 | void CopyTable(const ByteMnemonic bm[], InstructionType type); |
162 | void SetTableRange(InstructionType type, byte start, byte end, bool byte_size, |
163 | const char* mnem); |
164 | void AddJumpConditionalShort(); |
165 | }; |
166 | |
167 | InstructionTable::InstructionTable() { |
168 | Clear(); |
169 | Init(); |
170 | } |
171 | |
172 | void InstructionTable::Clear() { |
173 | for (int i = 0; i < 256; i++) { |
174 | instructions_[i].mnem = "(bad)"; |
175 | instructions_[i].type = NO_INSTR; |
176 | instructions_[i].op_order_ = UNSET_OP_ORDER; |
177 | instructions_[i].byte_size_operation = false; |
178 | } |
179 | } |
180 | |
181 | void InstructionTable::Init() { |
182 | CopyTable(two_operands_instr, TWO_OPERANDS_INSTR); |
183 | CopyTable(zero_operands_instr, ZERO_OPERANDS_INSTR); |
184 | CopyTable(call_jump_instr, CALL_JUMP_INSTR); |
185 | CopyTable(short_immediate_instr, SHORT_IMMEDIATE_INSTR); |
186 | AddJumpConditionalShort(); |
187 | SetTableRange(PUSHPOP_INSTR, 0x50, 0x57, false, "push"); |
188 | SetTableRange(PUSHPOP_INSTR, 0x58, 0x5F, false, "pop"); |
189 | SetTableRange(MOVE_REG_INSTR, 0xB8, 0xBF, false, "mov"); |
190 | } |
191 | |
192 | void InstructionTable::CopyTable(const ByteMnemonic bm[], |
193 | InstructionType type) { |
194 | for (int i = 0; bm[i].b >= 0; i++) { |
195 | InstructionDesc* id = &instructions_[bm[i].b]; |
196 | id->mnem = bm[i].mnem; |
197 | OperandType op_order = bm[i].op_order_; |
198 | id->op_order_ = |
199 | static_cast<OperandType>(op_order & ~BYTE_SIZE_OPERAND_FLAG); |
200 | DCHECK_EQ(NO_INSTR, id->type)((void) 0); // Information not already entered |
201 | id->type = type; |
202 | id->byte_size_operation = ((op_order & BYTE_SIZE_OPERAND_FLAG) != 0); |
203 | } |
204 | } |
205 | |
206 | void InstructionTable::SetTableRange(InstructionType type, byte start, byte end, |
207 | bool byte_size, const char* mnem) { |
208 | for (byte b = start; b <= end; b++) { |
209 | InstructionDesc* id = &instructions_[b]; |
210 | DCHECK_EQ(NO_INSTR, id->type)((void) 0); // Information not already entered |
211 | id->mnem = mnem; |
212 | id->type = type; |
213 | id->byte_size_operation = byte_size; |
214 | } |
215 | } |
216 | |
217 | void InstructionTable::AddJumpConditionalShort() { |
218 | for (byte b = 0x70; b <= 0x7F; b++) { |
219 | InstructionDesc* id = &instructions_[b]; |
220 | DCHECK_EQ(NO_INSTR, id->type)((void) 0); // Information not already entered |
221 | id->mnem = nullptr; // Computed depending on condition code. |
222 | id->type = JUMP_CONDITIONAL_SHORT_INSTR; |
223 | } |
224 | } |
225 | |
226 | namespace { |
227 | DEFINE_LAZY_LEAKY_OBJECT_GETTER(InstructionTable, GetInstructionTable)InstructionTable* GetInstructionTable() { static ::v8::base:: LeakyObject<InstructionTable> object{}; return object.get (); } |
228 | } // namespace |
229 | |
230 | static const InstructionDesc cmov_instructions[16] = { |
231 | {"cmovo", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false}, |
232 | {"cmovno", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false}, |
233 | {"cmovc", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false}, |
234 | {"cmovnc", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false}, |
235 | {"cmovz", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false}, |
236 | {"cmovnz", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false}, |
237 | {"cmovna", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false}, |
238 | {"cmova", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false}, |
239 | {"cmovs", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false}, |
240 | {"cmovns", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false}, |
241 | {"cmovpe", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false}, |
242 | {"cmovpo", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false}, |
243 | {"cmovl", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false}, |
244 | {"cmovge", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false}, |
245 | {"cmovle", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false}, |
246 | {"cmovg", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false}}; |
247 | |
248 | static const char* const cmp_pseudo_op[16] = { |
249 | "eq", "lt", "le", "unord", "neq", "nlt", "nle", "ord", |
250 | "eq_uq", "nge", "ngt", "false", "neq_oq", "ge", "gt", "true"}; |
251 | |
252 | namespace { |
253 | int8_t Imm8(const uint8_t* data) { |
254 | return *reinterpret_cast<const int8_t*>(data); |
255 | } |
256 | uint8_t Imm8_U(const uint8_t* data) { |
257 | return *reinterpret_cast<const uint8_t*>(data); |
258 | } |
259 | int16_t Imm16(const uint8_t* data) { |
260 | return v8::base::ReadUnalignedValue<int16_t>( |
261 | reinterpret_cast<v8::internal::Address>(data)); |
262 | } |
263 | uint16_t Imm16_U(const uint8_t* data) { |
264 | return v8::base::ReadUnalignedValue<uint16_t>( |
265 | reinterpret_cast<v8::internal::Address>(data)); |
266 | } |
267 | int32_t Imm32(const uint8_t* data) { |
268 | return v8::base::ReadUnalignedValue<int32_t>( |
269 | reinterpret_cast<v8::internal::Address>(data)); |
270 | } |
271 | uint32_t Imm32_U(const uint8_t* data) { |
272 | return v8::base::ReadUnalignedValue<uint32_t>( |
273 | reinterpret_cast<v8::internal::Address>(data)); |
274 | } |
275 | int64_t Imm64(const uint8_t* data) { |
276 | return v8::base::ReadUnalignedValue<int64_t>( |
277 | reinterpret_cast<v8::internal::Address>(data)); |
278 | } |
279 | } // namespace |
280 | |
281 | //------------------------------------------------------------------------------ |
282 | // DisassemblerX64 implementation. |
283 | |
284 | // Forward-declare NameOfYMMRegister to keep its implementation with the |
285 | // NameConverter methods and register name arrays at bottom. |
286 | const char* NameOfYMMRegister(int reg); |
287 | |
288 | // A new DisassemblerX64 object is created to disassemble each instruction. |
289 | // The object can only disassemble a single instruction. |
290 | class DisassemblerX64 { |
291 | public: |
292 | DisassemblerX64(const NameConverter& converter, |
293 | Disassembler::UnimplementedOpcodeAction unimplemented_action) |
294 | : converter_(converter), |
295 | tmp_buffer_pos_(0), |
296 | abort_on_unimplemented_(unimplemented_action == |
297 | Disassembler::kAbortOnUnimplementedOpcode), |
298 | rex_(0), |
299 | operand_size_(0), |
300 | group_1_prefix_(0), |
301 | vex_byte0_(0), |
302 | vex_byte1_(0), |
303 | vex_byte2_(0), |
304 | byte_size_operand_(false), |
305 | instruction_table_(GetInstructionTable()) { |
306 | tmp_buffer_[0] = '\0'; |
307 | } |
308 | |
309 | // Writes one disassembled instruction into 'buffer' (0-terminated). |
310 | // Returns the length of the disassembled machine instruction in bytes. |
311 | int InstructionDecode(v8::base::Vector<char> buffer, byte* instruction); |
312 | |
313 | private: |
314 | enum OperandSize { |
315 | OPERAND_BYTE_SIZE = 0, |
316 | OPERAND_WORD_SIZE = 1, |
317 | OPERAND_DOUBLEWORD_SIZE = 2, |
318 | OPERAND_QUADWORD_SIZE = 3 |
319 | }; |
320 | |
321 | const NameConverter& converter_; |
322 | v8::base::EmbeddedVector<char, 128> tmp_buffer_; |
323 | unsigned int tmp_buffer_pos_; |
324 | bool abort_on_unimplemented_; |
325 | // Prefixes parsed |
326 | byte rex_; |
327 | byte operand_size_; // 0x66 or (if no group 3 prefix is present) 0x0. |
328 | byte group_1_prefix_; // 0xF2, 0xF3, or (if no group 1 prefix is present) 0. |
329 | byte vex_byte0_; // 0xC4 or 0xC5 |
330 | byte vex_byte1_; |
331 | byte vex_byte2_; // only for 3 bytes vex prefix |
332 | // Byte size operand override. |
333 | bool byte_size_operand_; |
334 | const InstructionTable* const instruction_table_; |
335 | |
336 | void setRex(byte rex) { |
337 | DCHECK_EQ(0x40, rex & 0xF0)((void) 0); |
338 | rex_ = rex; |
339 | } |
340 | |
341 | bool rex() { return rex_ != 0; } |
342 | |
343 | bool rex_b() { return (rex_ & 0x01) != 0; } |
344 | |
345 | // Actual number of base register given the low bits and the rex.b state. |
346 | int base_reg(int low_bits) { return low_bits | ((rex_ & 0x01) << 3); } |
347 | |
348 | bool rex_x() { return (rex_ & 0x02) != 0; } |
349 | |
350 | bool rex_r() { return (rex_ & 0x04) != 0; } |
351 | |
352 | bool rex_w() { return (rex_ & 0x08) != 0; } |
353 | |
354 | bool vex_w() { |
355 | DCHECK(vex_byte0_ == VEX3_PREFIX || vex_byte0_ == VEX2_PREFIX)((void) 0); |
356 | return vex_byte0_ == VEX3_PREFIX ? (vex_byte2_ & 0x80) != 0 : false; |
357 | } |
358 | |
359 | bool vex_128() { |
360 | DCHECK(vex_byte0_ == VEX3_PREFIX || vex_byte0_ == VEX2_PREFIX)((void) 0); |
361 | byte checked = vex_byte0_ == VEX3_PREFIX ? vex_byte2_ : vex_byte1_; |
362 | return (checked & 4) == 0; |
363 | } |
364 | |
365 | bool vex_256() const { |
366 | DCHECK(vex_byte0_ == VEX3_PREFIX || vex_byte0_ == VEX2_PREFIX)((void) 0); |
367 | byte checked = vex_byte0_ == VEX3_PREFIX ? vex_byte2_ : vex_byte1_; |
368 | return (checked & 4) != 0; |
369 | } |
370 | |
371 | bool vex_none() { |
372 | DCHECK(vex_byte0_ == VEX3_PREFIX || vex_byte0_ == VEX2_PREFIX)((void) 0); |
373 | byte checked = vex_byte0_ == VEX3_PREFIX ? vex_byte2_ : vex_byte1_; |
374 | return (checked & 3) == 0; |
375 | } |
376 | |
377 | bool vex_66() { |
378 | DCHECK(vex_byte0_ == VEX3_PREFIX || vex_byte0_ == VEX2_PREFIX)((void) 0); |
379 | byte checked = vex_byte0_ == VEX3_PREFIX ? vex_byte2_ : vex_byte1_; |
380 | return (checked & 3) == 1; |
381 | } |
382 | |
383 | bool vex_f3() { |
384 | DCHECK(vex_byte0_ == VEX3_PREFIX || vex_byte0_ == VEX2_PREFIX)((void) 0); |
385 | byte checked = vex_byte0_ == VEX3_PREFIX ? vex_byte2_ : vex_byte1_; |
386 | return (checked & 3) == 2; |
387 | } |
388 | |
389 | bool vex_f2() { |
390 | DCHECK(vex_byte0_ == VEX3_PREFIX || vex_byte0_ == VEX2_PREFIX)((void) 0); |
391 | byte checked = vex_byte0_ == VEX3_PREFIX ? vex_byte2_ : vex_byte1_; |
392 | return (checked & 3) == 3; |
393 | } |
394 | |
395 | bool vex_0f() { |
396 | if (vex_byte0_ == VEX2_PREFIX) return true; |
397 | return (vex_byte1_ & 3) == 1; |
398 | } |
399 | |
400 | bool vex_0f38() { |
401 | if (vex_byte0_ == VEX2_PREFIX) return false; |
402 | return (vex_byte1_ & 3) == 2; |
403 | } |
404 | |
405 | bool vex_0f3a() { |
406 | if (vex_byte0_ == VEX2_PREFIX) return false; |
407 | return (vex_byte1_ & 3) == 3; |
408 | } |
409 | |
410 | int vex_vreg() { |
411 | DCHECK(vex_byte0_ == VEX3_PREFIX || vex_byte0_ == VEX2_PREFIX)((void) 0); |
412 | byte checked = vex_byte0_ == VEX3_PREFIX ? vex_byte2_ : vex_byte1_; |
413 | return ~(checked >> 3) & 0xF; |
414 | } |
415 | |
416 | OperandSize operand_size() { |
417 | if (byte_size_operand_) return OPERAND_BYTE_SIZE; |
418 | if (rex_w()) return OPERAND_QUADWORD_SIZE; |
419 | if (operand_size_ != 0) return OPERAND_WORD_SIZE; |
420 | return OPERAND_DOUBLEWORD_SIZE; |
421 | } |
422 | |
423 | char operand_size_code() { return "bwlq"[operand_size()]; } |
424 | |
425 | char float_size_code() { return "sd"[rex_w()]; } |
426 | |
427 | const char* NameOfCPURegister(int reg) const { |
428 | return converter_.NameOfCPURegister(reg); |
429 | } |
430 | |
431 | const char* NameOfByteCPURegister(int reg) const { |
432 | return converter_.NameOfByteCPURegister(reg); |
433 | } |
434 | |
435 | const char* NameOfXMMRegister(int reg) const { |
436 | return converter_.NameOfXMMRegister(reg); |
437 | } |
438 | |
439 | const char* NameOfAVXRegister(int reg) const { |
440 | if (vex_256()) { |
441 | return NameOfYMMRegister(reg); |
442 | } else { |
443 | return converter_.NameOfXMMRegister(reg); |
444 | } |
445 | } |
446 | |
447 | const char* NameOfAddress(byte* addr) const { |
448 | return converter_.NameOfAddress(addr); |
449 | } |
450 | |
451 | // Disassembler helper functions. |
452 | void get_modrm(byte data, int* mod, int* regop, int* rm) { |
453 | *mod = (data >> 6) & 3; |
454 | *regop = ((data & 0x38) >> 3) | (rex_r() ? 8 : 0); |
455 | *rm = (data & 7) | (rex_b() ? 8 : 0); |
456 | } |
457 | |
458 | void get_sib(byte data, int* scale, int* index, int* base) { |
459 | *scale = (data >> 6) & 3; |
460 | *index = ((data >> 3) & 7) | (rex_x() ? 8 : 0); |
461 | *base = (data & 7) | (rex_b() ? 8 : 0); |
462 | } |
463 | |
464 | using RegisterNameMapping = const char* (DisassemblerX64::*)(int reg) const; |
465 | |
466 | void TryAppendRootRelativeName(int offset); |
467 | int PrintRightOperandHelper(byte* modrmp, RegisterNameMapping register_name); |
468 | int PrintRightOperand(byte* modrmp); |
469 | int PrintRightByteOperand(byte* modrmp); |
470 | int PrintRightXMMOperand(byte* modrmp); |
471 | int PrintRightAVXOperand(byte* modrmp); |
472 | int PrintOperands(const char* mnem, OperandType op_order, byte* data); |
473 | int PrintImmediate(byte* data, OperandSize size); |
474 | int PrintImmediateOp(byte* data); |
475 | const char* TwoByteMnemonic(byte opcode); |
476 | int TwoByteOpcodeInstruction(byte* data); |
477 | int ThreeByteOpcodeInstruction(byte* data); |
478 | int F6F7Instruction(byte* data); |
479 | int ShiftInstruction(byte* data); |
480 | int JumpShort(byte* data); |
481 | int JumpConditional(byte* data); |
482 | int JumpConditionalShort(byte* data); |
483 | int SetCC(byte* data); |
484 | int FPUInstruction(byte* data); |
485 | int MemoryFPUInstruction(int escape_opcode, int regop, byte* modrm_start); |
486 | int RegisterFPUInstruction(int escape_opcode, byte modrm_byte); |
487 | int AVXInstruction(byte* data); |
488 | PRINTF_FORMAT(2, 3)__attribute__((format(printf, 2, 3))) void AppendToBuffer(const char* format, ...); |
489 | |
490 | void UnimplementedInstruction() { |
491 | if (abort_on_unimplemented_) { |
492 | FATAL("'Unimplemented Instruction'")V8_Fatal("'Unimplemented Instruction'"); |
493 | } else { |
494 | AppendToBuffer("'Unimplemented Instruction'"); |
495 | } |
496 | } |
497 | }; |
498 | |
499 | void DisassemblerX64::AppendToBuffer(const char* format, ...) { |
500 | v8::base::Vector<char> buf = tmp_buffer_ + tmp_buffer_pos_; |
501 | va_list args; |
502 | va_start(args, format)__builtin_va_start(args, format); |
503 | int result = v8::base::VSNPrintF(buf, format, args); |
504 | va_end(args)__builtin_va_end(args); |
505 | tmp_buffer_pos_ += result; |
506 | } |
507 | |
508 | void DisassemblerX64::TryAppendRootRelativeName(int offset) { |
509 | const char* maybe_name = converter_.RootRelativeName(offset); |
510 | if (maybe_name != nullptr) AppendToBuffer(" (%s)", maybe_name); |
511 | } |
512 | |
513 | int DisassemblerX64::PrintRightOperandHelper( |
514 | byte* modrmp, RegisterNameMapping direct_register_name) { |
515 | int mod, regop, rm; |
516 | get_modrm(*modrmp, &mod, ®op, &rm); |
517 | RegisterNameMapping register_name = |
518 | (mod == 3) ? direct_register_name : &DisassemblerX64::NameOfCPURegister; |
519 | switch (mod) { |
520 | case 0: |
521 | if ((rm & 7) == 5) { |
522 | AppendToBuffer("[rip+0x%x]", Imm32(modrmp + 1)); |
523 | return 5; |
524 | } else if ((rm & 7) == 4) { |
525 | // Codes for SIB byte. |
526 | byte sib = *(modrmp + 1); |
527 | int scale, index, base; |
528 | get_sib(sib, &scale, &index, &base); |
529 | if (index == 4 && (base & 7) == 4 && scale == 0 /*times_1*/) { |
530 | // index == rsp means no index. Only use sib byte with no index for |
531 | // rsp and r12 base. |
532 | AppendToBuffer("[%s]", NameOfCPURegister(base)); |
533 | return 2; |
534 | } else if (base == 5) { |
535 | // base == rbp means no base register (when mod == 0). |
536 | int32_t disp = Imm32(modrmp + 2); |
537 | AppendToBuffer("[%s*%d%s0x%x]", NameOfCPURegister(index), 1 << scale, |
538 | disp < 0 ? "-" : "+", disp < 0 ? -disp : disp); |
539 | return 6; |
540 | } else if (index != 4 && base != 5) { |
541 | // [base+index*scale] |
542 | AppendToBuffer("[%s+%s*%d]", NameOfCPURegister(base), |
543 | NameOfCPURegister(index), 1 << scale); |
544 | return 2; |
545 | } else { |
546 | UnimplementedInstruction(); |
547 | return 1; |
548 | } |
549 | } else { |
550 | AppendToBuffer("[%s]", NameOfCPURegister(rm)); |
551 | return 1; |
552 | } |
553 | case 1: // fall through |
554 | case 2: |
555 | if ((rm & 7) == 4) { |
556 | byte sib = *(modrmp + 1); |
557 | int scale, index, base; |
558 | get_sib(sib, &scale, &index, &base); |
559 | int disp = (mod == 2) ? Imm32(modrmp + 2) : Imm8(modrmp + 2); |
560 | if (index == 4 && (base & 7) == 4 && scale == 0 /*times_1*/) { |
561 | AppendToBuffer("[%s%s0x%x]", NameOfCPURegister(base), |
562 | disp < 0 ? "-" : "+", disp < 0 ? -disp : disp); |
563 | } else { |
564 | AppendToBuffer("[%s+%s*%d%s0x%x]", NameOfCPURegister(base), |
565 | NameOfCPURegister(index), 1 << scale, |
566 | disp < 0 ? "-" : "+", disp < 0 ? -disp : disp); |
567 | } |
568 | return mod == 2 ? 6 : 3; |
569 | } else { |
570 | // No sib. |
571 | int disp = (mod == 2) ? Imm32(modrmp + 1) : Imm8(modrmp + 1); |
572 | AppendToBuffer("[%s%s0x%x]", NameOfCPURegister(rm), |
573 | disp < 0 ? "-" : "+", disp < 0 ? -disp : disp); |
574 | if (rm == i::kRootRegister.code()) { |
575 | // For root-relative accesses, try to append a description. |
576 | TryAppendRootRelativeName(disp); |
577 | } |
578 | return (mod == 2) ? 5 : 2; |
579 | } |
580 | case 3: |
581 | AppendToBuffer("%s", (this->*register_name)(rm)); |
582 | return 1; |
583 | default: |
584 | UnimplementedInstruction(); |
585 | return 1; |
586 | } |
587 | UNREACHABLE()V8_Fatal("unreachable code"); |
588 | } |
589 | |
590 | int DisassemblerX64::PrintImmediate(byte* data, OperandSize size) { |
591 | int64_t value; |
592 | int count; |
593 | switch (size) { |
594 | case OPERAND_BYTE_SIZE: |
595 | value = *data; |
596 | count = 1; |
597 | break; |
598 | case OPERAND_WORD_SIZE: |
599 | value = Imm16(data); |
600 | count = 2; |
601 | break; |
602 | case OPERAND_DOUBLEWORD_SIZE: |
603 | value = Imm32_U(data); |
604 | count = 4; |
605 | break; |
606 | case OPERAND_QUADWORD_SIZE: |
607 | value = Imm32(data); |
608 | count = 4; |
609 | break; |
610 | default: |
611 | UNREACHABLE()V8_Fatal("unreachable code"); |
612 | } |
613 | AppendToBuffer("%" PRIx64"l" "x", value); |
614 | return count; |
615 | } |
616 | |
617 | int DisassemblerX64::PrintRightOperand(byte* modrmp) { |
618 | return PrintRightOperandHelper(modrmp, &DisassemblerX64::NameOfCPURegister); |
619 | } |
620 | |
621 | int DisassemblerX64::PrintRightByteOperand(byte* modrmp) { |
622 | return PrintRightOperandHelper(modrmp, |
623 | &DisassemblerX64::NameOfByteCPURegister); |
624 | } |
625 | |
626 | int DisassemblerX64::PrintRightXMMOperand(byte* modrmp) { |
627 | return PrintRightOperandHelper(modrmp, &DisassemblerX64::NameOfXMMRegister); |
628 | } |
629 | |
630 | int DisassemblerX64::PrintRightAVXOperand(byte* modrmp) { |
631 | return PrintRightOperandHelper(modrmp, &DisassemblerX64::NameOfAVXRegister); |
632 | } |
633 | |
634 | // Returns number of bytes used including the current *data. |
635 | // Writes instruction's mnemonic, left and right operands to 'tmp_buffer_'. |
636 | int DisassemblerX64::PrintOperands(const char* mnem, OperandType op_order, |
637 | byte* data) { |
638 | byte modrm = *data; |
639 | int mod, regop, rm; |
640 | get_modrm(modrm, &mod, ®op, &rm); |
641 | int advance = 0; |
642 | const char* register_name = byte_size_operand_ ? NameOfByteCPURegister(regop) |
643 | : NameOfCPURegister(regop); |
644 | switch (op_order) { |
645 | case REG_OPER_OP_ORDER: { |
646 | AppendToBuffer("%s%c %s,", mnem, operand_size_code(), register_name); |
647 | advance = byte_size_operand_ ? PrintRightByteOperand(data) |
648 | : PrintRightOperand(data); |
649 | break; |
650 | } |
651 | case OPER_REG_OP_ORDER: { |
652 | AppendToBuffer("%s%c ", mnem, operand_size_code()); |
653 | advance = byte_size_operand_ ? PrintRightByteOperand(data) |
654 | : PrintRightOperand(data); |
655 | AppendToBuffer(",%s", register_name); |
656 | break; |
657 | } |
658 | case XMMREG_XMMOPER_OP_ORDER: { |
659 | AppendToBuffer("%s %s,", mnem, NameOfXMMRegister(regop)); |
660 | advance = PrintRightXMMOperand(data); |
661 | break; |
662 | } |
663 | case XMMOPER_XMMREG_OP_ORDER: { |
664 | AppendToBuffer("%s ", mnem); |
665 | advance = PrintRightXMMOperand(data); |
666 | AppendToBuffer(",%s", NameOfXMMRegister(regop)); |
667 | break; |
668 | } |
669 | case OPER_XMMREG_OP_ORDER: { |
670 | AppendToBuffer("%s ", mnem); |
671 | advance = PrintRightOperand(data); |
672 | AppendToBuffer(",%s", NameOfXMMRegister(regop)); |
673 | break; |
674 | } |
675 | case XMMREG_OPER_OP_ORDER: { |
676 | AppendToBuffer("%s %s,", mnem, NameOfXMMRegister(regop)); |
677 | advance = PrintRightOperand(data); |
678 | break; |
679 | } |
680 | default: |
681 | UNREACHABLE()V8_Fatal("unreachable code"); |
682 | } |
683 | return advance; |
684 | } |
685 | |
686 | // Returns number of bytes used by machine instruction, including *data byte. |
687 | // Writes immediate instructions to 'tmp_buffer_'. |
688 | int DisassemblerX64::PrintImmediateOp(byte* data) { |
689 | bool byte_size_immediate = (*data & 0x02) != 0; |
690 | byte modrm = *(data + 1); |
691 | int mod, regop, rm; |
692 | get_modrm(modrm, &mod, ®op, &rm); |
693 | const char* mnem = "Imm???"; |
694 | switch (regop) { |
695 | case 0: |
696 | mnem = "add"; |
697 | break; |
698 | case 1: |
699 | mnem = "or"; |
700 | break; |
701 | case 2: |
702 | mnem = "adc"; |
703 | break; |
704 | case 3: |
705 | mnem = "sbb"; |
706 | break; |
707 | case 4: |
708 | mnem = "and"; |
709 | break; |
710 | case 5: |
711 | mnem = "sub"; |
712 | break; |
713 | case 6: |
714 | mnem = "xor"; |
715 | break; |
716 | case 7: |
717 | mnem = "cmp"; |
718 | break; |
719 | default: |
720 | UnimplementedInstruction(); |
721 | } |
722 | AppendToBuffer("%s%c ", mnem, operand_size_code()); |
723 | int count = PrintRightOperand(data + 1); |
724 | AppendToBuffer(",0x"); |
725 | OperandSize immediate_size = |
726 | byte_size_immediate ? OPERAND_BYTE_SIZE : operand_size(); |
727 | count += PrintImmediate(data + 1 + count, immediate_size); |
728 | return 1 + count; |
729 | } |
730 | |
731 | // Returns number of bytes used, including *data. |
732 | int DisassemblerX64::F6F7Instruction(byte* data) { |
733 | DCHECK(*data == 0xF7 || *data == 0xF6)((void) 0); |
734 | byte modrm = *(data + 1); |
735 | int mod, regop, rm; |
736 | get_modrm(modrm, &mod, ®op, &rm); |
737 | if (regop != 0) { |
738 | const char* mnem = nullptr; |
739 | switch (regop) { |
740 | case 2: |
741 | mnem = "not"; |
742 | break; |
743 | case 3: |
744 | mnem = "neg"; |
745 | break; |
746 | case 4: |
747 | mnem = "mul"; |
748 | break; |
749 | case 5: |
750 | mnem = "imul"; |
751 | break; |
752 | case 6: |
753 | mnem = "div"; |
754 | break; |
755 | case 7: |
756 | mnem = "idiv"; |
757 | break; |
758 | default: |
759 | UnimplementedInstruction(); |
760 | } |
761 | if (mod == 3) { |
762 | AppendToBuffer("%s%c %s", mnem, operand_size_code(), |
763 | NameOfCPURegister(rm)); |
764 | return 2; |
765 | } else if (mod == 1) { |
766 | AppendToBuffer("%s%c ", mnem, operand_size_code()); |
767 | int count = PrintRightOperand(data + 1); // Use name of 64-bit register. |
768 | return 1 + count; |
769 | } else { |
770 | UnimplementedInstruction(); |
771 | return 2; |
772 | } |
773 | } else if (regop == 0) { |
774 | AppendToBuffer("test%c ", operand_size_code()); |
775 | int count = PrintRightOperand(data + 1); // Use name of 64-bit register. |
776 | AppendToBuffer(",0x"); |
777 | count += PrintImmediate(data + 1 + count, operand_size()); |
778 | return 1 + count; |
779 | } else { |
780 | UnimplementedInstruction(); |
781 | return 2; |
782 | } |
783 | } |
784 | |
785 | int DisassemblerX64::ShiftInstruction(byte* data) { |
786 | byte op = *data & (~1); |
787 | int count = 1; |
788 | if (op != 0xD0 && op != 0xD2 && op != 0xC0) { |
789 | UnimplementedInstruction(); |
790 | return count; |
791 | } |
792 | // Print mneumonic. |
793 | { |
794 | byte modrm = *(data + count); |
795 | int mod, regop, rm; |
796 | get_modrm(modrm, &mod, ®op, &rm); |
797 | regop &= 0x7; // The REX.R bit does not affect the operation. |
798 | const char* mnem = nullptr; |
799 | switch (regop) { |
800 | case 0: |
801 | mnem = "rol"; |
802 | break; |
803 | case 1: |
804 | mnem = "ror"; |
805 | break; |
806 | case 2: |
807 | mnem = "rcl"; |
808 | break; |
809 | case 3: |
810 | mnem = "rcr"; |
811 | break; |
812 | case 4: |
813 | mnem = "shl"; |
814 | break; |
815 | case 5: |
816 | mnem = "shr"; |
817 | break; |
818 | case 7: |
819 | mnem = "sar"; |
820 | break; |
821 | default: |
822 | UnimplementedInstruction(); |
823 | return count + 1; |
824 | } |
825 | DCHECK_NOT_NULL(mnem)((void) 0); |
826 | AppendToBuffer("%s%c ", mnem, operand_size_code()); |
827 | } |
828 | count += PrintRightOperand(data + count); |
829 | if (op == 0xD2) { |
830 | AppendToBuffer(", cl"); |
831 | } else { |
832 | int imm8 = -1; |
833 | if (op == 0xD0) { |
834 | imm8 = 1; |
835 | } else { |
836 | DCHECK_EQ(0xC0, op)((void) 0); |
837 | imm8 = *(data + count); |
838 | count++; |
839 | } |
840 | AppendToBuffer(", %d", imm8); |
841 | } |
842 | return count; |
843 | } |
844 | |
845 | // Returns number of bytes used, including *data. |
846 | int DisassemblerX64::JumpShort(byte* data) { |
847 | DCHECK_EQ(0xEB, *data)((void) 0); |
848 | byte b = *(data + 1); |
849 | byte* dest = data + static_cast<int8_t>(b) + 2; |
850 | AppendToBuffer("jmp %s", NameOfAddress(dest)); |
851 | return 2; |
852 | } |
853 | |
854 | // Returns number of bytes used, including *data. |
855 | int DisassemblerX64::JumpConditional(byte* data) { |
856 | DCHECK_EQ(0x0F, *data)((void) 0); |
857 | byte cond = *(data + 1) & 0x0F; |
858 | byte* dest = data + Imm32(data + 2) + 6; |
859 | const char* mnem = conditional_code_suffix[cond]; |
860 | AppendToBuffer("j%s %s", mnem, NameOfAddress(dest)); |
861 | return 6; // includes 0x0F |
862 | } |
863 | |
864 | // Returns number of bytes used, including *data. |
865 | int DisassemblerX64::JumpConditionalShort(byte* data) { |
866 | byte cond = *data & 0x0F; |
867 | byte b = *(data + 1); |
868 | byte* dest = data + static_cast<int8_t>(b) + 2; |
869 | const char* mnem = conditional_code_suffix[cond]; |
870 | AppendToBuffer("j%s %s", mnem, NameOfAddress(dest)); |
871 | return 2; |
872 | } |
873 | |
874 | // Returns number of bytes used, including *data. |
875 | int DisassemblerX64::SetCC(byte* data) { |
876 | DCHECK_EQ(0x0F, *data)((void) 0); |
877 | byte cond = *(data + 1) & 0x0F; |
878 | const char* mnem = conditional_code_suffix[cond]; |
879 | AppendToBuffer("set%s%c ", mnem, operand_size_code()); |
880 | PrintRightByteOperand(data + 2); |
881 | return 3; // includes 0x0F |
882 | } |
883 | |
884 | const char* sf_str[4] = {"", "rl", "ra", "ll"}; |
885 | |
886 | int DisassemblerX64::AVXInstruction(byte* data) { |
887 | byte opcode = *data; |
888 | byte* current = data + 1; |
889 | if (vex_66() && vex_0f38()) { |
890 | int mod, regop, rm, vvvv = vex_vreg(); |
891 | get_modrm(*current, &mod, ®op, &rm); |
892 | switch (opcode) { |
893 | case 0x18: |
894 | AppendToBuffer("vbroadcastss %s,", NameOfAVXRegister(regop)); |
895 | current += PrintRightXMMOperand(current); |
896 | break; |
897 | case 0xF7: |
898 | AppendToBuffer("shlx%c %s,", operand_size_code(), |
899 | NameOfCPURegister(regop)); |
900 | current += PrintRightOperand(current); |
901 | AppendToBuffer(",%s", NameOfCPURegister(vvvv)); |
902 | break; |
903 | #define DECLARE_SSE_AVX_DIS_CASE(instruction, notUsed1, notUsed2, notUsed3, \ |
904 | opcode) \ |
905 | case 0x##opcode: { \ |
906 | AppendToBuffer("v" #instruction " %s,%s,", NameOfAVXRegister(regop), \ |
907 | NameOfAVXRegister(vvvv)); \ |
908 | current += PrintRightAVXOperand(current); \ |
909 | break; \ |
910 | } |
911 | |
912 | SSSE3_INSTRUCTION_LIST(DECLARE_SSE_AVX_DIS_CASE)DECLARE_SSE_AVX_DIS_CASE(pshufb, 66, 0F, 38, 00) DECLARE_SSE_AVX_DIS_CASE (phaddw, 66, 0F, 38, 01) DECLARE_SSE_AVX_DIS_CASE(phaddd, 66, 0F, 38, 02) DECLARE_SSE_AVX_DIS_CASE(pmaddubsw, 66, 0F, 38, 04 ) DECLARE_SSE_AVX_DIS_CASE(psignb, 66, 0F, 38, 08) DECLARE_SSE_AVX_DIS_CASE (psignw, 66, 0F, 38, 09) DECLARE_SSE_AVX_DIS_CASE(psignd, 66, 0F, 38, 0A) DECLARE_SSE_AVX_DIS_CASE(pmulhrsw, 66, 0F, 38, 0B ) |
913 | SSE4_INSTRUCTION_LIST(DECLARE_SSE_AVX_DIS_CASE)DECLARE_SSE_AVX_DIS_CASE(pmuldq, 66, 0F, 38, 28) DECLARE_SSE_AVX_DIS_CASE (pcmpeqq, 66, 0F, 38, 29) DECLARE_SSE_AVX_DIS_CASE(packusdw, 66 , 0F, 38, 2B) DECLARE_SSE_AVX_DIS_CASE(pminsb, 66, 0F, 38, 38 ) DECLARE_SSE_AVX_DIS_CASE(pminsd, 66, 0F, 38, 39) DECLARE_SSE_AVX_DIS_CASE (pminuw, 66, 0F, 38, 3A) DECLARE_SSE_AVX_DIS_CASE(pminud, 66, 0F, 38, 3B) DECLARE_SSE_AVX_DIS_CASE(pmaxsb, 66, 0F, 38, 3C) DECLARE_SSE_AVX_DIS_CASE(pmaxsd, 66, 0F, 38, 3D) DECLARE_SSE_AVX_DIS_CASE (pmaxuw, 66, 0F, 38, 3E) DECLARE_SSE_AVX_DIS_CASE(pmaxud, 66, 0F, 38, 3F) DECLARE_SSE_AVX_DIS_CASE(pmulld, 66, 0F, 38, 40) |
914 | SSE4_2_INSTRUCTION_LIST(DECLARE_SSE_AVX_DIS_CASE)DECLARE_SSE_AVX_DIS_CASE(pcmpgtq, 66, 0F, 38, 37) |
915 | #undef DECLARE_SSE_AVX_DIS_CASE |
916 | |
917 | #define DECLARE_SSE_UNOP_AVX_DIS_CASE(instruction, notUsed1, notUsed2, \ |
918 | notUsed3, opcode) \ |
919 | case 0x##opcode: { \ |
920 | AppendToBuffer("v" #instruction " %s,", NameOfAVXRegister(regop)); \ |
921 | current += PrintRightAVXOperand(current); \ |
922 | break; \ |
923 | } |
924 | SSSE3_UNOP_INSTRUCTION_LIST(DECLARE_SSE_UNOP_AVX_DIS_CASE)DECLARE_SSE_UNOP_AVX_DIS_CASE(pabsb, 66, 0F, 38, 1C) DECLARE_SSE_UNOP_AVX_DIS_CASE (pabsw, 66, 0F, 38, 1D) DECLARE_SSE_UNOP_AVX_DIS_CASE(pabsd, 66 , 0F, 38, 1E) |
925 | SSE4_UNOP_INSTRUCTION_LIST(DECLARE_SSE_UNOP_AVX_DIS_CASE)DECLARE_SSE_UNOP_AVX_DIS_CASE(ptest, 66, 0F, 38, 17) DECLARE_SSE_UNOP_AVX_DIS_CASE (pmovsxbw, 66, 0F, 38, 20) DECLARE_SSE_UNOP_AVX_DIS_CASE(pmovsxwd , 66, 0F, 38, 23) DECLARE_SSE_UNOP_AVX_DIS_CASE(pmovsxdq, 66, 0F, 38, 25) DECLARE_SSE_UNOP_AVX_DIS_CASE(pmovzxbw, 66, 0F, 38 , 30) DECLARE_SSE_UNOP_AVX_DIS_CASE(pmovzxwd, 66, 0F, 38, 33) DECLARE_SSE_UNOP_AVX_DIS_CASE(pmovzxdq, 66, 0F, 38, 35) |
926 | #undef DECLARE_SSE_UNOP_AVX_DIS_CASE |
927 | |
928 | #define DISASSEMBLE_AVX2_BROADCAST(instruction, _1, _2, _3, code) \ |
929 | case 0x##code: \ |
930 | AppendToBuffer("" #instruction " %s,", NameOfAVXRegister(regop)); \ |
931 | current += PrintRightXMMOperand(current); \ |
932 | break; |
933 | AVX2_BROADCAST_LIST(DISASSEMBLE_AVX2_BROADCAST)DISASSEMBLE_AVX2_BROADCAST(vpbroadcastd, 66, 0F, 38, 58) DISASSEMBLE_AVX2_BROADCAST (vpbroadcastb, 66, 0F, 38, 78) DISASSEMBLE_AVX2_BROADCAST(vpbroadcastw , 66, 0F, 38, 79) |
934 | #undef DISASSEMBLE_AVX2_BROADCAST |
935 | |
936 | default: { |
937 | #define DECLARE_FMA_DISASM(instruction, _1, _2, _3, _4, _5, code) \ |
938 | case 0x##code: { \ |
939 | AppendToBuffer(#instruction " %s,%s,", NameOfAVXRegister(regop), \ |
940 | NameOfAVXRegister(vvvv)); \ |
941 | current += PrintRightAVXOperand(current); \ |
942 | break; \ |
943 | } |
944 | // Handle all the fma instructions here in the default branch since they |
945 | // have the same opcodes but differ by rex_w. |
946 | if (rex_w()) { |
947 | switch (opcode) { |
948 | FMA_SS_INSTRUCTION_LIST(DECLARE_FMA_DISASM)DECLARE_FMA_DISASM(vfmadd132ss, LIG, 66, 0F, 38, W0, 99) DECLARE_FMA_DISASM (vfmadd213ss, LIG, 66, 0F, 38, W0, a9) DECLARE_FMA_DISASM(vfmadd231ss , LIG, 66, 0F, 38, W0, b9) DECLARE_FMA_DISASM(vfmsub132ss, LIG , 66, 0F, 38, W0, 9b) DECLARE_FMA_DISASM(vfmsub213ss, LIG, 66 , 0F, 38, W0, ab) DECLARE_FMA_DISASM(vfmsub231ss, LIG, 66, 0F , 38, W0, bb) DECLARE_FMA_DISASM(vfnmadd132ss, LIG, 66, 0F, 38 , W0, 9d) DECLARE_FMA_DISASM(vfnmadd213ss, LIG, 66, 0F, 38, W0 , ad) DECLARE_FMA_DISASM(vfnmadd231ss, LIG, 66, 0F, 38, W0, bd ) DECLARE_FMA_DISASM(vfnmsub132ss, LIG, 66, 0F, 38, W0, 9f) DECLARE_FMA_DISASM (vfnmsub213ss, LIG, 66, 0F, 38, W0, af) DECLARE_FMA_DISASM(vfnmsub231ss , LIG, 66, 0F, 38, W0, bf) |
949 | FMA_PS_INSTRUCTION_LIST(DECLARE_FMA_DISASM)DECLARE_FMA_DISASM(vfmadd132ps, L128, 66, 0F, 38, W0, 98) DECLARE_FMA_DISASM (vfmadd213ps, L128, 66, 0F, 38, W0, a8) DECLARE_FMA_DISASM(vfmadd231ps , L128, 66, 0F, 38, W0, b8) DECLARE_FMA_DISASM(vfnmadd132ps, L128 , 66, 0F, 38, W0, 9c) DECLARE_FMA_DISASM(vfnmadd213ps, L128, 66 , 0F, 38, W0, ac) DECLARE_FMA_DISASM(vfnmadd231ps, L128, 66, 0F , 38, W0, bc) |
950 | default: { |
951 | UnimplementedInstruction(); |
952 | } |
953 | } |
954 | } else { |
955 | switch (opcode) { |
956 | FMA_SD_INSTRUCTION_LIST(DECLARE_FMA_DISASM)DECLARE_FMA_DISASM(vfmadd132sd, L128, 66, 0F, 38, W1, 99) DECLARE_FMA_DISASM (vfmadd213sd, L128, 66, 0F, 38, W1, a9) DECLARE_FMA_DISASM(vfmadd231sd , L128, 66, 0F, 38, W1, b9) DECLARE_FMA_DISASM(vfmsub132sd, L128 , 66, 0F, 38, W1, 9b) DECLARE_FMA_DISASM(vfmsub213sd, L128, 66 , 0F, 38, W1, ab) DECLARE_FMA_DISASM(vfmsub231sd, L128, 66, 0F , 38, W1, bb) DECLARE_FMA_DISASM(vfnmadd132sd, L128, 66, 0F, 38 , W1, 9d) DECLARE_FMA_DISASM(vfnmadd213sd, L128, 66, 0F, 38, W1 , ad) DECLARE_FMA_DISASM(vfnmadd231sd, L128, 66, 0F, 38, W1, bd ) DECLARE_FMA_DISASM(vfnmsub132sd, L128, 66, 0F, 38, W1, 9f) DECLARE_FMA_DISASM (vfnmsub213sd, L128, 66, 0F, 38, W1, af) DECLARE_FMA_DISASM(vfnmsub231sd , L128, 66, 0F, 38, W1, bf) |
957 | FMA_PD_INSTRUCTION_LIST(DECLARE_FMA_DISASM)DECLARE_FMA_DISASM(vfmadd132pd, L128, 66, 0F, 38, W1, 98) DECLARE_FMA_DISASM (vfmadd213pd, L128, 66, 0F, 38, W1, a8) DECLARE_FMA_DISASM(vfmadd231pd , L128, 66, 0F, 38, W1, b8) DECLARE_FMA_DISASM(vfnmadd132pd, L128 , 66, 0F, 38, W1, 9c) DECLARE_FMA_DISASM(vfnmadd213pd, L128, 66 , 0F, 38, W1, ac) DECLARE_FMA_DISASM(vfnmadd231pd, L128, 66, 0F , 38, W1, bc) |
958 | default: { |
959 | UnimplementedInstruction(); |
960 | } |
961 | } |
962 | } |
963 | #undef DECLARE_FMA_DISASM |
964 | } |
965 | } |
966 | } else if (vex_66() && vex_0f3a()) { |
967 | int mod, regop, rm, vvvv = vex_vreg(); |
968 | get_modrm(*current, &mod, ®op, &rm); |
969 | switch (opcode) { |
970 | case 0x08: |
971 | AppendToBuffer("vroundps %s,", NameOfAVXRegister(regop)); |
972 | current += PrintRightAVXOperand(current); |
973 | AppendToBuffer(",0x%x", *current++); |
974 | break; |
975 | case 0x09: |
976 | AppendToBuffer("vroundpd %s,", NameOfAVXRegister(regop)); |
977 | current += PrintRightAVXOperand(current); |
978 | AppendToBuffer(",0x%x", *current++); |
979 | break; |
980 | case 0x0A: |
981 | AppendToBuffer("vroundss %s,%s,", NameOfAVXRegister(regop), |
982 | NameOfAVXRegister(vvvv)); |
983 | current += PrintRightAVXOperand(current); |
984 | AppendToBuffer(",0x%x", *current++); |
985 | break; |
986 | case 0x0B: |
987 | AppendToBuffer("vroundsd %s,%s,", NameOfAVXRegister(regop), |
988 | NameOfAVXRegister(vvvv)); |
989 | current += PrintRightAVXOperand(current); |
990 | AppendToBuffer(",0x%x", *current++); |
991 | break; |
992 | case 0x0E: |
993 | AppendToBuffer("vpblendw %s,%s,", NameOfAVXRegister(regop), |
994 | NameOfAVXRegister(vvvv)); |
995 | current += PrintRightAVXOperand(current); |
996 | AppendToBuffer(",0x%x", *current++); |
997 | break; |
998 | case 0x0F: |
999 | AppendToBuffer("vpalignr %s,%s,", NameOfAVXRegister(regop), |
1000 | NameOfAVXRegister(vvvv)); |
1001 | current += PrintRightAVXOperand(current); |
1002 | AppendToBuffer(",0x%x", *current++); |
1003 | break; |
1004 | case 0x14: |
1005 | AppendToBuffer("vpextrb "); |
1006 | current += PrintRightByteOperand(current); |
1007 | AppendToBuffer(",%s,0x%x", NameOfAVXRegister(regop), *current++); |
1008 | break; |
1009 | case 0x15: |
1010 | AppendToBuffer("vpextrw "); |
1011 | current += PrintRightOperand(current); |
1012 | AppendToBuffer(",%s,0x%x", NameOfAVXRegister(regop), *current++); |
1013 | break; |
1014 | case 0x16: |
1015 | AppendToBuffer("vpextr%c ", rex_w() ? 'q' : 'd'); |
1016 | current += PrintRightOperand(current); |
1017 | AppendToBuffer(",%s,0x%x", NameOfAVXRegister(regop), *current++); |
1018 | break; |
1019 | case 0x17: |
1020 | AppendToBuffer("vextractps "); |
1021 | current += PrintRightOperand(current); |
1022 | AppendToBuffer(",%s,0x%x", NameOfAVXRegister(regop), *current++); |
1023 | break; |
1024 | case 0x20: |
1025 | AppendToBuffer("vpinsrb %s,%s,", NameOfAVXRegister(regop), |
1026 | NameOfAVXRegister(vvvv)); |
1027 | current += PrintRightByteOperand(current); |
1028 | AppendToBuffer(",0x%x", *current++); |
1029 | break; |
1030 | case 0x21: |
1031 | AppendToBuffer("vinsertps %s,%s,", NameOfAVXRegister(regop), |
1032 | NameOfAVXRegister(vvvv)); |
1033 | current += PrintRightAVXOperand(current); |
1034 | AppendToBuffer(",0x%x", *current++); |
1035 | break; |
1036 | case 0x22: |
1037 | AppendToBuffer("vpinsr%c %s,%s,", rex_w() ? 'q' : 'd', |
1038 | NameOfAVXRegister(regop), NameOfAVXRegister(vvvv)); |
1039 | current += PrintRightOperand(current); |
1040 | AppendToBuffer(",0x%x", *current++); |
1041 | break; |
1042 | case 0x4A: { |
1043 | AppendToBuffer("vblendvps %s,%s,", NameOfAVXRegister(regop), |
1044 | NameOfAVXRegister(vvvv)); |
1045 | current += PrintRightAVXOperand(current); |
1046 | AppendToBuffer(",%s", NameOfAVXRegister((*current++) >> 4)); |
1047 | break; |
1048 | } |
1049 | case 0x4B: { |
1050 | AppendToBuffer("vblendvpd %s,%s,", NameOfAVXRegister(regop), |
1051 | NameOfAVXRegister(vvvv)); |
1052 | current += PrintRightAVXOperand(current); |
1053 | AppendToBuffer(",%s", NameOfAVXRegister((*current++) >> 4)); |
1054 | break; |
1055 | } |
1056 | case 0x4C: { |
1057 | AppendToBuffer("vpblendvb %s,%s,", NameOfAVXRegister(regop), |
1058 | NameOfAVXRegister(vvvv)); |
1059 | current += PrintRightAVXOperand(current); |
1060 | AppendToBuffer(",%s", NameOfAVXRegister((*current++) >> 4)); |
1061 | break; |
1062 | } |
1063 | default: |
1064 | UnimplementedInstruction(); |
1065 | } |
1066 | } else if (vex_f3() && vex_0f()) { |
1067 | int mod, regop, rm, vvvv = vex_vreg(); |
1068 | get_modrm(*current, &mod, ®op, &rm); |
1069 | switch (opcode) { |
1070 | case 0x10: |
1071 | AppendToBuffer("vmovss %s,", NameOfAVXRegister(regop)); |
1072 | if (mod == 3) { |
1073 | AppendToBuffer("%s,", NameOfAVXRegister(vvvv)); |
1074 | } |
1075 | current += PrintRightAVXOperand(current); |
1076 | break; |
1077 | case 0x11: |
1078 | AppendToBuffer("vmovss "); |
1079 | current += PrintRightAVXOperand(current); |
1080 | if (mod == 3) { |
1081 | AppendToBuffer(",%s", NameOfAVXRegister(vvvv)); |
1082 | } |
1083 | AppendToBuffer(",%s", NameOfAVXRegister(regop)); |
1084 | break; |
1085 | case 0x16: |
1086 | AppendToBuffer("vmovshdup %s,", NameOfAVXRegister(regop)); |
1087 | current += PrintRightAVXOperand(current); |
1088 | break; |
1089 | case 0x2A: |
1090 | AppendToBuffer("%s %s,%s,", vex_w() ? "vcvtqsi2ss" : "vcvtlsi2ss", |
1091 | NameOfAVXRegister(regop), NameOfAVXRegister(vvvv)); |
1092 | current += PrintRightOperand(current); |
1093 | break; |
1094 | case 0x2C: |
1095 | AppendToBuffer("vcvttss2si%s %s,", vex_w() ? "q" : "", |
1096 | NameOfCPURegister(regop)); |
1097 | current += PrintRightAVXOperand(current); |
1098 | break; |
1099 | case 0x51: |
1100 | AppendToBuffer("vsqrtss %s,%s,", NameOfAVXRegister(regop), |
1101 | NameOfAVXRegister(vvvv)); |
1102 | current += PrintRightAVXOperand(current); |
1103 | break; |
1104 | case 0x58: |
1105 | AppendToBuffer("vaddss %s,%s,", NameOfAVXRegister(regop), |
1106 | NameOfAVXRegister(vvvv)); |
1107 | current += PrintRightAVXOperand(current); |
1108 | break; |
1109 | case 0x59: |
1110 | AppendToBuffer("vmulss %s,%s,", NameOfAVXRegister(regop), |
1111 | NameOfAVXRegister(vvvv)); |
1112 | current += PrintRightAVXOperand(current); |
1113 | break; |
1114 | case 0x5A: |
1115 | AppendToBuffer("vcvtss2sd %s,%s,", NameOfAVXRegister(regop), |
1116 | NameOfAVXRegister(vvvv)); |
1117 | current += PrintRightAVXOperand(current); |
1118 | break; |
1119 | case 0x5B: |
1120 | AppendToBuffer("vcvttps2dq %s,", NameOfAVXRegister(regop)); |
1121 | current += PrintRightAVXOperand(current); |
1122 | break; |
1123 | case 0x5C: |
1124 | AppendToBuffer("vsubss %s,%s,", NameOfAVXRegister(regop), |
1125 | NameOfAVXRegister(vvvv)); |
1126 | current += PrintRightAVXOperand(current); |
1127 | break; |
1128 | case 0x5D: |
1129 | AppendToBuffer("vminss %s,%s,", NameOfAVXRegister(regop), |
1130 | NameOfAVXRegister(vvvv)); |
1131 | current += PrintRightAVXOperand(current); |
1132 | break; |
1133 | case 0x5E: |
1134 | AppendToBuffer("vdivss %s,%s,", NameOfAVXRegister(regop), |
1135 | NameOfAVXRegister(vvvv)); |
1136 | current += PrintRightAVXOperand(current); |
1137 | break; |
1138 | case 0x5F: |
1139 | AppendToBuffer("vmaxss %s,%s,", NameOfAVXRegister(regop), |
1140 | NameOfAVXRegister(vvvv)); |
1141 | current += PrintRightAVXOperand(current); |
1142 | break; |
1143 | case 0x6F: |
1144 | AppendToBuffer("vmovdqu %s,", NameOfAVXRegister(regop)); |
1145 | current += PrintRightAVXOperand(current); |
1146 | break; |
1147 | case 0x70: |
1148 | AppendToBuffer("vpshufhw %s,", NameOfAVXRegister(regop)); |
1149 | current += PrintRightAVXOperand(current); |
1150 | AppendToBuffer(",0x%x", *current++); |
1151 | break; |
1152 | case 0x7F: |
1153 | AppendToBuffer("vmovdqu "); |
1154 | current += PrintRightAVXOperand(current); |
1155 | AppendToBuffer(",%s", NameOfAVXRegister(regop)); |
1156 | break; |
1157 | case 0xE6: |
1158 | AppendToBuffer("vcvtdq2pd %s,", NameOfAVXRegister(regop)); |
1159 | current += PrintRightAVXOperand(current); |
1160 | break; |
1161 | case 0xC2: |
1162 | AppendToBuffer("vcmpss %s,%s,", NameOfAVXRegister(regop), |
1163 | NameOfAVXRegister(vvvv)); |
1164 | current += PrintRightAVXOperand(current); |
1165 | AppendToBuffer(", (%s)", cmp_pseudo_op[*current]); |
1166 | current += 1; |
1167 | break; |
1168 | default: |
1169 | UnimplementedInstruction(); |
1170 | } |
1171 | } else if (vex_f2() && vex_0f()) { |
1172 | int mod, regop, rm, vvvv = vex_vreg(); |
1173 | get_modrm(*current, &mod, ®op, &rm); |
1174 | switch (opcode) { |
1175 | case 0x10: |
1176 | AppendToBuffer("vmovsd %s,", NameOfAVXRegister(regop)); |
1177 | if (mod == 3) { |
1178 | AppendToBuffer("%s,", NameOfAVXRegister(vvvv)); |
1179 | } |
1180 | current += PrintRightAVXOperand(current); |
1181 | break; |
1182 | case 0x11: |
1183 | AppendToBuffer("vmovsd "); |
1184 | current += PrintRightAVXOperand(current); |
1185 | if (mod == 3) { |
1186 | AppendToBuffer(",%s", NameOfAVXRegister(vvvv)); |
1187 | } |
1188 | AppendToBuffer(",%s", NameOfAVXRegister(regop)); |
1189 | break; |
1190 | case 0x12: |
1191 | AppendToBuffer("vmovddup %s,", NameOfAVXRegister(regop)); |
1192 | current += PrintRightAVXOperand(current); |
1193 | break; |
1194 | case 0x2A: |
1195 | AppendToBuffer("%s %s,%s,", vex_w() ? "vcvtqsi2sd" : "vcvtlsi2sd", |
1196 | NameOfAVXRegister(regop), NameOfAVXRegister(vvvv)); |
1197 | current += PrintRightOperand(current); |
1198 | break; |
1199 | case 0x2C: |
1200 | AppendToBuffer("vcvttsd2si%s %s,", vex_w() ? "q" : "", |
1201 | NameOfCPURegister(regop)); |
1202 | current += PrintRightAVXOperand(current); |
1203 | break; |
1204 | case 0x2D: |
1205 | AppendToBuffer("vcvtsd2si%s %s,", vex_w() ? "q" : "", |
1206 | NameOfCPURegister(regop)); |
1207 | current += PrintRightAVXOperand(current); |
1208 | break; |
1209 | case 0xF0: |
1210 | AppendToBuffer("vlddqu %s,", NameOfAVXRegister(regop)); |
1211 | current += PrintRightAVXOperand(current); |
1212 | break; |
1213 | case 0x70: |
1214 | AppendToBuffer("vpshuflw %s,", NameOfAVXRegister(regop)); |
1215 | current += PrintRightAVXOperand(current); |
1216 | AppendToBuffer(",0x%x", *current++); |
1217 | break; |
1218 | case 0x7C: |
1219 | AppendToBuffer("vhaddps %s,%s,", NameOfAVXRegister(regop), |
1220 | NameOfAVXRegister(vvvv)); |
1221 | current += PrintRightAVXOperand(current); |
1222 | break; |
1223 | case 0xC2: |
1224 | AppendToBuffer("vcmpsd %s,%s,", NameOfAVXRegister(regop), |
1225 | NameOfAVXRegister(vvvv)); |
1226 | current += PrintRightAVXOperand(current); |
1227 | AppendToBuffer(", (%s)", cmp_pseudo_op[*current]); |
1228 | current += 1; |
1229 | break; |
1230 | #define DISASM_SSE2_INSTRUCTION_LIST_SD(instruction, _1, _2, opcode) \ |
1231 | case 0x##opcode: \ |
1232 | AppendToBuffer("v" #instruction " %s,%s,", NameOfAVXRegister(regop), \ |
1233 | NameOfAVXRegister(vvvv)); \ |
1234 | current += PrintRightAVXOperand(current); \ |
1235 | break; |
1236 | SSE2_INSTRUCTION_LIST_SD(DISASM_SSE2_INSTRUCTION_LIST_SD)DISASM_SSE2_INSTRUCTION_LIST_SD(sqrtsd, F2, 0F, 51) DISASM_SSE2_INSTRUCTION_LIST_SD (addsd, F2, 0F, 58) DISASM_SSE2_INSTRUCTION_LIST_SD(mulsd, F2 , 0F, 59) DISASM_SSE2_INSTRUCTION_LIST_SD(cvtsd2ss, F2, 0F, 5A ) DISASM_SSE2_INSTRUCTION_LIST_SD(subsd, F2, 0F, 5C) DISASM_SSE2_INSTRUCTION_LIST_SD (minsd, F2, 0F, 5D) DISASM_SSE2_INSTRUCTION_LIST_SD(divsd, F2 , 0F, 5E) DISASM_SSE2_INSTRUCTION_LIST_SD(maxsd, F2, 0F, 5F) |
1237 | #undef DISASM_SSE2_INSTRUCTION_LIST_SD |
1238 | default: |
1239 | UnimplementedInstruction(); |
1240 | } |
1241 | } else if (vex_none() && vex_0f38()) { |
1242 | int mod, regop, rm, vvvv = vex_vreg(); |
1243 | get_modrm(*current, &mod, ®op, &rm); |
1244 | const char* mnem = "?"; |
1245 | switch (opcode) { |
1246 | case 0xF2: |
1247 | AppendToBuffer("andn%c %s,%s,", operand_size_code(), |
1248 | NameOfCPURegister(regop), NameOfCPURegister(vvvv)); |
1249 | current += PrintRightOperand(current); |
1250 | break; |
1251 | case 0xF5: |
1252 | AppendToBuffer("bzhi%c %s,", operand_size_code(), |
1253 | NameOfCPURegister(regop)); |
1254 | current += PrintRightOperand(current); |
1255 | AppendToBuffer(",%s", NameOfCPURegister(vvvv)); |
1256 | break; |
1257 | case 0xF7: |
1258 | AppendToBuffer("bextr%c %s,", operand_size_code(), |
1259 | NameOfCPURegister(regop)); |
1260 | current += PrintRightOperand(current); |
1261 | AppendToBuffer(",%s", NameOfCPURegister(vvvv)); |
1262 | break; |
1263 | case 0xF3: |
1264 | switch (regop) { |
1265 | case 1: |
1266 | mnem = "blsr"; |
1267 | break; |
1268 | case 2: |
1269 | mnem = "blsmsk"; |
1270 | break; |
1271 | case 3: |
1272 | mnem = "blsi"; |
1273 | break; |
1274 | default: |
1275 | UnimplementedInstruction(); |
1276 | } |
1277 | AppendToBuffer("%s%c %s,", mnem, operand_size_code(), |
1278 | NameOfCPURegister(vvvv)); |
1279 | current += PrintRightOperand(current); |
1280 | mnem = "?"; |
1281 | break; |
1282 | default: |
1283 | UnimplementedInstruction(); |
1284 | } |
1285 | } else if (vex_f2() && vex_0f38()) { |
1286 | int mod, regop, rm, vvvv = vex_vreg(); |
1287 | get_modrm(*current, &mod, ®op, &rm); |
1288 | switch (opcode) { |
1289 | case 0xF5: |
1290 | AppendToBuffer("pdep%c %s,%s,", operand_size_code(), |
1291 | NameOfCPURegister(regop), NameOfCPURegister(vvvv)); |
1292 | current += PrintRightOperand(current); |
1293 | break; |
1294 | case 0xF6: |
1295 | AppendToBuffer("mulx%c %s,%s,", operand_size_code(), |
1296 | NameOfCPURegister(regop), NameOfCPURegister(vvvv)); |
1297 | current += PrintRightOperand(current); |
1298 | break; |
1299 | case 0xF7: |
1300 | AppendToBuffer("shrx%c %s,", operand_size_code(), |
1301 | NameOfCPURegister(regop)); |
1302 | current += PrintRightOperand(current); |
1303 | AppendToBuffer(",%s", NameOfCPURegister(vvvv)); |
1304 | break; |
1305 | default: |
1306 | UnimplementedInstruction(); |
1307 | } |
1308 | } else if (vex_f3() && vex_0f38()) { |
1309 | int mod, regop, rm, vvvv = vex_vreg(); |
1310 | get_modrm(*current, &mod, ®op, &rm); |
1311 | switch (opcode) { |
1312 | case 0xF5: |
1313 | AppendToBuffer("pext%c %s,%s,", operand_size_code(), |
1314 | NameOfCPURegister(regop), NameOfCPURegister(vvvv)); |
1315 | current += PrintRightOperand(current); |
1316 | break; |
1317 | case 0xF7: |
1318 | AppendToBuffer("sarx%c %s,", operand_size_code(), |
1319 | NameOfCPURegister(regop)); |
1320 | current += PrintRightOperand(current); |
1321 | AppendToBuffer(",%s", NameOfCPURegister(vvvv)); |
1322 | break; |
1323 | default: |
1324 | UnimplementedInstruction(); |
1325 | } |
1326 | } else if (vex_f2() && vex_0f3a()) { |
1327 | int mod, regop, rm; |
1328 | get_modrm(*current, &mod, ®op, &rm); |
1329 | switch (opcode) { |
1330 | case 0xF0: |
1331 | AppendToBuffer("rorx%c %s,", operand_size_code(), |
1332 | NameOfCPURegister(regop)); |
1333 | current += PrintRightOperand(current); |
1334 | switch (operand_size()) { |
1335 | case OPERAND_DOUBLEWORD_SIZE: |
1336 | AppendToBuffer(",%d", *current & 0x1F); |
1337 | break; |
1338 | case OPERAND_QUADWORD_SIZE: |
1339 | AppendToBuffer(",%d", *current & 0x3F); |
1340 | break; |
1341 | default: |
1342 | UnimplementedInstruction(); |
1343 | } |
1344 | current += 1; |
1345 | break; |
1346 | default: |
1347 | UnimplementedInstruction(); |
1348 | } |
1349 | } else if (vex_none() && vex_0f()) { |
1350 | int mod, regop, rm, vvvv = vex_vreg(); |
1351 | get_modrm(*current, &mod, ®op, &rm); |
1352 | switch (opcode) { |
1353 | case 0x10: |
1354 | AppendToBuffer("vmovups %s,", NameOfAVXRegister(regop)); |
1355 | current += PrintRightAVXOperand(current); |
1356 | break; |
1357 | case 0x11: |
1358 | AppendToBuffer("vmovups "); |
1359 | current += PrintRightAVXOperand(current); |
1360 | AppendToBuffer(",%s", NameOfAVXRegister(regop)); |
1361 | break; |
1362 | case 0x12: |
1363 | if (mod == 0b11) { |
1364 | AppendToBuffer("vmovhlps %s,%s,", NameOfAVXRegister(regop), |
1365 | NameOfAVXRegister(vvvv)); |
1366 | current += PrintRightAVXOperand(current); |
1367 | } else { |
1368 | AppendToBuffer("vmovlps %s,%s,", NameOfAVXRegister(regop), |
1369 | NameOfAVXRegister(vvvv)); |
1370 | current += PrintRightAVXOperand(current); |
1371 | } |
1372 | break; |
1373 | case 0x13: |
1374 | AppendToBuffer("vmovlps "); |
1375 | current += PrintRightAVXOperand(current); |
1376 | AppendToBuffer(",%s", NameOfAVXRegister(regop)); |
1377 | break; |
1378 | case 0x16: |
1379 | if (mod == 0b11) { |
1380 | AppendToBuffer("vmovlhps %s,%s,", NameOfAVXRegister(regop), |
1381 | NameOfAVXRegister(vvvv)); |
1382 | current += PrintRightAVXOperand(current); |
1383 | } else { |
1384 | AppendToBuffer("vmovhps %s,%s,", NameOfAVXRegister(regop), |
1385 | NameOfAVXRegister(vvvv)); |
1386 | current += PrintRightAVXOperand(current); |
1387 | } |
1388 | break; |
1389 | case 0x17: |
1390 | AppendToBuffer("vmovhps "); |
1391 | current += PrintRightAVXOperand(current); |
1392 | AppendToBuffer(",%s", NameOfAVXRegister(regop)); |
1393 | break; |
1394 | case 0x28: |
1395 | AppendToBuffer("vmovaps %s,", NameOfAVXRegister(regop)); |
1396 | current += PrintRightAVXOperand(current); |
1397 | break; |
1398 | case 0x29: |
1399 | AppendToBuffer("vmovaps "); |
1400 | current += PrintRightAVXOperand(current); |
1401 | AppendToBuffer(",%s", NameOfAVXRegister(regop)); |
1402 | break; |
1403 | case 0x2E: |
1404 | AppendToBuffer("vucomiss %s,", NameOfAVXRegister(regop)); |
1405 | current += PrintRightAVXOperand(current); |
1406 | break; |
1407 | case 0x50: |
1408 | AppendToBuffer("vmovmskps %s,", NameOfCPURegister(regop)); |
1409 | current += PrintRightAVXOperand(current); |
1410 | break; |
1411 | case 0xC2: { |
1412 | AppendToBuffer("vcmpps %s,%s,", NameOfAVXRegister(regop), |
1413 | NameOfAVXRegister(vvvv)); |
1414 | current += PrintRightAVXOperand(current); |
1415 | AppendToBuffer(", (%s)", cmp_pseudo_op[*current]); |
1416 | current += 1; |
1417 | break; |
1418 | } |
1419 | case 0xC6: { |
1420 | AppendToBuffer("vshufps %s,%s,", NameOfAVXRegister(regop), |
1421 | NameOfAVXRegister(vvvv)); |
1422 | current += PrintRightAVXOperand(current); |
1423 | AppendToBuffer(",0x%x", *current++); |
1424 | break; |
1425 | } |
1426 | #define SSE_UNOP_CASE(instruction, unused, code) \ |
1427 | case 0x##code: \ |
1428 | AppendToBuffer("v" #instruction " %s,", NameOfAVXRegister(regop)); \ |
1429 | current += PrintRightAVXOperand(current); \ |
1430 | break; |
1431 | SSE_UNOP_INSTRUCTION_LIST(SSE_UNOP_CASE)SSE_UNOP_CASE(sqrtps, 0F, 51) SSE_UNOP_CASE(rsqrtps, 0F, 52) SSE_UNOP_CASE (rcpps, 0F, 53) SSE_UNOP_CASE(cvtps2pd, 0F, 5A) SSE_UNOP_CASE (cvtdq2ps, 0F, 5B) |
1432 | #undef SSE_UNOP_CASE |
1433 | #define SSE_BINOP_CASE(instruction, unused, code) \ |
1434 | case 0x##code: \ |
1435 | AppendToBuffer("v" #instruction " %s,%s,", NameOfAVXRegister(regop), \ |
1436 | NameOfAVXRegister(vvvv)); \ |
1437 | current += PrintRightAVXOperand(current); \ |
1438 | break; |
1439 | SSE_BINOP_INSTRUCTION_LIST(SSE_BINOP_CASE)SSE_BINOP_CASE(unpcklps, 0F, 14) SSE_BINOP_CASE(andps, 0F, 54 ) SSE_BINOP_CASE(andnps, 0F, 55) SSE_BINOP_CASE(orps, 0F, 56) SSE_BINOP_CASE(xorps, 0F, 57) SSE_BINOP_CASE(addps, 0F, 58) SSE_BINOP_CASE (mulps, 0F, 59) SSE_BINOP_CASE(subps, 0F, 5C) SSE_BINOP_CASE( minps, 0F, 5D) SSE_BINOP_CASE(divps, 0F, 5E) SSE_BINOP_CASE(maxps , 0F, 5F) |
1440 | #undef SSE_BINOP_CASE |
1441 | default: |
1442 | UnimplementedInstruction(); |
1443 | } |
1444 | } else if (vex_66() && vex_0f()) { |
1445 | int mod, regop, rm, vvvv = vex_vreg(); |
1446 | get_modrm(*current, &mod, ®op, &rm); |
1447 | switch (opcode) { |
1448 | case 0x10: |
1449 | AppendToBuffer("vmovupd %s,", NameOfAVXRegister(regop)); |
1450 | current += PrintRightAVXOperand(current); |
1451 | break; |
1452 | case 0x11: |
1453 | AppendToBuffer("vmovupd "); |
1454 | current += PrintRightAVXOperand(current); |
1455 | AppendToBuffer(",%s", NameOfAVXRegister(regop)); |
1456 | break; |
1457 | case 0x28: |
1458 | AppendToBuffer("vmovapd %s,", NameOfAVXRegister(regop)); |
1459 | current += PrintRightAVXOperand(current); |
1460 | break; |
1461 | case 0x29: |
1462 | AppendToBuffer("vmovapd "); |
1463 | current += PrintRightAVXOperand(current); |
1464 | AppendToBuffer(",%s", NameOfAVXRegister(regop)); |
1465 | break; |
1466 | case 0x50: |
1467 | AppendToBuffer("vmovmskpd %s,", NameOfCPURegister(regop)); |
1468 | current += PrintRightAVXOperand(current); |
1469 | break; |
1470 | case 0x6E: |
1471 | AppendToBuffer("vmov%c %s,", vex_w() ? 'q' : 'd', |
1472 | NameOfAVXRegister(regop)); |
1473 | current += PrintRightOperand(current); |
1474 | break; |
1475 | case 0x6F: |
1476 | AppendToBuffer("vmovdqa %s,", NameOfAVXRegister(regop)); |
1477 | current += PrintRightAVXOperand(current); |
1478 | break; |
1479 | case 0x70: |
1480 | AppendToBuffer("vpshufd %s,", NameOfAVXRegister(regop)); |
1481 | current += PrintRightAVXOperand(current); |
1482 | AppendToBuffer(",0x%x", *current++); |
1483 | break; |
1484 | case 0x71: |
1485 | AppendToBuffer("vps%sw %s,", sf_str[regop / 2], |
1486 | NameOfAVXRegister(vvvv)); |
1487 | current += PrintRightAVXOperand(current); |
1488 | AppendToBuffer(",%u", *current++); |
1489 | break; |
1490 | case 0x72: |
1491 | AppendToBuffer("vps%sd %s,", sf_str[regop / 2], |
1492 | NameOfAVXRegister(vvvv)); |
1493 | current += PrintRightAVXOperand(current); |
1494 | AppendToBuffer(",%u", *current++); |
1495 | break; |
1496 | case 0x73: |
1497 | AppendToBuffer("vps%sq %s,", sf_str[regop / 2], |
1498 | NameOfAVXRegister(vvvv)); |
1499 | current += PrintRightAVXOperand(current); |
1500 | AppendToBuffer(",%u", *current++); |
1501 | break; |
1502 | case 0x7E: |
1503 | AppendToBuffer("vmov%c ", vex_w() ? 'q' : 'd'); |
1504 | current += PrintRightOperand(current); |
1505 | AppendToBuffer(",%s", NameOfAVXRegister(regop)); |
1506 | break; |
1507 | case 0xC2: { |
1508 | AppendToBuffer("vcmppd %s,%s,", NameOfAVXRegister(regop), |
1509 | NameOfAVXRegister(vvvv)); |
1510 | current += PrintRightAVXOperand(current); |
1511 | AppendToBuffer(", (%s)", cmp_pseudo_op[*current]); |
1512 | current += 1; |
1513 | break; |
1514 | } |
1515 | case 0xC4: |
1516 | AppendToBuffer("vpinsrw %s,%s,", NameOfAVXRegister(regop), |
1517 | NameOfAVXRegister(vvvv)); |
1518 | current += PrintRightOperand(current); |
1519 | AppendToBuffer(",0x%x", *current++); |
1520 | break; |
1521 | case 0xC5: |
1522 | AppendToBuffer("vpextrw %s,", NameOfCPURegister(regop)); |
1523 | current += PrintRightAVXOperand(current); |
1524 | AppendToBuffer(",0x%x", *current++); |
1525 | break; |
1526 | case 0xD7: |
1527 | AppendToBuffer("vpmovmskb %s,", NameOfCPURegister(regop)); |
1528 | current += PrintRightAVXOperand(current); |
1529 | break; |
1530 | #define DECLARE_SSE_AVX_DIS_CASE(instruction, notUsed1, notUsed2, opcode) \ |
1531 | case 0x##opcode: { \ |
1532 | AppendToBuffer("v" #instruction " %s,%s,", NameOfAVXRegister(regop), \ |
1533 | NameOfAVXRegister(vvvv)); \ |
1534 | current += PrintRightAVXOperand(current); \ |
1535 | break; \ |
1536 | } |
1537 | |
1538 | SSE2_INSTRUCTION_LIST(DECLARE_SSE_AVX_DIS_CASE)DECLARE_SSE_AVX_DIS_CASE(andpd, 66, 0F, 54) DECLARE_SSE_AVX_DIS_CASE (andnpd, 66, 0F, 55) DECLARE_SSE_AVX_DIS_CASE(orpd, 66, 0F, 56 ) DECLARE_SSE_AVX_DIS_CASE(xorpd, 66, 0F, 57) DECLARE_SSE_AVX_DIS_CASE (addpd, 66, 0F, 58) DECLARE_SSE_AVX_DIS_CASE(mulpd, 66, 0F, 59 ) DECLARE_SSE_AVX_DIS_CASE(subpd, 66, 0F, 5C) DECLARE_SSE_AVX_DIS_CASE (minpd, 66, 0F, 5D) DECLARE_SSE_AVX_DIS_CASE(divpd, 66, 0F, 5E ) DECLARE_SSE_AVX_DIS_CASE(maxpd, 66, 0F, 5F) DECLARE_SSE_AVX_DIS_CASE (punpcklbw, 66, 0F, 60) DECLARE_SSE_AVX_DIS_CASE(punpcklwd, 66 , 0F, 61) DECLARE_SSE_AVX_DIS_CASE(punpckldq, 66, 0F, 62) DECLARE_SSE_AVX_DIS_CASE (packsswb, 66, 0F, 63) DECLARE_SSE_AVX_DIS_CASE(pcmpgtb, 66, 0F , 64) DECLARE_SSE_AVX_DIS_CASE(pcmpgtw, 66, 0F, 65) DECLARE_SSE_AVX_DIS_CASE (pcmpgtd, 66, 0F, 66) DECLARE_SSE_AVX_DIS_CASE(packuswb, 66, 0F , 67) DECLARE_SSE_AVX_DIS_CASE(punpckhbw, 66, 0F, 68) DECLARE_SSE_AVX_DIS_CASE (punpckhwd, 66, 0F, 69) DECLARE_SSE_AVX_DIS_CASE(punpckhdq, 66 , 0F, 6A) DECLARE_SSE_AVX_DIS_CASE(packssdw, 66, 0F, 6B) DECLARE_SSE_AVX_DIS_CASE (punpcklqdq, 66, 0F, 6C) DECLARE_SSE_AVX_DIS_CASE(punpckhqdq, 66, 0F, 6D) DECLARE_SSE_AVX_DIS_CASE(pcmpeqb, 66, 0F, 74) DECLARE_SSE_AVX_DIS_CASE (pcmpeqw, 66, 0F, 75) DECLARE_SSE_AVX_DIS_CASE(pcmpeqd, 66, 0F , 76) DECLARE_SSE_AVX_DIS_CASE(paddq, 66, 0F, D4) DECLARE_SSE_AVX_DIS_CASE (pmullw, 66, 0F, D5) DECLARE_SSE_AVX_DIS_CASE(psubusb, 66, 0F , D8) DECLARE_SSE_AVX_DIS_CASE(psubusw, 66, 0F, D9) DECLARE_SSE_AVX_DIS_CASE (pminub, 66, 0F, DA) DECLARE_SSE_AVX_DIS_CASE(pand, 66, 0F, DB ) DECLARE_SSE_AVX_DIS_CASE(paddusb, 66, 0F, DC) DECLARE_SSE_AVX_DIS_CASE (paddusw, 66, 0F, DD) DECLARE_SSE_AVX_DIS_CASE(pmaxub, 66, 0F , DE) DECLARE_SSE_AVX_DIS_CASE(pandn, 66, 0F, DF) DECLARE_SSE_AVX_DIS_CASE (pavgb, 66, 0F, E0) DECLARE_SSE_AVX_DIS_CASE(pavgw, 66, 0F, E3 ) DECLARE_SSE_AVX_DIS_CASE(pmulhuw, 66, 0F, E4) DECLARE_SSE_AVX_DIS_CASE (pmulhw, 66, 0F, E5) DECLARE_SSE_AVX_DIS_CASE(psubsb, 66, 0F, E8) DECLARE_SSE_AVX_DIS_CASE(psubsw, 66, 0F, E9) DECLARE_SSE_AVX_DIS_CASE (pminsw, 66, 0F, EA) DECLARE_SSE_AVX_DIS_CASE(por, 66, 0F, EB ) DECLARE_SSE_AVX_DIS_CASE(paddsb, 66, 0F, EC) DECLARE_SSE_AVX_DIS_CASE (paddsw, 66, 0F, ED) DECLARE_SSE_AVX_DIS_CASE(pmaxsw, 66, 0F, EE) DECLARE_SSE_AVX_DIS_CASE(pxor, 66, 0F, EF) DECLARE_SSE_AVX_DIS_CASE (pmuludq, 66, 0F, F4) DECLARE_SSE_AVX_DIS_CASE(pmaddwd, 66, 0F , F5) DECLARE_SSE_AVX_DIS_CASE(psubb, 66, 0F, F8) DECLARE_SSE_AVX_DIS_CASE (psubw, 66, 0F, F9) DECLARE_SSE_AVX_DIS_CASE(psubd, 66, 0F, FA ) DECLARE_SSE_AVX_DIS_CASE(psubq, 66, 0F, FB) DECLARE_SSE_AVX_DIS_CASE (paddb, 66, 0F, FC) DECLARE_SSE_AVX_DIS_CASE(paddw, 66, 0F, FD ) DECLARE_SSE_AVX_DIS_CASE(paddd, 66, 0F, FE) DECLARE_SSE_AVX_DIS_CASE (psrlw, 66, 0F, D1) DECLARE_SSE_AVX_DIS_CASE(psrld, 66, 0F, D2 ) DECLARE_SSE_AVX_DIS_CASE(psrlq, 66, 0F, D3) DECLARE_SSE_AVX_DIS_CASE (psraw, 66, 0F, E1) DECLARE_SSE_AVX_DIS_CASE(psrad, 66, 0F, E2 ) DECLARE_SSE_AVX_DIS_CASE(psllw, 66, 0F, F1) DECLARE_SSE_AVX_DIS_CASE (pslld, 66, 0F, F2) DECLARE_SSE_AVX_DIS_CASE(psllq, 66, 0F, F3 ) |
1539 | #undef DECLARE_SSE_AVX_DIS_CASE |
1540 | #define DECLARE_SSE_UNOP_AVX_DIS_CASE(instruction, notUsed1, notUsed2, opcode) \ |
1541 | case 0x##opcode: { \ |
1542 | AppendToBuffer("v" #instruction " %s,", NameOfAVXRegister(regop)); \ |
1543 | current += PrintRightAVXOperand(current); \ |
1544 | break; \ |
1545 | } |
1546 | |
1547 | SSE2_UNOP_INSTRUCTION_LIST(DECLARE_SSE_UNOP_AVX_DIS_CASE)DECLARE_SSE_UNOP_AVX_DIS_CASE(ucomisd, 66, 0F, 2E) DECLARE_SSE_UNOP_AVX_DIS_CASE (sqrtpd, 66, 0F, 51) DECLARE_SSE_UNOP_AVX_DIS_CASE(cvtpd2ps, 66 , 0F, 5A) DECLARE_SSE_UNOP_AVX_DIS_CASE(cvtps2dq, 66, 0F, 5B) DECLARE_SSE_UNOP_AVX_DIS_CASE(cvttpd2dq, 66, 0F, E6) |
1548 | #undef DECLARE_SSE_UNOP_AVX_DIS_CASE |
1549 | default: |
1550 | UnimplementedInstruction(); |
1551 | } |
1552 | |
1553 | } else { |
1554 | UnimplementedInstruction(); |
1555 | } |
1556 | |
1557 | return static_cast<int>(current - data); |
1558 | } |
1559 | |
1560 | // Returns number of bytes used, including *data. |
1561 | int DisassemblerX64::FPUInstruction(byte* data) { |
1562 | byte escape_opcode = *data; |
1563 | DCHECK_EQ(0xD8, escape_opcode & 0xF8)((void) 0); |
1564 | byte modrm_byte = *(data + 1); |
1565 | |
1566 | if (modrm_byte >= 0xC0) { |
1567 | return RegisterFPUInstruction(escape_opcode, modrm_byte); |
1568 | } else { |
1569 | return MemoryFPUInstruction(escape_opcode, modrm_byte, data + 1); |
1570 | } |
1571 | } |
1572 | |
1573 | int DisassemblerX64::MemoryFPUInstruction(int escape_opcode, int modrm_byte, |
1574 | byte* modrm_start) { |
1575 | const char* mnem = "?"; |
1576 | int regop = (modrm_byte >> 3) & 0x7; // reg/op field of modrm byte. |
1577 | switch (escape_opcode) { |
1578 | case 0xD9: |
1579 | switch (regop) { |
1580 | case 0: |
1581 | mnem = "fld_s"; |
1582 | break; |
1583 | case 3: |
1584 | mnem = "fstp_s"; |
1585 | break; |
1586 | case 7: |
1587 | mnem = "fstcw"; |
1588 | break; |
1589 | default: |
1590 | UnimplementedInstruction(); |
1591 | } |
1592 | break; |
1593 | |
1594 | case 0xDB: |
1595 | switch (regop) { |
1596 | case 0: |
1597 | mnem = "fild_s"; |
1598 | break; |
1599 | case 1: |
1600 | mnem = "fisttp_s"; |
1601 | break; |
1602 | case 2: |
1603 | mnem = "fist_s"; |
1604 | break; |
1605 | case 3: |
1606 | mnem = "fistp_s"; |
1607 | break; |
1608 | default: |
1609 | UnimplementedInstruction(); |
1610 | } |
1611 | break; |
1612 | |
1613 | case 0xDD: |
1614 | switch (regop) { |
1615 | case 0: |
1616 | mnem = "fld_d"; |
1617 | break; |
1618 | case 3: |
1619 | mnem = "fstp_d"; |
1620 | break; |
1621 | default: |
1622 | UnimplementedInstruction(); |
1623 | } |
1624 | break; |
1625 | |
1626 | case 0xDF: |
1627 | switch (regop) { |
1628 | case 5: |
1629 | mnem = "fild_d"; |
1630 | break; |
1631 | case 7: |
1632 | mnem = "fistp_d"; |
1633 | break; |
1634 | default: |
1635 | UnimplementedInstruction(); |
1636 | } |
1637 | break; |
1638 | |
1639 | default: |
1640 | UnimplementedInstruction(); |
1641 | } |
1642 | AppendToBuffer("%s ", mnem); |
1643 | int count = PrintRightOperand(modrm_start); |
1644 | return count + 1; |
1645 | } |
1646 | |
1647 | int DisassemblerX64::RegisterFPUInstruction(int escape_opcode, |
1648 | byte modrm_byte) { |
1649 | bool has_register = false; // Is the FPU register encoded in modrm_byte? |
1650 | const char* mnem = "?"; |
1651 | |
1652 | switch (escape_opcode) { |
1653 | case 0xD8: |
1654 | UnimplementedInstruction(); |
1655 | break; |
1656 | |
1657 | case 0xD9: |
1658 | switch (modrm_byte & 0xF8) { |
1659 | case 0xC0: |
1660 | mnem = "fld"; |
1661 | has_register = true; |
1662 | break; |
1663 | case 0xC8: |
1664 | mnem = "fxch"; |
1665 | has_register = true; |
1666 | break; |
1667 | default: |
1668 | switch (modrm_byte) { |
1669 | case 0xE0: |
1670 | mnem = "fchs"; |
1671 | break; |
1672 | case 0xE1: |
1673 | mnem = "fabs"; |
1674 | break; |
1675 | case 0xE3: |
1676 | mnem = "fninit"; |
1677 | break; |
1678 | case 0xE4: |
1679 | mnem = "ftst"; |
1680 | break; |
1681 | case 0xE8: |
1682 | mnem = "fld1"; |
1683 | break; |
1684 | case 0xEB: |
1685 | mnem = "fldpi"; |
1686 | break; |
1687 | case 0xED: |
1688 | mnem = "fldln2"; |
1689 | break; |
1690 | case 0xEE: |
1691 | mnem = "fldz"; |
1692 | break; |
1693 | case 0xF0: |
1694 | mnem = "f2xm1"; |
1695 | break; |
1696 | case 0xF1: |
1697 | mnem = "fyl2x"; |
1698 | break; |
1699 | case 0xF2: |
1700 | mnem = "fptan"; |
1701 | break; |
1702 | case 0xF5: |
1703 | mnem = "fprem1"; |
1704 | break; |
1705 | case 0xF7: |
1706 | mnem = "fincstp"; |
1707 | break; |
1708 | case 0xF8: |
1709 | mnem = "fprem"; |
1710 | break; |
1711 | case 0xFC: |
1712 | mnem = "frndint"; |
1713 | break; |
1714 | case 0xFD: |
1715 | mnem = "fscale"; |
1716 | break; |
1717 | case 0xFE: |
1718 | mnem = "fsin"; |
1719 | break; |
1720 | case 0xFF: |
1721 | mnem = "fcos"; |
1722 | break; |
1723 | default: |
1724 | UnimplementedInstruction(); |
1725 | } |
1726 | } |
1727 | break; |
1728 | |
1729 | case 0xDA: |
1730 | if (modrm_byte == 0xE9) { |
1731 | mnem = "fucompp"; |
1732 | } else { |
1733 | UnimplementedInstruction(); |
1734 | } |
1735 | break; |
1736 | |
1737 | case 0xDB: |
1738 | if ((modrm_byte & 0xF8) == 0xE8) { |
1739 | mnem = "fucomi"; |
1740 | has_register = true; |
1741 | } else if (modrm_byte == 0xE2) { |
1742 | mnem = "fclex"; |
1743 | } else if (modrm_byte == 0xE3) { |
1744 | mnem = "fninit"; |
1745 | } else { |
1746 | UnimplementedInstruction(); |
1747 | } |
1748 | break; |
1749 | |
1750 | case 0xDC: |
1751 | has_register = true; |
1752 | switch (modrm_byte & 0xF8) { |
1753 | case 0xC0: |
1754 | mnem = "fadd"; |
1755 | break; |
1756 | case 0xE8: |
1757 | mnem = "fsub"; |
1758 | break; |
1759 | case 0xC8: |
1760 | mnem = "fmul"; |
1761 | break; |
1762 | case 0xF8: |
1763 | mnem = "fdiv"; |
1764 | break; |
1765 | default: |
1766 | UnimplementedInstruction(); |
1767 | } |
1768 | break; |
1769 | |
1770 | case 0xDD: |
1771 | has_register = true; |
1772 | switch (modrm_byte & 0xF8) { |
1773 | case 0xC0: |
1774 | mnem = "ffree"; |
1775 | break; |
1776 | case 0xD8: |
1777 | mnem = "fstp"; |
1778 | break; |
1779 | default: |
1780 | UnimplementedInstruction(); |
1781 | } |
1782 | break; |
1783 | |
1784 | case 0xDE: |
1785 | if (modrm_byte == 0xD9) { |
1786 | mnem = "fcompp"; |
1787 | } else { |
1788 | has_register = true; |
1789 | switch (modrm_byte & 0xF8) { |
1790 | case 0xC0: |
1791 | mnem = "faddp"; |
1792 | break; |
1793 | case 0xE8: |
1794 | mnem = "fsubp"; |
1795 | break; |
1796 | case 0xC8: |
1797 | mnem = "fmulp"; |
1798 | break; |
1799 | case 0xF8: |
1800 | mnem = "fdivp"; |
1801 | break; |
1802 | default: |
1803 | UnimplementedInstruction(); |
1804 | } |
1805 | } |
1806 | break; |
1807 | |
1808 | case 0xDF: |
1809 | if (modrm_byte == 0xE0) { |
1810 | mnem = "fnstsw_ax"; |
1811 | } else if ((modrm_byte & 0xF8) == 0xE8) { |
1812 | mnem = "fucomip"; |
1813 | has_register = true; |
1814 | } |
1815 | break; |
1816 | |
1817 | default: |
1818 | UnimplementedInstruction(); |
1819 | } |
1820 | |
1821 | if (has_register) { |
1822 | AppendToBuffer("%s st%d", mnem, modrm_byte & 0x7); |
1823 | } else { |
1824 | AppendToBuffer("%s", mnem); |
1825 | } |
1826 | return 2; |
1827 | } |
1828 | |
1829 | // Handle all two-byte opcodes, which start with 0x0F. |
1830 | // These instructions may be affected by an 0x66, 0xF2, or 0xF3 prefix. |
1831 | int DisassemblerX64::TwoByteOpcodeInstruction(byte* data) { |
1832 | byte opcode = *(data + 1); |
1833 | byte* current = data + 2; |
1834 | // At return, "current" points to the start of the next instruction. |
1835 | const char* mnemonic = TwoByteMnemonic(opcode); |
1836 | // Not every instruction will use this, but it doesn't hurt to figure it out |
1837 | // here, since it doesn't update any pointers. |
1838 | int mod, regop, rm; |
1839 | get_modrm(*current, &mod, ®op, &rm); |
1840 | if (operand_size_ == 0x66) { |
1841 | // These are three-byte opcodes, see ThreeByteOpcodeInstruction. |
1842 | DCHECK_NE(0x38, opcode)((void) 0); |
1843 | DCHECK_NE(0x3A, opcode)((void) 0); |
1844 | // 0x66 0x0F prefix. |
1845 | if (opcode == 0xC1) { |
1846 | current += PrintOperands("xadd", OPER_REG_OP_ORDER, current); |
1847 | } else if (opcode == 0x1F) { |
1848 | current++; |
1849 | if (rm == 4) { // SIB byte present. |
1850 | current++; |
1851 | } |
1852 | if (mod == 1) { // Byte displacement. |
1853 | current += 1; |
1854 | } else if (mod == 2) { // 32-bit displacement. |
1855 | current += 4; |
1856 | } // else no immediate displacement. |
1857 | AppendToBuffer("nop"); |
1858 | } else if (opcode == 0x10) { |
1859 | current += PrintOperands("movupd", XMMREG_XMMOPER_OP_ORDER, current); |
1860 | } else if (opcode == 0x11) { |
1861 | current += PrintOperands("movupd", XMMOPER_XMMREG_OP_ORDER, current); |
1862 | } else if (opcode == 0x28) { |
1863 | current += PrintOperands("movapd", XMMREG_XMMOPER_OP_ORDER, current); |
1864 | } else if (opcode == 0x29) { |
1865 | current += PrintOperands("movapd", XMMOPER_XMMREG_OP_ORDER, current); |
1866 | } else if (opcode == 0x6E) { |
1867 | current += PrintOperands(rex_w() ? "movq" : "movd", XMMREG_OPER_OP_ORDER, |
1868 | current); |
1869 | } else if (opcode == 0x6F) { |
1870 | current += PrintOperands("movdqa", XMMREG_XMMOPER_OP_ORDER, current); |
1871 | } else if (opcode == 0x7E) { |
1872 | current += PrintOperands(rex_w() ? "movq" : "movd", OPER_XMMREG_OP_ORDER, |
1873 | current); |
1874 | } else if (opcode == 0x7F) { |
1875 | current += PrintOperands("movdqa", XMMOPER_XMMREG_OP_ORDER, current); |
1876 | } else if (opcode == 0xD6) { |
1877 | current += PrintOperands("movq", XMMOPER_XMMREG_OP_ORDER, current); |
1878 | } else if (opcode == 0x50) { |
1879 | AppendToBuffer("movmskpd %s,", NameOfCPURegister(regop)); |
1880 | current += PrintRightXMMOperand(current); |
1881 | } else if (opcode == 0x70) { |
1882 | current += PrintOperands("pshufd", XMMREG_XMMOPER_OP_ORDER, current); |
1883 | AppendToBuffer(",0x%x", *current++); |
1884 | } else if (opcode == 0x71) { |
1885 | current += 1; |
1886 | AppendToBuffer("ps%sw %s,%d", sf_str[regop / 2], NameOfXMMRegister(rm), |
1887 | *current & 0x7F); |
1888 | current += 1; |
1889 | } else if (opcode == 0x72) { |
1890 | current += 1; |
1891 | AppendToBuffer("ps%sd %s,%d", sf_str[regop / 2], NameOfXMMRegister(rm), |
1892 | *current & 0x7F); |
1893 | current += 1; |
1894 | } else if (opcode == 0x73) { |
1895 | current += 1; |
1896 | AppendToBuffer("ps%sq %s,%d", sf_str[regop / 2], NameOfXMMRegister(rm), |
1897 | *current & 0x7F); |
1898 | current += 1; |
1899 | } else if (opcode == 0xB1) { |
1900 | current += PrintOperands("cmpxchg", OPER_REG_OP_ORDER, current); |
1901 | } else if (opcode == 0xC2) { |
1902 | AppendToBuffer("cmppd %s,", NameOfXMMRegister(regop)); |
1903 | current += PrintRightXMMOperand(current); |
1904 | AppendToBuffer(", (%s)", cmp_pseudo_op[*current++]); |
1905 | } else if (opcode == 0xC4) { |
1906 | current += PrintOperands("pinsrw", XMMREG_OPER_OP_ORDER, current); |
1907 | AppendToBuffer(",0x%x", (*current++) & 7); |
1908 | } else if (opcode == 0xD7) { |
1909 | current += PrintOperands("pmovmskb", OPER_XMMREG_OP_ORDER, current); |
1910 | } else { |
1911 | #define SSE2_CASE(instruction, notUsed1, notUsed2, opcode) \ |
1912 | case 0x##opcode: \ |
1913 | mnemonic = "" #instruction; \ |
1914 | break; |
1915 | |
1916 | switch (opcode) { |
1917 | SSE2_INSTRUCTION_LIST(SSE2_CASE)SSE2_CASE(andpd, 66, 0F, 54) SSE2_CASE(andnpd, 66, 0F, 55) SSE2_CASE (orpd, 66, 0F, 56) SSE2_CASE(xorpd, 66, 0F, 57) SSE2_CASE(addpd , 66, 0F, 58) SSE2_CASE(mulpd, 66, 0F, 59) SSE2_CASE(subpd, 66 , 0F, 5C) SSE2_CASE(minpd, 66, 0F, 5D) SSE2_CASE(divpd, 66, 0F , 5E) SSE2_CASE(maxpd, 66, 0F, 5F) SSE2_CASE(punpcklbw, 66, 0F , 60) SSE2_CASE(punpcklwd, 66, 0F, 61) SSE2_CASE(punpckldq, 66 , 0F, 62) SSE2_CASE(packsswb, 66, 0F, 63) SSE2_CASE(pcmpgtb, 66 , 0F, 64) SSE2_CASE(pcmpgtw, 66, 0F, 65) SSE2_CASE(pcmpgtd, 66 , 0F, 66) SSE2_CASE(packuswb, 66, 0F, 67) SSE2_CASE(punpckhbw , 66, 0F, 68) SSE2_CASE(punpckhwd, 66, 0F, 69) SSE2_CASE(punpckhdq , 66, 0F, 6A) SSE2_CASE(packssdw, 66, 0F, 6B) SSE2_CASE(punpcklqdq , 66, 0F, 6C) SSE2_CASE(punpckhqdq, 66, 0F, 6D) SSE2_CASE(pcmpeqb , 66, 0F, 74) SSE2_CASE(pcmpeqw, 66, 0F, 75) SSE2_CASE(pcmpeqd , 66, 0F, 76) SSE2_CASE(paddq, 66, 0F, D4) SSE2_CASE(pmullw, 66 , 0F, D5) SSE2_CASE(psubusb, 66, 0F, D8) SSE2_CASE(psubusw, 66 , 0F, D9) SSE2_CASE(pminub, 66, 0F, DA) SSE2_CASE(pand, 66, 0F , DB) SSE2_CASE(paddusb, 66, 0F, DC) SSE2_CASE(paddusw, 66, 0F , DD) SSE2_CASE(pmaxub, 66, 0F, DE) SSE2_CASE(pandn, 66, 0F, DF ) SSE2_CASE(pavgb, 66, 0F, E0) SSE2_CASE(pavgw, 66, 0F, E3) SSE2_CASE (pmulhuw, 66, 0F, E4) SSE2_CASE(pmulhw, 66, 0F, E5) SSE2_CASE (psubsb, 66, 0F, E8) SSE2_CASE(psubsw, 66, 0F, E9) SSE2_CASE( pminsw, 66, 0F, EA) SSE2_CASE(por, 66, 0F, EB) SSE2_CASE(paddsb , 66, 0F, EC) SSE2_CASE(paddsw, 66, 0F, ED) SSE2_CASE(pmaxsw, 66, 0F, EE) SSE2_CASE(pxor, 66, 0F, EF) SSE2_CASE(pmuludq, 66 , 0F, F4) SSE2_CASE(pmaddwd, 66, 0F, F5) SSE2_CASE(psubb, 66, 0F, F8) SSE2_CASE(psubw, 66, 0F, F9) SSE2_CASE(psubd, 66, 0F , FA) SSE2_CASE(psubq, 66, 0F, FB) SSE2_CASE(paddb, 66, 0F, FC ) SSE2_CASE(paddw, 66, 0F, FD) SSE2_CASE(paddd, 66, 0F, FE) SSE2_CASE (psrlw, 66, 0F, D1) SSE2_CASE(psrld, 66, 0F, D2) SSE2_CASE(psrlq , 66, 0F, D3) SSE2_CASE(psraw, 66, 0F, E1) SSE2_CASE(psrad, 66 , 0F, E2) SSE2_CASE(psllw, 66, 0F, F1) SSE2_CASE(pslld, 66, 0F , F2) SSE2_CASE(psllq, 66, 0F, F3) |
1918 | SSE2_UNOP_INSTRUCTION_LIST(SSE2_CASE)SSE2_CASE(ucomisd, 66, 0F, 2E) SSE2_CASE(sqrtpd, 66, 0F, 51) SSE2_CASE (cvtpd2ps, 66, 0F, 5A) SSE2_CASE(cvtps2dq, 66, 0F, 5B) SSE2_CASE (cvttpd2dq, 66, 0F, E6) |
1919 | } |
1920 | #undef SSE2_CASE |
1921 | AppendToBuffer("%s %s,", mnemonic, NameOfXMMRegister(regop)); |
1922 | current += PrintRightXMMOperand(current); |
1923 | } |
1924 | } else if (group_1_prefix_ == 0xF2) { |
1925 | // Beginning of instructions with prefix 0xF2. |
1926 | if (opcode == 0x10) { |
1927 | // MOVSD: Move scalar double-precision fp to/from/between XMM registers. |
1928 | current += PrintOperands("movsd", XMMREG_XMMOPER_OP_ORDER, current); |
1929 | } else if (opcode == 0x11) { |
1930 | current += PrintOperands("movsd", XMMOPER_XMMREG_OP_ORDER, current); |
1931 | } else if (opcode == 0x12) { |
1932 | current += PrintOperands("movddup", XMMREG_XMMOPER_OP_ORDER, current); |
1933 | } else if (opcode == 0x2A) { |
1934 | // CVTSI2SD: integer to XMM double conversion. |
1935 | current += PrintOperands(mnemonic, XMMREG_OPER_OP_ORDER, current); |
1936 | } else if (opcode == 0x2C) { |
1937 | // CVTTSD2SI: |
1938 | // Convert with truncation scalar double-precision FP to integer. |
1939 | AppendToBuffer("cvttsd2si%c %s,", operand_size_code(), |
1940 | NameOfCPURegister(regop)); |
1941 | current += PrintRightXMMOperand(current); |
1942 | } else if (opcode == 0x2D) { |
1943 | // CVTSD2SI: Convert scalar double-precision FP to integer. |
1944 | AppendToBuffer("cvtsd2si%c %s,", operand_size_code(), |
1945 | NameOfCPURegister(regop)); |
1946 | current += PrintRightXMMOperand(current); |
1947 | } else if (opcode == 0x5B) { |
1948 | // CVTTPS2DQ: Convert packed single-precision FP values to packed signed |
1949 | // doubleword integer values |
1950 | AppendToBuffer("cvttps2dq%c %s,", operand_size_code(), |
1951 | NameOfCPURegister(regop)); |
1952 | current += PrintRightXMMOperand(current); |
1953 | } else if ((opcode & 0xF8) == 0x58 || opcode == 0x51) { |
1954 | // XMM arithmetic. Mnemonic was retrieved at the start of this function. |
1955 | current += PrintOperands(mnemonic, XMMREG_XMMOPER_OP_ORDER, current); |
1956 | } else if (opcode == 0x70) { |
1957 | current += PrintOperands("pshuflw", XMMREG_XMMOPER_OP_ORDER, current); |
1958 | AppendToBuffer(",%d", (*current++) & 7); |
1959 | } else if (opcode == 0xC2) { |
1960 | AppendToBuffer("cmp%ssd %s,%s", cmp_pseudo_op[current[1]], |
1961 | NameOfXMMRegister(regop), NameOfXMMRegister(rm)); |
1962 | current += 2; |
1963 | } else if (opcode == 0xF0) { |
1964 | current += PrintOperands("lddqu", XMMREG_OPER_OP_ORDER, current); |
1965 | } else if (opcode == 0x7C) { |
1966 | current += PrintOperands("haddps", XMMREG_XMMOPER_OP_ORDER, current); |
1967 | } else { |
1968 | UnimplementedInstruction(); |
1969 | } |
1970 | } else if (group_1_prefix_ == 0xF3) { |
1971 | // Instructions with prefix 0xF3. |
1972 | if (opcode == 0x10) { |
1973 | // MOVSS: Move scalar double-precision fp to/from/between XMM registers. |
1974 | current += PrintOperands("movss", XMMREG_OPER_OP_ORDER, current); |
1975 | } else if (opcode == 0x11) { |
1976 | current += PrintOperands("movss", OPER_XMMREG_OP_ORDER, current); |
1977 | } else if (opcode == 0x16) { |
1978 | current += PrintOperands("movshdup", XMMREG_XMMOPER_OP_ORDER, current); |
1979 | } else if (opcode == 0x2A) { |
1980 | // CVTSI2SS: integer to XMM single conversion. |
1981 | current += PrintOperands(mnemonic, XMMREG_OPER_OP_ORDER, current); |
1982 | } else if (opcode == 0x2C) { |
1983 | // CVTTSS2SI: |
1984 | // Convert with truncation scalar single-precision FP to dword integer. |
1985 | AppendToBuffer("cvttss2si%c %s,", operand_size_code(), |
1986 | NameOfCPURegister(regop)); |
1987 | current += PrintRightXMMOperand(current); |
1988 | } else if (opcode == 0x70) { |
1989 | current += PrintOperands("pshufhw", XMMREG_XMMOPER_OP_ORDER, current); |
1990 | AppendToBuffer(", %d", (*current++) & 7); |
1991 | } else if (opcode == 0x6F) { |
1992 | current += PrintOperands("movdqu", XMMREG_XMMOPER_OP_ORDER, current); |
1993 | } else if (opcode == 0x7E) { |
1994 | current += PrintOperands("movq", XMMREG_XMMOPER_OP_ORDER, current); |
1995 | } else if (opcode == 0x7F) { |
1996 | current += PrintOperands("movdqu", XMMOPER_XMMREG_OP_ORDER, current); |
1997 | } else if ((opcode & 0xF8) == 0x58 || opcode == 0x51) { |
1998 | // XMM arithmetic. Mnemonic was retrieved at the start of this function. |
1999 | current += PrintOperands(mnemonic, XMMREG_XMMOPER_OP_ORDER, current); |
2000 | } else if (opcode == 0xB8) { |
2001 | AppendToBuffer("popcnt%c %s,", operand_size_code(), |
2002 | NameOfCPURegister(regop)); |
2003 | current += PrintRightOperand(current); |
2004 | } else if (opcode == 0xBC) { |
2005 | AppendToBuffer("tzcnt%c %s,", operand_size_code(), |
2006 | NameOfCPURegister(regop)); |
2007 | current += PrintRightOperand(current); |
2008 | } else if (opcode == 0xBD) { |
2009 | AppendToBuffer("lzcnt%c %s,", operand_size_code(), |
2010 | NameOfCPURegister(regop)); |
2011 | current += PrintRightOperand(current); |
2012 | } else if (opcode == 0xC2) { |
2013 | AppendToBuffer("cmp%sss %s,%s", cmp_pseudo_op[current[1]], |
2014 | NameOfXMMRegister(regop), NameOfXMMRegister(rm)); |
2015 | current += 2; |
2016 | } else if (opcode == 0xE6) { |
2017 | current += PrintOperands("cvtdq2pd", XMMREG_XMMOPER_OP_ORDER, current); |
2018 | } else if (opcode == 0xAE) { |
2019 | // incssp[d|q] |
2020 | AppendToBuffer("incssp%c ", operand_size_code()); |
2021 | current += PrintRightOperand(current); |
2022 | } else { |
2023 | UnimplementedInstruction(); |
2024 | } |
2025 | } else if (opcode == 0x10) { |
2026 | // movups xmm, xmm/m128 |
2027 | current += PrintOperands("movups", XMMREG_XMMOPER_OP_ORDER, current); |
2028 | } else if (opcode == 0x11) { |
2029 | // movups xmm/m128, xmm |
2030 | current += PrintOperands("movups", XMMOPER_XMMREG_OP_ORDER, current); |
2031 | } else if (opcode == 0x12) { |
2032 | // movhlps xmm1, xmm2 |
2033 | // movlps xmm1, m64 |
2034 | if (mod == 0b11) { |
2035 | current += PrintOperands("movhlps", XMMREG_XMMOPER_OP_ORDER, current); |
2036 | } else { |
2037 | current += PrintOperands("movlps", XMMREG_OPER_OP_ORDER, current); |
2038 | } |
2039 | } else if (opcode == 0x13) { |
2040 | // movlps m64, xmm1 |
2041 | current += PrintOperands("movlps", XMMOPER_XMMREG_OP_ORDER, current); |
2042 | } else if (opcode == 0x16) { |
2043 | if (mod == 0b11) { |
2044 | current += PrintOperands("movlhps", XMMREG_XMMOPER_OP_ORDER, current); |
2045 | } else { |
2046 | current += PrintOperands("movhps", XMMREG_XMMOPER_OP_ORDER, current); |
2047 | } |
2048 | } else if (opcode == 0x17) { |
2049 | current += PrintOperands("movhps", XMMOPER_XMMREG_OP_ORDER, current); |
2050 | } else if (opcode == 0x1F) { |
2051 | // NOP |
2052 | current++; |
2053 | if (rm == 4) { // SIB byte present. |
2054 | current++; |
2055 | } |
2056 | if (mod == 1) { // Byte displacement. |
2057 | current += 1; |
2058 | } else if (mod == 2) { // 32-bit displacement. |
2059 | current += 4; |
2060 | } // else no immediate displacement. |
2061 | AppendToBuffer("nop"); |
2062 | |
2063 | } else if (opcode == 0x28) { |
2064 | current += PrintOperands("movaps", XMMREG_XMMOPER_OP_ORDER, current); |
2065 | } else if (opcode == 0x29) { |
2066 | current += PrintOperands("movaps", XMMOPER_XMMREG_OP_ORDER, current); |
2067 | } else if (opcode == 0x2E) { |
2068 | current += PrintOperands("ucomiss", XMMREG_XMMOPER_OP_ORDER, current); |
2069 | } else if (opcode == 0xA2) { |
2070 | // CPUID |
2071 | AppendToBuffer("%s", mnemonic); |
2072 | } else if ((opcode & 0xF0) == 0x40) { |
2073 | // CMOVcc: conditional move. |
2074 | int condition = opcode & 0x0F; |
2075 | const InstructionDesc& idesc = cmov_instructions[condition]; |
2076 | byte_size_operand_ = idesc.byte_size_operation; |
2077 | current += PrintOperands(idesc.mnem, idesc.op_order_, current); |
2078 | } else if (opcode == 0xC0) { |
2079 | byte_size_operand_ = true; |
2080 | current += PrintOperands("xadd", OPER_REG_OP_ORDER, current); |
2081 | } else if (opcode == 0xC1) { |
2082 | current += PrintOperands("xadd", OPER_REG_OP_ORDER, current); |
2083 | } else if (opcode == 0xC2) { |
2084 | // cmpps xmm, xmm/m128, imm8 |
2085 | AppendToBuffer("cmpps %s, ", NameOfXMMRegister(regop)); |
2086 | current += PrintRightXMMOperand(current); |
2087 | AppendToBuffer(", %s", cmp_pseudo_op[*current]); |
2088 | current += 1; |
2089 | } else if (opcode == 0xC6) { |
2090 | // shufps xmm, xmm/m128, imm8 |
2091 | AppendToBuffer("shufps %s, ", NameOfXMMRegister(regop)); |
2092 | current += PrintRightXMMOperand(current); |
2093 | AppendToBuffer(", %d", (*current) & 3); |
2094 | current += 1; |
2095 | } else if (opcode >= 0xC8 && opcode <= 0xCF) { |
2096 | // bswap |
2097 | int reg = (opcode - 0xC8) | (rex_r() ? 8 : 0); |
2098 | AppendToBuffer("bswap%c %s", operand_size_code(), NameOfCPURegister(reg)); |
2099 | } else if (opcode == 0x50) { |
2100 | // movmskps reg, xmm |
2101 | AppendToBuffer("movmskps %s,", NameOfCPURegister(regop)); |
2102 | current += PrintRightXMMOperand(current); |
2103 | } else if ((opcode & 0xF0) == 0x80) { |
2104 | // Jcc: Conditional jump (branch). |
2105 | current = data + JumpConditional(data); |
2106 | |
2107 | } else if (opcode == 0xBE || opcode == 0xBF || opcode == 0xB6 || |
2108 | opcode == 0xB7 || opcode == 0xAF) { |
2109 | // Size-extending moves, IMUL. |
2110 | current += PrintOperands(mnemonic, REG_OPER_OP_ORDER, current); |
2111 | } else if ((opcode & 0xF0) == 0x90) { |
2112 | // SETcc: Set byte on condition. Needs pointer to beginning of instruction. |
2113 | current = data + SetCC(data); |
2114 | } else if (opcode == 0xA3 || opcode == 0xA5 || opcode == 0xAB || |
2115 | opcode == 0xAD) { |
2116 | // BT (bit test), SHLD, BTS (bit test and set), |
2117 | // SHRD (double-precision shift) |
2118 | AppendToBuffer("%s ", mnemonic); |
2119 | current += PrintRightOperand(current); |
2120 | if (opcode == 0xAB) { |
2121 | AppendToBuffer(",%s", NameOfCPURegister(regop)); |
2122 | } else { |
2123 | AppendToBuffer(",%s,cl", NameOfCPURegister(regop)); |
2124 | } |
2125 | } else if (opcode == 0xBA) { |
2126 | // BTS / BTR (bit test and set/reset) with immediate |
2127 | mnemonic = regop == 5 ? "bts" : regop == 6 ? "btr" : "?"; |
2128 | AppendToBuffer("%s ", mnemonic); |
2129 | current += PrintRightOperand(current); |
2130 | AppendToBuffer(",%d", *current++); |
2131 | } else if (opcode == 0xB8 || opcode == 0xBC || opcode == 0xBD) { |
2132 | // POPCNT, CTZ, CLZ. |
2133 | AppendToBuffer("%s%c ", mnemonic, operand_size_code()); |
2134 | AppendToBuffer("%s,", NameOfCPURegister(regop)); |
2135 | current += PrintRightOperand(current); |
2136 | } else if (opcode == 0x0B) { |
2137 | AppendToBuffer("ud2"); |
2138 | } else if (opcode == 0xB0 || opcode == 0xB1) { |
2139 | // CMPXCHG. |
2140 | if (opcode == 0xB0) { |
2141 | byte_size_operand_ = true; |
2142 | } |
2143 | current += PrintOperands(mnemonic, OPER_REG_OP_ORDER, current); |
2144 | } else if (opcode == 0xAE && (data[2] & 0xF8) == 0xF0) { |
2145 | AppendToBuffer("mfence"); |
2146 | current = data + 3; |
2147 | } else if (opcode == 0xAE && (data[2] & 0xF8) == 0xE8) { |
2148 | AppendToBuffer("lfence"); |
2149 | current = data + 3; |
2150 | // clang-format off |
2151 | #define SSE_DISASM_CASE(instruction, unused, code) \ |
2152 | } else if (opcode == 0x##code) { \ |
2153 | current += PrintOperands(#instruction, XMMREG_XMMOPER_OP_ORDER, current); |
2154 | SSE_UNOP_INSTRUCTION_LIST(SSE_DISASM_CASE)SSE_DISASM_CASE(sqrtps, 0F, 51) SSE_DISASM_CASE(rsqrtps, 0F, 52 ) SSE_DISASM_CASE(rcpps, 0F, 53) SSE_DISASM_CASE(cvtps2pd, 0F , 5A) SSE_DISASM_CASE(cvtdq2ps, 0F, 5B) |
2155 | SSE_BINOP_INSTRUCTION_LIST(SSE_DISASM_CASE)SSE_DISASM_CASE(unpcklps, 0F, 14) SSE_DISASM_CASE(andps, 0F, 54 ) SSE_DISASM_CASE(andnps, 0F, 55) SSE_DISASM_CASE(orps, 0F, 56 ) SSE_DISASM_CASE(xorps, 0F, 57) SSE_DISASM_CASE(addps, 0F, 58 ) SSE_DISASM_CASE(mulps, 0F, 59) SSE_DISASM_CASE(subps, 0F, 5C ) SSE_DISASM_CASE(minps, 0F, 5D) SSE_DISASM_CASE(divps, 0F, 5E ) SSE_DISASM_CASE(maxps, 0F, 5F) |
2156 | #undef SSE_DISASM_CASE |
2157 | // clang-format on |
2158 | } else { |
2159 | UnimplementedInstruction(); |
2160 | } |
2161 | return static_cast<int>(current - data); |
2162 | } |
2163 | |
2164 | // Handle all three-byte opcodes, which start with 0x0F38 or 0x0F3A. |
2165 | // These instructions may be affected by an 0x66, 0xF2, or 0xF3 prefix, but we |
2166 | // only have instructions prefixed with 0x66 for now. |
2167 | int DisassemblerX64::ThreeByteOpcodeInstruction(byte* data) { |
2168 | DCHECK_EQ(0x0F, *data)((void) 0); |
2169 | // Only support 3-byte opcodes prefixed with 0x66 for now. |
2170 | DCHECK_EQ(0x66, operand_size_)((void) 0); |
2171 | byte second_byte = *(data + 1); |
2172 | byte third_byte = *(data + 2); |
2173 | byte* current = data + 3; |
2174 | int mod, regop, rm; |
2175 | get_modrm(*current, &mod, ®op, &rm); |
2176 | if (second_byte == 0x38) { |
2177 | switch (third_byte) { |
2178 | case 0x10: { |
2179 | current += PrintOperands("pblendvb", XMMREG_XMMOPER_OP_ORDER, current); |
2180 | AppendToBuffer(",<xmm0>"); |
2181 | break; |
2182 | } |
2183 | case 0x14: { |
2184 | current += PrintOperands("blendvps", XMMREG_XMMOPER_OP_ORDER, current); |
2185 | AppendToBuffer(",<xmm0>"); |
2186 | break; |
2187 | } |
2188 | case 0x15: { |
2189 | current += PrintOperands("blendvpd", XMMREG_XMMOPER_OP_ORDER, current); |
2190 | AppendToBuffer(",<xmm0>"); |
2191 | break; |
2192 | } |
2193 | #define SSE34_DIS_CASE(instruction, notUsed1, notUsed2, notUsed3, opcode) \ |
2194 | case 0x##opcode: { \ |
2195 | current += PrintOperands(#instruction, XMMREG_XMMOPER_OP_ORDER, current); \ |
2196 | break; \ |
2197 | } |
2198 | |
2199 | SSSE3_INSTRUCTION_LIST(SSE34_DIS_CASE)SSE34_DIS_CASE(pshufb, 66, 0F, 38, 00) SSE34_DIS_CASE(phaddw, 66, 0F, 38, 01) SSE34_DIS_CASE(phaddd, 66, 0F, 38, 02) SSE34_DIS_CASE (pmaddubsw, 66, 0F, 38, 04) SSE34_DIS_CASE(psignb, 66, 0F, 38 , 08) SSE34_DIS_CASE(psignw, 66, 0F, 38, 09) SSE34_DIS_CASE(psignd , 66, 0F, 38, 0A) SSE34_DIS_CASE(pmulhrsw, 66, 0F, 38, 0B) |
2200 | SSSE3_UNOP_INSTRUCTION_LIST(SSE34_DIS_CASE)SSE34_DIS_CASE(pabsb, 66, 0F, 38, 1C) SSE34_DIS_CASE(pabsw, 66 , 0F, 38, 1D) SSE34_DIS_CASE(pabsd, 66, 0F, 38, 1E) |
2201 | SSE4_INSTRUCTION_LIST(SSE34_DIS_CASE)SSE34_DIS_CASE(pmuldq, 66, 0F, 38, 28) SSE34_DIS_CASE(pcmpeqq , 66, 0F, 38, 29) SSE34_DIS_CASE(packusdw, 66, 0F, 38, 2B) SSE34_DIS_CASE (pminsb, 66, 0F, 38, 38) SSE34_DIS_CASE(pminsd, 66, 0F, 38, 39 ) SSE34_DIS_CASE(pminuw, 66, 0F, 38, 3A) SSE34_DIS_CASE(pminud , 66, 0F, 38, 3B) SSE34_DIS_CASE(pmaxsb, 66, 0F, 38, 3C) SSE34_DIS_CASE (pmaxsd, 66, 0F, 38, 3D) SSE34_DIS_CASE(pmaxuw, 66, 0F, 38, 3E ) SSE34_DIS_CASE(pmaxud, 66, 0F, 38, 3F) SSE34_DIS_CASE(pmulld , 66, 0F, 38, 40) |
2202 | SSE4_UNOP_INSTRUCTION_LIST(SSE34_DIS_CASE)SSE34_DIS_CASE(ptest, 66, 0F, 38, 17) SSE34_DIS_CASE(pmovsxbw , 66, 0F, 38, 20) SSE34_DIS_CASE(pmovsxwd, 66, 0F, 38, 23) SSE34_DIS_CASE (pmovsxdq, 66, 0F, 38, 25) SSE34_DIS_CASE(pmovzxbw, 66, 0F, 38 , 30) SSE34_DIS_CASE(pmovzxwd, 66, 0F, 38, 33) SSE34_DIS_CASE (pmovzxdq, 66, 0F, 38, 35) |
2203 | SSE4_2_INSTRUCTION_LIST(SSE34_DIS_CASE)SSE34_DIS_CASE(pcmpgtq, 66, 0F, 38, 37) |
2204 | #undef SSE34_DIS_CASE |
2205 | default: |
2206 | UnimplementedInstruction(); |
2207 | } |
2208 | } else { |
2209 | DCHECK_EQ(0x3A, second_byte)((void) 0); |
2210 | if (third_byte == 0x17) { |
2211 | current += PrintOperands("extractps", OPER_XMMREG_OP_ORDER, current); |
2212 | AppendToBuffer(",%d", (*current++) & 3); |
2213 | } else if (third_byte == 0x08) { |
2214 | current += PrintOperands("roundps", XMMREG_XMMOPER_OP_ORDER, current); |
2215 | AppendToBuffer(",0x%x", (*current++) & 3); |
2216 | } else if (third_byte == 0x09) { |
2217 | current += PrintOperands("roundpd", XMMREG_XMMOPER_OP_ORDER, current); |
2218 | AppendToBuffer(",0x%x", (*current++) & 3); |
2219 | } else if (third_byte == 0x0A) { |
2220 | current += PrintOperands("roundss", XMMREG_XMMOPER_OP_ORDER, current); |
2221 | AppendToBuffer(",0x%x", (*current++) & 3); |
2222 | } else if (third_byte == 0x0B) { |
2223 | current += PrintOperands("roundsd", XMMREG_XMMOPER_OP_ORDER, current); |
2224 | AppendToBuffer(",0x%x", (*current++) & 3); |
2225 | } else if (third_byte == 0x0E) { |
2226 | current += PrintOperands("pblendw", XMMREG_XMMOPER_OP_ORDER, current); |
2227 | AppendToBuffer(",0x%x", *current++); |
2228 | } else if (third_byte == 0x0F) { |
2229 | current += PrintOperands("palignr", XMMREG_XMMOPER_OP_ORDER, current); |
2230 | AppendToBuffer(",0x%x", *current++); |
2231 | } else if (third_byte == 0x14) { |
2232 | current += PrintOperands("pextrb", OPER_XMMREG_OP_ORDER, current); |
2233 | AppendToBuffer(",%d", (*current++) & 0xf); |
2234 | } else if (third_byte == 0x15) { |
2235 | current += PrintOperands("pextrw", OPER_XMMREG_OP_ORDER, current); |
2236 | AppendToBuffer(",%d", (*current++) & 7); |
2237 | } else if (third_byte == 0x16) { |
2238 | const char* mnem = rex_w() ? "pextrq" : "pextrd"; |
2239 | current += PrintOperands(mnem, OPER_XMMREG_OP_ORDER, current); |
2240 | AppendToBuffer(",%d", (*current++) & 3); |
2241 | } else if (third_byte == 0x20) { |
2242 | current += PrintOperands("pinsrb", XMMREG_OPER_OP_ORDER, current); |
2243 | AppendToBuffer(",%d", (*current++) & 3); |
2244 | } else if (third_byte == 0x21) { |
2245 | current += PrintOperands("insertps", XMMREG_XMMOPER_OP_ORDER, current); |
2246 | AppendToBuffer(",0x%x", *current++); |
2247 | } else if (third_byte == 0x22) { |
2248 | const char* mnem = rex_w() ? "pinsrq" : "pinsrd"; |
2249 | current += PrintOperands(mnem, XMMREG_OPER_OP_ORDER, current); |
2250 | AppendToBuffer(",%d", (*current++) & 3); |
2251 | } else { |
2252 | UnimplementedInstruction(); |
2253 | } |
2254 | } |
2255 | return static_cast<int>(current - data); |
2256 | } |
2257 | |
2258 | // Mnemonics for two-byte opcode instructions starting with 0x0F. |
2259 | // The argument is the second byte of the two-byte opcode. |
2260 | // Returns nullptr if the instruction is not handled here. |
2261 | const char* DisassemblerX64::TwoByteMnemonic(byte opcode) { |
2262 | if (opcode >= 0xC8 && opcode <= 0xCF) return "bswap"; |
2263 | switch (opcode) { |
2264 | case 0x1F: |
2265 | return "nop"; |
2266 | case 0x2A: // F2/F3 prefix. |
2267 | return (group_1_prefix_ == 0xF2) ? "cvtsi2sd" : "cvtsi2ss"; |
2268 | case 0x51: // F2/F3 prefix. |
2269 | return (group_1_prefix_ == 0xF2) ? "sqrtsd" : "sqrtss"; |
2270 | case 0x58: // F2/F3 prefix. |
2271 | return (group_1_prefix_ == 0xF2) ? "addsd" : "addss"; |
2272 | case 0x59: // F2/F3 prefix. |
2273 | return (group_1_prefix_ == 0xF2) ? "mulsd" : "mulss"; |
2274 | case 0x5A: // F2/F3 prefix. |
2275 | return (group_1_prefix_ == 0xF2) ? "cvtsd2ss" : "cvtss2sd"; |
2276 | case 0x5B: // F2/F3 prefix. |
2277 | return "cvttps2dq"; |
2278 | case 0x5D: // F2/F3 prefix. |
2279 | return (group_1_prefix_ == 0xF2) ? "minsd" : "minss"; |
2280 | case 0x5C: // F2/F3 prefix. |
2281 | return (group_1_prefix_ == 0xF2) ? "subsd" : "subss"; |
2282 | case 0x5E: // F2/F3 prefix. |
2283 | return (group_1_prefix_ == 0xF2) ? "divsd" : "divss"; |
2284 | case 0x5F: // F2/F3 prefix. |
2285 | return (group_1_prefix_ == 0xF2) ? "maxsd" : "maxss"; |
2286 | case 0xA2: |
2287 | return "cpuid"; |
2288 | case 0xA3: |
2289 | return "bt"; |
2290 | case 0xA5: |
2291 | return "shld"; |
2292 | case 0xAB: |
2293 | return "bts"; |
2294 | case 0xAD: |
2295 | return "shrd"; |
2296 | case 0xAF: |
2297 | return "imul"; |
2298 | case 0xB0: |
2299 | case 0xB1: |
2300 | return "cmpxchg"; |
2301 | case 0xB6: |
2302 | return "movzxb"; |
2303 | case 0xB7: |
2304 | return "movzxw"; |
2305 | case 0xBC: |
2306 | return "bsf"; |
2307 | case 0xBD: |
2308 | return "bsr"; |
2309 | case 0xBE: |
2310 | return "movsxb"; |
2311 | case 0xBF: |
2312 | return "movsxw"; |
2313 | case 0xC2: |
2314 | return "cmpss"; |
2315 | default: |
2316 | return nullptr; |
2317 | } |
2318 | } |
2319 | |
2320 | // Disassembles the instruction at instr, and writes it into out_buffer. |
2321 | int DisassemblerX64::InstructionDecode(v8::base::Vector<char> out_buffer, |
2322 | byte* instr) { |
2323 | tmp_buffer_pos_ = 0; // starting to write as position 0 |
2324 | byte* data = instr; |
2325 | bool processed = true; // Will be set to false if the current instruction |
2326 | // is not in 'instructions' table. |
2327 | byte current; |
2328 | |
2329 | // Scan for prefixes. |
2330 | while (true) { |
2331 | current = *data; |
2332 | if (current == OPERAND_SIZE_OVERRIDE_PREFIX) { // Group 3 prefix. |
2333 | operand_size_ = current; |
2334 | } else if ((current & 0xF0) == 0x40) { // REX prefix. |
2335 | setRex(current); |
2336 | if (rex_w()) AppendToBuffer("REX.W "); |
2337 | } else if ((current & 0xFE) == 0xF2) { // Group 1 prefix (0xF2 or 0xF3). |
2338 | group_1_prefix_ = current; |
2339 | } else if (current == LOCK_PREFIX) { |
2340 | AppendToBuffer("lock "); |
2341 | } else if (current == VEX3_PREFIX) { |
2342 | vex_byte0_ = current; |
2343 | vex_byte1_ = *(data + 1); |
2344 | vex_byte2_ = *(data + 2); |
2345 | setRex(0x40 | (~(vex_byte1_ >> 5) & 7) | ((vex_byte2_ >> 4) & 8)); |
2346 | data += 3; |
2347 | break; // Vex is the last prefix. |
2348 | } else if (current == VEX2_PREFIX) { |
2349 | vex_byte0_ = current; |
2350 | vex_byte1_ = *(data + 1); |
2351 | setRex(0x40 | (~(vex_byte1_ >> 5) & 4)); |
2352 | data += 2; |
2353 | break; // Vex is the last prefix. |
2354 | } else { // Not a prefix - an opcode. |
2355 | break; |
2356 | } |
2357 | data++; |
2358 | } |
2359 | |
2360 | // Decode AVX instructions. |
2361 | if (vex_byte0_ != 0) { |
2362 | processed = true; |
2363 | data += AVXInstruction(data); |
2364 | } else { |
2365 | const InstructionDesc& idesc = instruction_table_->Get(current); |
2366 | byte_size_operand_ = idesc.byte_size_operation; |
2367 | switch (idesc.type) { |
2368 | case ZERO_OPERANDS_INSTR: |
2369 | if ((current >= 0xA4 && current <= 0xA7) || |
2370 | (current >= 0xAA && current <= 0xAD)) { |
2371 | // String move or compare operations. |
2372 | if (group_1_prefix_ == REP_PREFIX) { |
2373 | // REP. |
2374 | AppendToBuffer("rep "); |
2375 | } |
2376 | AppendToBuffer("%s%c", idesc.mnem, operand_size_code()); |
2377 | } else { |
2378 | AppendToBuffer("%s%c", idesc.mnem, operand_size_code()); |
2379 | } |
2380 | data++; |
2381 | break; |
2382 | |
2383 | case TWO_OPERANDS_INSTR: |
2384 | data++; |
2385 | data += PrintOperands(idesc.mnem, idesc.op_order_, data); |
2386 | break; |
2387 | |
2388 | case JUMP_CONDITIONAL_SHORT_INSTR: |
2389 | data += JumpConditionalShort(data); |
2390 | break; |
2391 | |
2392 | case REGISTER_INSTR: |
2393 | AppendToBuffer("%s%c %s", idesc.mnem, operand_size_code(), |
2394 | NameOfCPURegister(base_reg(current & 0x07))); |
2395 | data++; |
2396 | break; |
2397 | case PUSHPOP_INSTR: |
2398 | AppendToBuffer("%s %s", idesc.mnem, |
2399 | NameOfCPURegister(base_reg(current & 0x07))); |
2400 | data++; |
2401 | break; |
2402 | case MOVE_REG_INSTR: { |
2403 | byte* addr = nullptr; |
2404 | switch (operand_size()) { |
2405 | case OPERAND_WORD_SIZE: |
2406 | addr = reinterpret_cast<byte*>(Imm16(data + 1)); |
2407 | data += 3; |
2408 | break; |
2409 | case OPERAND_DOUBLEWORD_SIZE: |
2410 | addr = reinterpret_cast<byte*>(Imm32_U(data + 1)); |
2411 | data += 5; |
2412 | break; |
2413 | case OPERAND_QUADWORD_SIZE: |
2414 | addr = reinterpret_cast<byte*>(Imm64(data + 1)); |
2415 | data += 9; |
2416 | break; |
2417 | default: |
2418 | UNREACHABLE()V8_Fatal("unreachable code"); |
2419 | } |
2420 | AppendToBuffer("mov%c %s,%s", operand_size_code(), |
2421 | NameOfCPURegister(base_reg(current & 0x07)), |
2422 | NameOfAddress(addr)); |
2423 | break; |
2424 | } |
2425 | |
2426 | case CALL_JUMP_INSTR: { |
2427 | byte* addr = data + Imm32(data + 1) + 5; |
2428 | AppendToBuffer("%s %s", idesc.mnem, NameOfAddress(addr)); |
2429 | data += 5; |
2430 | break; |
2431 | } |
2432 | |
2433 | case SHORT_IMMEDIATE_INSTR: { |
2434 | int32_t imm; |
2435 | if (operand_size() == OPERAND_WORD_SIZE) { |
2436 | imm = Imm16(data + 1); |
2437 | data += 3; |
2438 | } else { |
2439 | imm = Imm32(data + 1); |
2440 | data += 5; |
2441 | } |
2442 | AppendToBuffer("%s rax,0x%x", idesc.mnem, imm); |
2443 | break; |
2444 | } |
2445 | |
2446 | case NO_INSTR: |
2447 | processed = false; |
2448 | break; |
2449 | |
2450 | default: |
2451 | UNIMPLEMENTED()V8_Fatal("unimplemented code"); // This type is not implemented. |
2452 | } |
2453 | } |
2454 | |
2455 | // The first byte didn't match any of the simple opcodes, so we |
2456 | // need to do special processing on it. |
2457 | if (!processed) { |
2458 | switch (*data) { |
2459 | case 0xC2: |
2460 | AppendToBuffer("ret 0x%x", Imm16_U(data + 1)); |
2461 | data += 3; |
2462 | break; |
2463 | |
2464 | case 0x69: // fall through |
2465 | case 0x6B: { |
2466 | int count = 1; |
2467 | count += PrintOperands("imul", REG_OPER_OP_ORDER, data + count); |
2468 | AppendToBuffer(",0x"); |
2469 | if (*data == 0x69) { |
2470 | count += PrintImmediate(data + count, operand_size()); |
2471 | } else { |
2472 | count += PrintImmediate(data + count, OPERAND_BYTE_SIZE); |
2473 | } |
2474 | data += count; |
2475 | break; |
2476 | } |
2477 | |
2478 | case 0x81: // fall through |
2479 | case 0x83: // 0x81 with sign extension bit set |
2480 | data += PrintImmediateOp(data); |
2481 | break; |
2482 | |
2483 | case 0x0F: |
2484 | // Check for three-byte opcodes, 0x0F38 or 0x0F3A. |
2485 | if (*(data + 1) == 0x38 || *(data + 1) == 0x3A) { |
2486 | data += ThreeByteOpcodeInstruction(data); |
2487 | } else { |
2488 | data += TwoByteOpcodeInstruction(data); |
2489 | } |
2490 | break; |
2491 | |
2492 | case 0x8F: { |
2493 | data++; |
2494 | int mod, regop, rm; |
2495 | get_modrm(*data, &mod, ®op, &rm); |
2496 | if (regop == 0) { |
2497 | AppendToBuffer("pop "); |
2498 | data += PrintRightOperand(data); |
2499 | } |
2500 | } break; |
2501 | |
2502 | case 0xFF: { |
2503 | data++; |
2504 | int mod, regop, rm; |
2505 | get_modrm(*data, &mod, ®op, &rm); |
2506 | const char* mnem = nullptr; |
2507 | switch (regop) { |
2508 | case 0: |
2509 | mnem = "inc"; |
2510 | break; |
2511 | case 1: |
2512 | mnem = "dec"; |
2513 | break; |
2514 | case 2: |
2515 | mnem = "call"; |
2516 | break; |
2517 | case 4: |
2518 | mnem = "jmp"; |
2519 | break; |
2520 | case 6: |
2521 | mnem = "push"; |
2522 | break; |
2523 | default: |
2524 | mnem = "???"; |
2525 | } |
2526 | if (regop <= 1) { |
2527 | AppendToBuffer("%s%c ", mnem, operand_size_code()); |
2528 | } else { |
2529 | AppendToBuffer("%s ", mnem); |
2530 | } |
2531 | data += PrintRightOperand(data); |
2532 | } break; |
2533 | |
2534 | case 0xC7: // imm32, fall through |
2535 | case 0xC6: // imm8 |
2536 | { |
2537 | bool is_byte = *data == 0xC6; |
2538 | data++; |
2539 | if (is_byte) { |
2540 | AppendToBuffer("movb "); |
2541 | data += PrintRightByteOperand(data); |
2542 | int32_t imm = *data; |
2543 | AppendToBuffer(",0x%x", imm); |
2544 | data++; |
2545 | } else { |
2546 | AppendToBuffer("mov%c ", operand_size_code()); |
2547 | data += PrintRightOperand(data); |
2548 | if (operand_size() == OPERAND_WORD_SIZE) { |
2549 | AppendToBuffer(",0x%x", Imm16(data)); |
2550 | data += 2; |
2551 | } else { |
2552 | AppendToBuffer(",0x%x", Imm32(data)); |
2553 | data += 4; |
2554 | } |
2555 | } |
2556 | } break; |
2557 | |
2558 | case 0x80: { |
2559 | data++; |
2560 | AppendToBuffer("cmpb "); |
2561 | data += PrintRightByteOperand(data); |
2562 | int32_t imm = *data; |
2563 | AppendToBuffer(",0x%x", imm); |
2564 | data++; |
2565 | } break; |
2566 | |
2567 | case 0x88: // 8bit, fall through |
2568 | case 0x89: // 32bit |
2569 | { |
2570 | bool is_byte = *data == 0x88; |
2571 | int mod, regop, rm; |
2572 | data++; |
2573 | get_modrm(*data, &mod, ®op, &rm); |
2574 | if (is_byte) { |
2575 | AppendToBuffer("movb "); |
2576 | data += PrintRightByteOperand(data); |
2577 | AppendToBuffer(",%s", NameOfByteCPURegister(regop)); |
2578 | } else { |
2579 | AppendToBuffer("mov%c ", operand_size_code()); |
2580 | data += PrintRightOperand(data); |
2581 | AppendToBuffer(",%s", NameOfCPURegister(regop)); |
2582 | } |
2583 | } break; |
2584 | |
2585 | case 0x90: |
2586 | case 0x91: |
2587 | case 0x92: |
2588 | case 0x93: |
2589 | case 0x94: |
2590 | case 0x95: |
2591 | case 0x96: |
2592 | case 0x97: { |
2593 | int reg = (*data & 0x7) | (rex_b() ? 8 : 0); |
2594 | if (group_1_prefix_ == 0xF3 && *data == 0x90) { |
2595 | AppendToBuffer("pause"); |
2596 | } else if (reg == 0) { |
2597 | AppendToBuffer("nop"); // Common name for xchg rax,rax. |
2598 | } else { |
2599 | AppendToBuffer("xchg%c rax,%s", operand_size_code(), |
2600 | NameOfCPURegister(reg)); |
2601 | } |
2602 | data++; |
2603 | } break; |
2604 | case 0xB0: |
2605 | case 0xB1: |
2606 | case 0xB2: |
2607 | case 0xB3: |
2608 | case 0xB4: |
2609 | case 0xB5: |
2610 | case 0xB6: |
2611 | case 0xB7: |
2612 | case 0xB8: |
2613 | case 0xB9: |
2614 | case 0xBA: |
2615 | case 0xBB: |
2616 | case 0xBC: |
2617 | case 0xBD: |
2618 | case 0xBE: |
2619 | case 0xBF: { |
2620 | // mov reg8,imm8 or mov reg32,imm32 |
2621 | byte opcode = *data; |
2622 | data++; |
2623 | bool is_32bit = (opcode >= 0xB8); |
2624 | int reg = (opcode & 0x7) | (rex_b() ? 8 : 0); |
2625 | if (is_32bit) { |
2626 | AppendToBuffer("mov%c %s,", operand_size_code(), |
2627 | NameOfCPURegister(reg)); |
2628 | data += PrintImmediate(data, OPERAND_DOUBLEWORD_SIZE); |
2629 | } else { |
2630 | AppendToBuffer("movb %s,", NameOfByteCPURegister(reg)); |
2631 | data += PrintImmediate(data, OPERAND_BYTE_SIZE); |
2632 | } |
2633 | break; |
2634 | } |
2635 | case 0xFE: { |
2636 | data++; |
2637 | int mod, regop, rm; |
2638 | get_modrm(*data, &mod, ®op, &rm); |
2639 | if (regop == 1) { |
2640 | AppendToBuffer("decb "); |
2641 | data += PrintRightByteOperand(data); |
2642 | } else { |
2643 | UnimplementedInstruction(); |
2644 | } |
2645 | break; |
2646 | } |
2647 | case 0x68: |
2648 | AppendToBuffer("push 0x%x", Imm32(data + 1)); |
2649 | data += 5; |
2650 | break; |
2651 | |
2652 | case 0x6A: |
2653 | AppendToBuffer("push 0x%x", Imm8(data + 1)); |
2654 | data += 2; |
2655 | break; |
2656 | |
2657 | case 0xA1: // Fall through. |
2658 | case 0xA3: |
2659 | switch (operand_size()) { |
2660 | case OPERAND_DOUBLEWORD_SIZE: { |
2661 | const char* memory_location = |
2662 | NameOfAddress(reinterpret_cast<byte*>(Imm32(data + 1))); |
2663 | if (*data == 0xA1) { // Opcode 0xA1 |
2664 | AppendToBuffer("movzxlq rax,(%s)", memory_location); |
2665 | } else { // Opcode 0xA3 |
2666 | AppendToBuffer("movzxlq (%s),rax", memory_location); |
2667 | } |
2668 | data += 5; |
2669 | break; |
2670 | } |
2671 | case OPERAND_QUADWORD_SIZE: { |
2672 | // New x64 instruction mov rax,(imm_64). |
2673 | const char* memory_location = |
2674 | NameOfAddress(reinterpret_cast<byte*>(Imm64(data + 1))); |
2675 | if (*data == 0xA1) { // Opcode 0xA1 |
2676 | AppendToBuffer("movq rax,(%s)", memory_location); |
2677 | } else { // Opcode 0xA3 |
2678 | AppendToBuffer("movq (%s),rax", memory_location); |
2679 | } |
2680 | data += 9; |
2681 | break; |
2682 | } |
2683 | default: |
2684 | UnimplementedInstruction(); |
2685 | data += 2; |
2686 | } |
2687 | break; |
2688 | |
2689 | case 0xA8: |
2690 | AppendToBuffer("test al,0x%x", Imm8_U(data + 1)); |
2691 | data += 2; |
2692 | break; |
2693 | |
2694 | case 0xA9: { |
2695 | int64_t value = 0; |
2696 | switch (operand_size()) { |
2697 | case OPERAND_WORD_SIZE: |
2698 | value = Imm16_U(data + 1); |
2699 | data += 3; |
2700 | break; |
2701 | case OPERAND_DOUBLEWORD_SIZE: |
2702 | value = Imm32_U(data + 1); |
2703 | data += 5; |
2704 | break; |
2705 | case OPERAND_QUADWORD_SIZE: |
2706 | value = Imm32(data + 1); |
2707 | data += 5; |
2708 | break; |
2709 | default: |
2710 | UNREACHABLE()V8_Fatal("unreachable code"); |
2711 | } |
2712 | AppendToBuffer("test%c rax,0x%" PRIx64"l" "x", operand_size_code(), value); |
2713 | break; |
2714 | } |
2715 | case 0xD1: // fall through |
2716 | case 0xD3: // fall through |
2717 | case 0xC1: |
2718 | data += ShiftInstruction(data); |
2719 | break; |
2720 | case 0xD0: // fall through |
2721 | case 0xD2: // fall through |
2722 | case 0xC0: |
2723 | byte_size_operand_ = true; |
2724 | data += ShiftInstruction(data); |
2725 | break; |
2726 | |
2727 | case 0xD9: // fall through |
2728 | case 0xDA: // fall through |
2729 | case 0xDB: // fall through |
2730 | case 0xDC: // fall through |
2731 | case 0xDD: // fall through |
2732 | case 0xDE: // fall through |
2733 | case 0xDF: |
2734 | data += FPUInstruction(data); |
2735 | break; |
2736 | |
2737 | case 0xEB: |
2738 | data += JumpShort(data); |
2739 | break; |
2740 | |
2741 | case 0xF6: |
2742 | byte_size_operand_ = true; |
2743 | V8_FALLTHROUGH[[clang::fallthrough]]; |
2744 | case 0xF7: |
2745 | data += F6F7Instruction(data); |
2746 | break; |
2747 | |
2748 | case 0x3C: |
2749 | AppendToBuffer("cmp al,0x%x", Imm8(data + 1)); |
2750 | data += 2; |
2751 | break; |
2752 | |
2753 | default: |
2754 | UnimplementedInstruction(); |
2755 | data += 1; |
2756 | } |
2757 | } // !processed |
2758 | |
2759 | if (tmp_buffer_pos_ < sizeof tmp_buffer_) { |
2760 | tmp_buffer_[tmp_buffer_pos_] = '\0'; |
2761 | } |
2762 | |
2763 | int instr_len = static_cast<int>(data - instr); |
2764 | DCHECK_GT(instr_len, 0)((void) 0); // Ensure progress. |
2765 | |
2766 | int outp = 0; |
2767 | // Instruction bytes. |
2768 | for (byte* bp = instr; bp < data; bp++) { |
2769 | outp += v8::base::SNPrintF(out_buffer + outp, "%02x", *bp); |
2770 | } |
2771 | // Indent instruction, leaving space for 10 bytes, i.e. 20 characters in hex. |
2772 | // 10-byte mov is (probably) the largest we emit. |
2773 | while (outp < 20) { |
2774 | outp += v8::base::SNPrintF(out_buffer + outp, " "); |
2775 | } |
2776 | |
2777 | outp += v8::base::SNPrintF(out_buffer + outp, " %s", tmp_buffer_.begin()); |
Value stored to 'outp' is never read | |
2778 | return instr_len; |
2779 | } |
2780 | |
2781 | //------------------------------------------------------------------------------ |
2782 | |
2783 | static const char* const cpu_regs[16] = { |
2784 | "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", |
2785 | "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"}; |
2786 | |
2787 | static const char* const byte_cpu_regs[16] = { |
2788 | "al", "cl", "dl", "bl", "spl", "bpl", "sil", "dil", |
2789 | "r8l", "r9l", "r10l", "r11l", "r12l", "r13l", "r14l", "r15l"}; |
2790 | |
2791 | static const char* const xmm_regs[16] = { |
2792 | "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7", |
2793 | "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15"}; |
2794 | |
2795 | static const char* const ymm_regs[16] = { |
2796 | "ymm0", "ymm1", "ymm2", "ymm3", "ymm4", "ymm5", "ymm6", "ymm7", |
2797 | "ymm8", "ymm9", "ymm10", "ymm11", "ymm12", "ymm13", "ymm14", "ymm15"}; |
2798 | |
2799 | const char* NameConverter::NameOfAddress(byte* addr) const { |
2800 | v8::base::SNPrintF(tmp_buffer_, "%p", static_cast<void*>(addr)); |
2801 | return tmp_buffer_.begin(); |
2802 | } |
2803 | |
2804 | const char* NameConverter::NameOfConstant(byte* addr) const { |
2805 | return NameOfAddress(addr); |
2806 | } |
2807 | |
2808 | const char* NameConverter::NameOfCPURegister(int reg) const { |
2809 | if (0 <= reg && reg < 16) return cpu_regs[reg]; |
2810 | return "noreg"; |
2811 | } |
2812 | |
2813 | const char* NameConverter::NameOfByteCPURegister(int reg) const { |
2814 | if (0 <= reg && reg < 16) return byte_cpu_regs[reg]; |
2815 | return "noreg"; |
2816 | } |
2817 | |
2818 | const char* NameConverter::NameOfXMMRegister(int reg) const { |
2819 | if (0 <= reg && reg < 16) return xmm_regs[reg]; |
2820 | return "noxmmreg"; |
2821 | } |
2822 | |
2823 | const char* NameOfYMMRegister(int reg) { |
2824 | if (0 <= reg && reg < 16) return ymm_regs[reg]; |
2825 | return "noymmreg"; |
2826 | } |
2827 | |
2828 | const char* NameConverter::NameInCode(byte* addr) const { |
2829 | // X64 does not embed debug strings at the moment. |
2830 | UNREACHABLE()V8_Fatal("unreachable code"); |
2831 | } |
2832 | |
2833 | //------------------------------------------------------------------------------ |
2834 | |
2835 | int Disassembler::InstructionDecode(v8::base::Vector<char> buffer, |
2836 | byte* instruction) { |
2837 | DisassemblerX64 d(converter_, unimplemented_opcode_action()); |
2838 | return d.InstructionDecode(buffer, instruction); |
2839 | } |
2840 | |
2841 | // The X64 assembler does not use constant pools. |
2842 | int Disassembler::ConstantPoolSizeAt(byte* instruction) { return -1; } |
2843 | |
2844 | void Disassembler::Disassemble(FILE* f, byte* begin, byte* end, |
2845 | UnimplementedOpcodeAction unimplemented_action) { |
2846 | NameConverter converter; |
2847 | Disassembler d(converter, unimplemented_action); |
2848 | for (byte* pc = begin; pc < end;) { |
2849 | v8::base::EmbeddedVector<char, 128> buffer; |
2850 | buffer[0] = '\0'; |
2851 | byte* prev_pc = pc; |
2852 | pc += d.InstructionDecode(buffer, pc); |
2853 | fprintf(f, "%p", static_cast<void*>(prev_pc)); |
2854 | fprintf(f, " "); |
2855 | |
2856 | for (byte* bp = prev_pc; bp < pc; bp++) { |
2857 | fprintf(f, "%02x", *bp); |
2858 | } |
2859 | for (int i = 6 - static_cast<int>(pc - prev_pc); i >= 0; i--) { |
2860 | fprintf(f, " "); |
2861 | } |
2862 | fprintf(f, " %s\n", buffer.begin()); |
2863 | } |
2864 | } |
2865 | |
2866 | } // namespace disasm |
2867 | |
2868 | #endif // V8_TARGET_ARCH_X64 |