blob: 2b8fc2d4dcf4456a29504b6e4f41fe482e1bfb99 [file] [log] [blame]
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001// Copyright 2011 the V8 project authors. All rights reserved.
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
Steve Blocka7e24c12009-10-30 11:49:00 +00004
5#include <assert.h>
Steve Blocka7e24c12009-10-30 11:49:00 +00006#include <stdarg.h>
Ben Murdochb8a8cc12014-11-26 15:28:44 +00007#include <stdio.h>
Steve Blocka7e24c12009-10-30 11:49:00 +00008
Ben Murdochb8a8cc12014-11-26 15:28:44 +00009#include "src/v8.h"
Leon Clarkef7060e22010-06-03 12:02:55 +010010
Ben Murdochb8a8cc12014-11-26 15:28:44 +000011#if V8_TARGET_ARCH_X64
Leon Clarkef7060e22010-06-03 12:02:55 +010012
Ben Murdochb8a8cc12014-11-26 15:28:44 +000013#include "src/base/lazy-instance.h"
14#include "src/disasm.h"
Steve Blocka7e24c12009-10-30 11:49:00 +000015
16namespace disasm {
17
18enum OperandType {
19 UNSET_OP_ORDER = 0,
20 // Operand size decides between 16, 32 and 64 bit operands.
21 REG_OPER_OP_ORDER = 1, // Register destination, operand source.
22 OPER_REG_OP_ORDER = 2, // Operand destination, register source.
23 // Fixed 8-bit operands.
24 BYTE_SIZE_OPERAND_FLAG = 4,
25 BYTE_REG_OPER_OP_ORDER = REG_OPER_OP_ORDER | BYTE_SIZE_OPERAND_FLAG,
26 BYTE_OPER_REG_OP_ORDER = OPER_REG_OP_ORDER | BYTE_SIZE_OPERAND_FLAG
27};
28
Ben Murdochb8a8cc12014-11-26 15:28:44 +000029
Steve Blocka7e24c12009-10-30 11:49:00 +000030//------------------------------------------------------------------
31// Tables
32//------------------------------------------------------------------
33struct ByteMnemonic {
34 int b; // -1 terminates, otherwise must be in range (0..255)
35 OperandType op_order_;
36 const char* mnem;
37};
38
39
Ben Murdoch69a99ed2011-11-30 16:03:39 +000040static const ByteMnemonic two_operands_instr[] = {
Steve Blocka7e24c12009-10-30 11:49:00 +000041 { 0x00, BYTE_OPER_REG_OP_ORDER, "add" },
42 { 0x01, OPER_REG_OP_ORDER, "add" },
43 { 0x02, BYTE_REG_OPER_OP_ORDER, "add" },
44 { 0x03, REG_OPER_OP_ORDER, "add" },
45 { 0x08, BYTE_OPER_REG_OP_ORDER, "or" },
46 { 0x09, OPER_REG_OP_ORDER, "or" },
47 { 0x0A, BYTE_REG_OPER_OP_ORDER, "or" },
48 { 0x0B, REG_OPER_OP_ORDER, "or" },
49 { 0x10, BYTE_OPER_REG_OP_ORDER, "adc" },
50 { 0x11, OPER_REG_OP_ORDER, "adc" },
51 { 0x12, BYTE_REG_OPER_OP_ORDER, "adc" },
52 { 0x13, REG_OPER_OP_ORDER, "adc" },
53 { 0x18, BYTE_OPER_REG_OP_ORDER, "sbb" },
54 { 0x19, OPER_REG_OP_ORDER, "sbb" },
55 { 0x1A, BYTE_REG_OPER_OP_ORDER, "sbb" },
56 { 0x1B, REG_OPER_OP_ORDER, "sbb" },
57 { 0x20, BYTE_OPER_REG_OP_ORDER, "and" },
58 { 0x21, OPER_REG_OP_ORDER, "and" },
59 { 0x22, BYTE_REG_OPER_OP_ORDER, "and" },
60 { 0x23, REG_OPER_OP_ORDER, "and" },
61 { 0x28, BYTE_OPER_REG_OP_ORDER, "sub" },
62 { 0x29, OPER_REG_OP_ORDER, "sub" },
63 { 0x2A, BYTE_REG_OPER_OP_ORDER, "sub" },
64 { 0x2B, REG_OPER_OP_ORDER, "sub" },
65 { 0x30, BYTE_OPER_REG_OP_ORDER, "xor" },
66 { 0x31, OPER_REG_OP_ORDER, "xor" },
67 { 0x32, BYTE_REG_OPER_OP_ORDER, "xor" },
68 { 0x33, REG_OPER_OP_ORDER, "xor" },
69 { 0x38, BYTE_OPER_REG_OP_ORDER, "cmp" },
70 { 0x39, OPER_REG_OP_ORDER, "cmp" },
71 { 0x3A, BYTE_REG_OPER_OP_ORDER, "cmp" },
72 { 0x3B, REG_OPER_OP_ORDER, "cmp" },
Ben Murdochb8a8cc12014-11-26 15:28:44 +000073 { 0x63, REG_OPER_OP_ORDER, "movsxl" },
Steve Blocka7e24c12009-10-30 11:49:00 +000074 { 0x84, BYTE_REG_OPER_OP_ORDER, "test" },
75 { 0x85, REG_OPER_OP_ORDER, "test" },
76 { 0x86, BYTE_REG_OPER_OP_ORDER, "xchg" },
77 { 0x87, REG_OPER_OP_ORDER, "xchg" },
78 { 0x88, BYTE_OPER_REG_OP_ORDER, "mov" },
79 { 0x89, OPER_REG_OP_ORDER, "mov" },
80 { 0x8A, BYTE_REG_OPER_OP_ORDER, "mov" },
81 { 0x8B, REG_OPER_OP_ORDER, "mov" },
82 { 0x8D, REG_OPER_OP_ORDER, "lea" },
83 { -1, UNSET_OP_ORDER, "" }
84};
85
86
Ben Murdoch69a99ed2011-11-30 16:03:39 +000087static const ByteMnemonic zero_operands_instr[] = {
Steve Blocka7e24c12009-10-30 11:49:00 +000088 { 0xC3, UNSET_OP_ORDER, "ret" },
89 { 0xC9, UNSET_OP_ORDER, "leave" },
90 { 0xF4, UNSET_OP_ORDER, "hlt" },
Ben Murdoch3ef787d2012-04-12 10:51:47 +010091 { 0xFC, UNSET_OP_ORDER, "cld" },
Steve Blocka7e24c12009-10-30 11:49:00 +000092 { 0xCC, UNSET_OP_ORDER, "int3" },
93 { 0x60, UNSET_OP_ORDER, "pushad" },
94 { 0x61, UNSET_OP_ORDER, "popad" },
95 { 0x9C, UNSET_OP_ORDER, "pushfd" },
96 { 0x9D, UNSET_OP_ORDER, "popfd" },
97 { 0x9E, UNSET_OP_ORDER, "sahf" },
98 { 0x99, UNSET_OP_ORDER, "cdq" },
99 { 0x9B, UNSET_OP_ORDER, "fwait" },
Leon Clarked91b9f72010-01-27 17:25:45 +0000100 { 0xA4, UNSET_OP_ORDER, "movs" },
101 { 0xA5, UNSET_OP_ORDER, "movs" },
102 { 0xA6, UNSET_OP_ORDER, "cmps" },
103 { 0xA7, UNSET_OP_ORDER, "cmps" },
Steve Blocka7e24c12009-10-30 11:49:00 +0000104 { -1, UNSET_OP_ORDER, "" }
105};
106
107
Ben Murdoch69a99ed2011-11-30 16:03:39 +0000108static const ByteMnemonic call_jump_instr[] = {
Steve Blocka7e24c12009-10-30 11:49:00 +0000109 { 0xE8, UNSET_OP_ORDER, "call" },
110 { 0xE9, UNSET_OP_ORDER, "jmp" },
111 { -1, UNSET_OP_ORDER, "" }
112};
113
114
Ben Murdoch69a99ed2011-11-30 16:03:39 +0000115static const ByteMnemonic short_immediate_instr[] = {
Steve Blocka7e24c12009-10-30 11:49:00 +0000116 { 0x05, UNSET_OP_ORDER, "add" },
117 { 0x0D, UNSET_OP_ORDER, "or" },
118 { 0x15, UNSET_OP_ORDER, "adc" },
119 { 0x1D, UNSET_OP_ORDER, "sbb" },
120 { 0x25, UNSET_OP_ORDER, "and" },
121 { 0x2D, UNSET_OP_ORDER, "sub" },
122 { 0x35, UNSET_OP_ORDER, "xor" },
123 { 0x3D, UNSET_OP_ORDER, "cmp" },
124 { -1, UNSET_OP_ORDER, "" }
125};
126
127
Ben Murdoch69a99ed2011-11-30 16:03:39 +0000128static const char* const conditional_code_suffix[] = {
Steve Blocka7e24c12009-10-30 11:49:00 +0000129 "o", "no", "c", "nc", "z", "nz", "na", "a",
130 "s", "ns", "pe", "po", "l", "ge", "le", "g"
131};
132
133
134enum InstructionType {
135 NO_INSTR,
136 ZERO_OPERANDS_INSTR,
137 TWO_OPERANDS_INSTR,
138 JUMP_CONDITIONAL_SHORT_INSTR,
139 REGISTER_INSTR,
140 PUSHPOP_INSTR, // Has implicit 64-bit operand size.
141 MOVE_REG_INSTR,
142 CALL_JUMP_INSTR,
143 SHORT_IMMEDIATE_INSTR
144};
145
146
Leon Clarked91b9f72010-01-27 17:25:45 +0000147enum Prefixes {
148 ESCAPE_PREFIX = 0x0F,
149 OPERAND_SIZE_OVERRIDE_PREFIX = 0x66,
150 ADDRESS_SIZE_OVERRIDE_PREFIX = 0x67,
151 REPNE_PREFIX = 0xF2,
152 REP_PREFIX = 0xF3,
153 REPEQ_PREFIX = REP_PREFIX
154};
155
156
Steve Blocka7e24c12009-10-30 11:49:00 +0000157struct InstructionDesc {
158 const char* mnem;
159 InstructionType type;
160 OperandType op_order_;
161 bool byte_size_operation; // Fixed 8-bit operation.
162};
163
164
165class InstructionTable {
166 public:
167 InstructionTable();
168 const InstructionDesc& Get(byte x) const {
169 return instructions_[x];
170 }
171
172 private:
173 InstructionDesc instructions_[256];
174 void Clear();
175 void Init();
Ben Murdoch69a99ed2011-11-30 16:03:39 +0000176 void CopyTable(const ByteMnemonic bm[], InstructionType type);
Steve Blocka7e24c12009-10-30 11:49:00 +0000177 void SetTableRange(InstructionType type, byte start, byte end, bool byte_size,
178 const char* mnem);
179 void AddJumpConditionalShort();
180};
181
182
183InstructionTable::InstructionTable() {
184 Clear();
185 Init();
186}
187
188
189void InstructionTable::Clear() {
190 for (int i = 0; i < 256; i++) {
191 instructions_[i].mnem = "(bad)";
192 instructions_[i].type = NO_INSTR;
193 instructions_[i].op_order_ = UNSET_OP_ORDER;
194 instructions_[i].byte_size_operation = false;
195 }
196}
197
198
199void InstructionTable::Init() {
200 CopyTable(two_operands_instr, TWO_OPERANDS_INSTR);
201 CopyTable(zero_operands_instr, ZERO_OPERANDS_INSTR);
202 CopyTable(call_jump_instr, CALL_JUMP_INSTR);
203 CopyTable(short_immediate_instr, SHORT_IMMEDIATE_INSTR);
204 AddJumpConditionalShort();
205 SetTableRange(PUSHPOP_INSTR, 0x50, 0x57, false, "push");
206 SetTableRange(PUSHPOP_INSTR, 0x58, 0x5F, false, "pop");
207 SetTableRange(MOVE_REG_INSTR, 0xB8, 0xBF, false, "mov");
208}
209
210
Ben Murdoch69a99ed2011-11-30 16:03:39 +0000211void InstructionTable::CopyTable(const ByteMnemonic bm[],
212 InstructionType type) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000213 for (int i = 0; bm[i].b >= 0; i++) {
214 InstructionDesc* id = &instructions_[bm[i].b];
215 id->mnem = bm[i].mnem;
216 OperandType op_order = bm[i].op_order_;
217 id->op_order_ =
218 static_cast<OperandType>(op_order & ~BYTE_SIZE_OPERAND_FLAG);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000219 DCHECK_EQ(NO_INSTR, id->type); // Information not already entered
Steve Blocka7e24c12009-10-30 11:49:00 +0000220 id->type = type;
221 id->byte_size_operation = ((op_order & BYTE_SIZE_OPERAND_FLAG) != 0);
222 }
223}
224
225
226void InstructionTable::SetTableRange(InstructionType type,
227 byte start,
228 byte end,
229 bool byte_size,
230 const char* mnem) {
231 for (byte b = start; b <= end; b++) {
232 InstructionDesc* id = &instructions_[b];
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000233 DCHECK_EQ(NO_INSTR, id->type); // Information not already entered
Steve Blocka7e24c12009-10-30 11:49:00 +0000234 id->mnem = mnem;
235 id->type = type;
236 id->byte_size_operation = byte_size;
237 }
238}
239
240
241void InstructionTable::AddJumpConditionalShort() {
242 for (byte b = 0x70; b <= 0x7F; b++) {
243 InstructionDesc* id = &instructions_[b];
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000244 DCHECK_EQ(NO_INSTR, id->type); // Information not already entered
Steve Blocka7e24c12009-10-30 11:49:00 +0000245 id->mnem = NULL; // Computed depending on condition code.
246 id->type = JUMP_CONDITIONAL_SHORT_INSTR;
247 }
248}
249
250
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000251static v8::base::LazyInstance<InstructionTable>::type instruction_table =
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100252 LAZY_INSTANCE_INITIALIZER;
Steve Blocka7e24c12009-10-30 11:49:00 +0000253
Steve Block44f0eee2011-05-26 01:26:41 +0100254
Steve Blocka7e24c12009-10-30 11:49:00 +0000255static InstructionDesc cmov_instructions[16] = {
256 {"cmovo", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
257 {"cmovno", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
258 {"cmovc", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
259 {"cmovnc", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
260 {"cmovz", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
261 {"cmovnz", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
262 {"cmovna", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
263 {"cmova", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
264 {"cmovs", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
265 {"cmovns", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
266 {"cmovpe", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
267 {"cmovpo", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
268 {"cmovl", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
269 {"cmovge", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
270 {"cmovle", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
271 {"cmovg", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false}
272};
273
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000274
Steve Blocka7e24c12009-10-30 11:49:00 +0000275//------------------------------------------------------------------------------
276// DisassemblerX64 implementation.
277
278enum UnimplementedOpcodeAction {
279 CONTINUE_ON_UNIMPLEMENTED_OPCODE,
280 ABORT_ON_UNIMPLEMENTED_OPCODE
281};
282
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000283
Steve Blocka7e24c12009-10-30 11:49:00 +0000284// A new DisassemblerX64 object is created to disassemble each instruction.
285// The object can only disassemble a single instruction.
286class DisassemblerX64 {
287 public:
288 DisassemblerX64(const NameConverter& converter,
289 UnimplementedOpcodeAction unimplemented_action =
290 ABORT_ON_UNIMPLEMENTED_OPCODE)
291 : converter_(converter),
292 tmp_buffer_pos_(0),
293 abort_on_unimplemented_(
294 unimplemented_action == ABORT_ON_UNIMPLEMENTED_OPCODE),
295 rex_(0),
296 operand_size_(0),
297 group_1_prefix_(0),
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000298 byte_size_operand_(false),
299 instruction_table_(instruction_table.Pointer()) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000300 tmp_buffer_[0] = '\0';
301 }
302
303 virtual ~DisassemblerX64() {
304 }
305
306 // Writes one disassembled instruction into 'buffer' (0-terminated).
307 // Returns the length of the disassembled machine instruction in bytes.
308 int InstructionDecode(v8::internal::Vector<char> buffer, byte* instruction);
309
310 private:
311 enum OperandSize {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000312 OPERAND_BYTE_SIZE = 0,
313 OPERAND_WORD_SIZE = 1,
314 OPERAND_DOUBLEWORD_SIZE = 2,
315 OPERAND_QUADWORD_SIZE = 3
Steve Blocka7e24c12009-10-30 11:49:00 +0000316 };
317
318 const NameConverter& converter_;
319 v8::internal::EmbeddedVector<char, 128> tmp_buffer_;
320 unsigned int tmp_buffer_pos_;
321 bool abort_on_unimplemented_;
322 // Prefixes parsed
323 byte rex_;
324 byte operand_size_; // 0x66 or (if no group 3 prefix is present) 0x0.
325 byte group_1_prefix_; // 0xF2, 0xF3, or (if no group 1 prefix is present) 0.
326 // Byte size operand override.
327 bool byte_size_operand_;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000328 const InstructionTable* const instruction_table_;
Steve Blocka7e24c12009-10-30 11:49:00 +0000329
330 void setRex(byte rex) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000331 DCHECK_EQ(0x40, rex & 0xF0);
Steve Blocka7e24c12009-10-30 11:49:00 +0000332 rex_ = rex;
333 }
334
335 bool rex() { return rex_ != 0; }
336
337 bool rex_b() { return (rex_ & 0x01) != 0; }
338
339 // Actual number of base register given the low bits and the rex.b state.
340 int base_reg(int low_bits) { return low_bits | ((rex_ & 0x01) << 3); }
341
342 bool rex_x() { return (rex_ & 0x02) != 0; }
343
344 bool rex_r() { return (rex_ & 0x04) != 0; }
345
346 bool rex_w() { return (rex_ & 0x08) != 0; }
347
348 OperandSize operand_size() {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000349 if (byte_size_operand_) return OPERAND_BYTE_SIZE;
350 if (rex_w()) return OPERAND_QUADWORD_SIZE;
351 if (operand_size_ != 0) return OPERAND_WORD_SIZE;
352 return OPERAND_DOUBLEWORD_SIZE;
Steve Blocka7e24c12009-10-30 11:49:00 +0000353 }
354
355 char operand_size_code() {
356 return "bwlq"[operand_size()];
357 }
358
359 const char* NameOfCPURegister(int reg) const {
360 return converter_.NameOfCPURegister(reg);
361 }
362
363 const char* NameOfByteCPURegister(int reg) const {
364 return converter_.NameOfByteCPURegister(reg);
365 }
366
367 const char* NameOfXMMRegister(int reg) const {
368 return converter_.NameOfXMMRegister(reg);
369 }
370
371 const char* NameOfAddress(byte* addr) const {
372 return converter_.NameOfAddress(addr);
373 }
374
375 // Disassembler helper functions.
376 void get_modrm(byte data,
377 int* mod,
378 int* regop,
379 int* rm) {
380 *mod = (data >> 6) & 3;
381 *regop = ((data & 0x38) >> 3) | (rex_r() ? 8 : 0);
382 *rm = (data & 7) | (rex_b() ? 8 : 0);
383 }
384
385 void get_sib(byte data,
386 int* scale,
387 int* index,
388 int* base) {
389 *scale = (data >> 6) & 3;
390 *index = ((data >> 3) & 7) | (rex_x() ? 8 : 0);
391 *base = (data & 7) | (rex_b() ? 8 : 0);
392 }
393
394 typedef const char* (DisassemblerX64::*RegisterNameMapping)(int reg) const;
395
396 int PrintRightOperandHelper(byte* modrmp,
397 RegisterNameMapping register_name);
398 int PrintRightOperand(byte* modrmp);
399 int PrintRightByteOperand(byte* modrmp);
Steve Blockd0582a62009-12-15 09:54:21 +0000400 int PrintRightXMMOperand(byte* modrmp);
Steve Blocka7e24c12009-10-30 11:49:00 +0000401 int PrintOperands(const char* mnem,
402 OperandType op_order,
403 byte* data);
404 int PrintImmediate(byte* data, OperandSize size);
405 int PrintImmediateOp(byte* data);
406 const char* TwoByteMnemonic(byte opcode);
407 int TwoByteOpcodeInstruction(byte* data);
Steve Blockd0582a62009-12-15 09:54:21 +0000408 int F6F7Instruction(byte* data);
Steve Blocka7e24c12009-10-30 11:49:00 +0000409 int ShiftInstruction(byte* data);
410 int JumpShort(byte* data);
411 int JumpConditional(byte* data);
412 int JumpConditionalShort(byte* data);
413 int SetCC(byte* data);
414 int FPUInstruction(byte* data);
Steve Blockd0582a62009-12-15 09:54:21 +0000415 int MemoryFPUInstruction(int escape_opcode, int regop, byte* modrm_start);
416 int RegisterFPUInstruction(int escape_opcode, byte modrm_byte);
Steve Blocka7e24c12009-10-30 11:49:00 +0000417 void AppendToBuffer(const char* format, ...);
418
419 void UnimplementedInstruction() {
420 if (abort_on_unimplemented_) {
421 CHECK(false);
422 } else {
423 AppendToBuffer("'Unimplemented Instruction'");
424 }
425 }
426};
427
428
429void DisassemblerX64::AppendToBuffer(const char* format, ...) {
430 v8::internal::Vector<char> buf = tmp_buffer_ + tmp_buffer_pos_;
431 va_list args;
432 va_start(args, format);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000433 int result = v8::internal::VSNPrintF(buf, format, args);
Steve Blocka7e24c12009-10-30 11:49:00 +0000434 va_end(args);
435 tmp_buffer_pos_ += result;
436}
437
438
439int DisassemblerX64::PrintRightOperandHelper(
440 byte* modrmp,
Steve Block44f0eee2011-05-26 01:26:41 +0100441 RegisterNameMapping direct_register_name) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000442 int mod, regop, rm;
443 get_modrm(*modrmp, &mod, &regop, &rm);
Steve Block44f0eee2011-05-26 01:26:41 +0100444 RegisterNameMapping register_name = (mod == 3) ? direct_register_name :
445 &DisassemblerX64::NameOfCPURegister;
Steve Blocka7e24c12009-10-30 11:49:00 +0000446 switch (mod) {
447 case 0:
448 if ((rm & 7) == 5) {
449 int32_t disp = *reinterpret_cast<int32_t*>(modrmp + 1);
450 AppendToBuffer("[0x%x]", disp);
451 return 5;
452 } else if ((rm & 7) == 4) {
453 // Codes for SIB byte.
454 byte sib = *(modrmp + 1);
455 int scale, index, base;
456 get_sib(sib, &scale, &index, &base);
457 if (index == 4 && (base & 7) == 4 && scale == 0 /*times_1*/) {
458 // index == rsp means no index. Only use sib byte with no index for
459 // rsp and r12 base.
Steve Block8defd9f2010-07-08 12:39:36 +0100460 AppendToBuffer("[%s]", NameOfCPURegister(base));
Steve Blocka7e24c12009-10-30 11:49:00 +0000461 return 2;
462 } else if (base == 5) {
463 // base == rbp means no base register (when mod == 0).
464 int32_t disp = *reinterpret_cast<int32_t*>(modrmp + 2);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000465 AppendToBuffer("[%s*%d%s0x%x]",
Steve Block8defd9f2010-07-08 12:39:36 +0100466 NameOfCPURegister(index),
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000467 1 << scale,
468 disp < 0 ? "-" : "+",
469 disp < 0 ? -disp : disp);
Steve Blocka7e24c12009-10-30 11:49:00 +0000470 return 6;
471 } else if (index != 4 && base != 5) {
472 // [base+index*scale]
473 AppendToBuffer("[%s+%s*%d]",
Steve Block8defd9f2010-07-08 12:39:36 +0100474 NameOfCPURegister(base),
475 NameOfCPURegister(index),
Steve Blocka7e24c12009-10-30 11:49:00 +0000476 1 << scale);
477 return 2;
478 } else {
479 UnimplementedInstruction();
480 return 1;
481 }
482 } else {
Steve Block8defd9f2010-07-08 12:39:36 +0100483 AppendToBuffer("[%s]", NameOfCPURegister(rm));
Steve Blocka7e24c12009-10-30 11:49:00 +0000484 return 1;
485 }
486 break;
487 case 1: // fall through
488 case 2:
489 if ((rm & 7) == 4) {
490 byte sib = *(modrmp + 1);
491 int scale, index, base;
492 get_sib(sib, &scale, &index, &base);
493 int disp = (mod == 2) ? *reinterpret_cast<int32_t*>(modrmp + 2)
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000494 : *reinterpret_cast<int8_t*>(modrmp + 2);
Steve Blocka7e24c12009-10-30 11:49:00 +0000495 if (index == 4 && (base & 7) == 4 && scale == 0 /*times_1*/) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000496 AppendToBuffer("[%s%s0x%x]",
497 NameOfCPURegister(base),
498 disp < 0 ? "-" : "+",
499 disp < 0 ? -disp : disp);
Steve Blocka7e24c12009-10-30 11:49:00 +0000500 } else {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000501 AppendToBuffer("[%s+%s*%d%s0x%x]",
502 NameOfCPURegister(base),
503 NameOfCPURegister(index),
504 1 << scale,
505 disp < 0 ? "-" : "+",
506 disp < 0 ? -disp : disp);
Steve Blocka7e24c12009-10-30 11:49:00 +0000507 }
508 return mod == 2 ? 6 : 3;
509 } else {
510 // No sib.
511 int disp = (mod == 2) ? *reinterpret_cast<int32_t*>(modrmp + 1)
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000512 : *reinterpret_cast<int8_t*>(modrmp + 1);
513 AppendToBuffer("[%s%s0x%x]",
514 NameOfCPURegister(rm),
515 disp < 0 ? "-" : "+",
516 disp < 0 ? -disp : disp);
Steve Blocka7e24c12009-10-30 11:49:00 +0000517 return (mod == 2) ? 5 : 2;
518 }
519 break;
520 case 3:
521 AppendToBuffer("%s", (this->*register_name)(rm));
522 return 1;
523 default:
524 UnimplementedInstruction();
525 return 1;
526 }
527 UNREACHABLE();
528}
529
530
531int DisassemblerX64::PrintImmediate(byte* data, OperandSize size) {
532 int64_t value;
533 int count;
534 switch (size) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000535 case OPERAND_BYTE_SIZE:
Steve Blocka7e24c12009-10-30 11:49:00 +0000536 value = *data;
537 count = 1;
538 break;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000539 case OPERAND_WORD_SIZE:
Steve Blocka7e24c12009-10-30 11:49:00 +0000540 value = *reinterpret_cast<int16_t*>(data);
541 count = 2;
542 break;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000543 case OPERAND_DOUBLEWORD_SIZE:
Steve Blocka7e24c12009-10-30 11:49:00 +0000544 value = *reinterpret_cast<uint32_t*>(data);
545 count = 4;
546 break;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000547 case OPERAND_QUADWORD_SIZE:
Steve Blocka7e24c12009-10-30 11:49:00 +0000548 value = *reinterpret_cast<int32_t*>(data);
549 count = 4;
550 break;
551 default:
552 UNREACHABLE();
553 value = 0; // Initialize variables on all paths to satisfy the compiler.
554 count = 0;
555 }
556 AppendToBuffer("%" V8_PTR_PREFIX "x", value);
557 return count;
558}
559
560
561int DisassemblerX64::PrintRightOperand(byte* modrmp) {
562 return PrintRightOperandHelper(modrmp,
563 &DisassemblerX64::NameOfCPURegister);
564}
565
566
567int DisassemblerX64::PrintRightByteOperand(byte* modrmp) {
568 return PrintRightOperandHelper(modrmp,
569 &DisassemblerX64::NameOfByteCPURegister);
570}
571
572
Steve Blockd0582a62009-12-15 09:54:21 +0000573int DisassemblerX64::PrintRightXMMOperand(byte* modrmp) {
574 return PrintRightOperandHelper(modrmp,
575 &DisassemblerX64::NameOfXMMRegister);
576}
577
578
Steve Blocka7e24c12009-10-30 11:49:00 +0000579// Returns number of bytes used including the current *data.
580// Writes instruction's mnemonic, left and right operands to 'tmp_buffer_'.
581int DisassemblerX64::PrintOperands(const char* mnem,
582 OperandType op_order,
583 byte* data) {
584 byte modrm = *data;
585 int mod, regop, rm;
586 get_modrm(modrm, &mod, &regop, &rm);
587 int advance = 0;
588 const char* register_name =
589 byte_size_operand_ ? NameOfByteCPURegister(regop)
590 : NameOfCPURegister(regop);
591 switch (op_order) {
592 case REG_OPER_OP_ORDER: {
593 AppendToBuffer("%s%c %s,",
594 mnem,
595 operand_size_code(),
596 register_name);
597 advance = byte_size_operand_ ? PrintRightByteOperand(data)
598 : PrintRightOperand(data);
599 break;
600 }
601 case OPER_REG_OP_ORDER: {
602 AppendToBuffer("%s%c ", mnem, operand_size_code());
603 advance = byte_size_operand_ ? PrintRightByteOperand(data)
604 : PrintRightOperand(data);
605 AppendToBuffer(",%s", register_name);
606 break;
607 }
608 default:
609 UNREACHABLE();
610 break;
611 }
612 return advance;
613}
614
615
616// Returns number of bytes used by machine instruction, including *data byte.
617// Writes immediate instructions to 'tmp_buffer_'.
618int DisassemblerX64::PrintImmediateOp(byte* data) {
619 bool byte_size_immediate = (*data & 0x02) != 0;
620 byte modrm = *(data + 1);
621 int mod, regop, rm;
622 get_modrm(modrm, &mod, &regop, &rm);
623 const char* mnem = "Imm???";
624 switch (regop) {
625 case 0:
626 mnem = "add";
627 break;
628 case 1:
629 mnem = "or";
630 break;
631 case 2:
632 mnem = "adc";
633 break;
Ben Murdoch8b112d22011-06-08 16:22:53 +0100634 case 3:
635 mnem = "sbb";
636 break;
Steve Blocka7e24c12009-10-30 11:49:00 +0000637 case 4:
638 mnem = "and";
639 break;
640 case 5:
641 mnem = "sub";
642 break;
643 case 6:
644 mnem = "xor";
645 break;
646 case 7:
647 mnem = "cmp";
648 break;
649 default:
650 UnimplementedInstruction();
651 }
652 AppendToBuffer("%s%c ", mnem, operand_size_code());
653 int count = PrintRightOperand(data + 1);
654 AppendToBuffer(",0x");
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000655 OperandSize immediate_size =
656 byte_size_immediate ? OPERAND_BYTE_SIZE : operand_size();
Steve Blocka7e24c12009-10-30 11:49:00 +0000657 count += PrintImmediate(data + 1 + count, immediate_size);
658 return 1 + count;
659}
660
661
662// Returns number of bytes used, including *data.
Steve Blockd0582a62009-12-15 09:54:21 +0000663int DisassemblerX64::F6F7Instruction(byte* data) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000664 DCHECK(*data == 0xF7 || *data == 0xF6);
Steve Blocka7e24c12009-10-30 11:49:00 +0000665 byte modrm = *(data + 1);
666 int mod, regop, rm;
667 get_modrm(modrm, &mod, &regop, &rm);
668 if (mod == 3 && regop != 0) {
669 const char* mnem = NULL;
670 switch (regop) {
671 case 2:
672 mnem = "not";
673 break;
674 case 3:
675 mnem = "neg";
676 break;
677 case 4:
678 mnem = "mul";
679 break;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000680 case 5:
681 mnem = "imul";
682 break;
683 case 6:
684 mnem = "div";
685 break;
Steve Blocka7e24c12009-10-30 11:49:00 +0000686 case 7:
687 mnem = "idiv";
688 break;
689 default:
690 UnimplementedInstruction();
691 }
692 AppendToBuffer("%s%c %s",
693 mnem,
694 operand_size_code(),
695 NameOfCPURegister(rm));
696 return 2;
Steve Blocka7e24c12009-10-30 11:49:00 +0000697 } else if (regop == 0) {
698 AppendToBuffer("test%c ", operand_size_code());
Steve Blockd0582a62009-12-15 09:54:21 +0000699 int count = PrintRightOperand(data + 1); // Use name of 64-bit register.
700 AppendToBuffer(",0x");
701 count += PrintImmediate(data + 1 + count, operand_size());
702 return 1 + count;
Steve Blocka7e24c12009-10-30 11:49:00 +0000703 } else {
704 UnimplementedInstruction();
705 return 2;
706 }
707}
708
709
710int DisassemblerX64::ShiftInstruction(byte* data) {
711 byte op = *data & (~1);
712 if (op != 0xD0 && op != 0xD2 && op != 0xC0) {
713 UnimplementedInstruction();
714 return 1;
715 }
716 byte modrm = *(data + 1);
717 int mod, regop, rm;
718 get_modrm(modrm, &mod, &regop, &rm);
719 regop &= 0x7; // The REX.R bit does not affect the operation.
720 int imm8 = -1;
721 int num_bytes = 2;
722 if (mod != 3) {
723 UnimplementedInstruction();
724 return num_bytes;
725 }
726 const char* mnem = NULL;
727 switch (regop) {
728 case 0:
729 mnem = "rol";
730 break;
731 case 1:
732 mnem = "ror";
733 break;
734 case 2:
735 mnem = "rcl";
736 break;
737 case 3:
738 mnem = "rcr";
739 break;
740 case 4:
741 mnem = "shl";
742 break;
743 case 5:
744 mnem = "shr";
745 break;
746 case 7:
747 mnem = "sar";
748 break;
749 default:
750 UnimplementedInstruction();
751 return num_bytes;
752 }
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000753 DCHECK_NE(NULL, mnem);
Steve Blocka7e24c12009-10-30 11:49:00 +0000754 if (op == 0xD0) {
755 imm8 = 1;
756 } else if (op == 0xC0) {
757 imm8 = *(data + 2);
758 num_bytes = 3;
759 }
760 AppendToBuffer("%s%c %s,",
761 mnem,
762 operand_size_code(),
763 byte_size_operand_ ? NameOfByteCPURegister(rm)
764 : NameOfCPURegister(rm));
765 if (op == 0xD2) {
766 AppendToBuffer("cl");
767 } else {
768 AppendToBuffer("%d", imm8);
769 }
770 return num_bytes;
771}
772
773
774// Returns number of bytes used, including *data.
775int DisassemblerX64::JumpShort(byte* data) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000776 DCHECK_EQ(0xEB, *data);
Steve Blocka7e24c12009-10-30 11:49:00 +0000777 byte b = *(data + 1);
778 byte* dest = data + static_cast<int8_t>(b) + 2;
779 AppendToBuffer("jmp %s", NameOfAddress(dest));
780 return 2;
781}
782
783
784// Returns number of bytes used, including *data.
785int DisassemblerX64::JumpConditional(byte* data) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000786 DCHECK_EQ(0x0F, *data);
Steve Blocka7e24c12009-10-30 11:49:00 +0000787 byte cond = *(data + 1) & 0x0F;
788 byte* dest = data + *reinterpret_cast<int32_t*>(data + 2) + 6;
789 const char* mnem = conditional_code_suffix[cond];
790 AppendToBuffer("j%s %s", mnem, NameOfAddress(dest));
791 return 6; // includes 0x0F
792}
793
794
795// Returns number of bytes used, including *data.
796int DisassemblerX64::JumpConditionalShort(byte* data) {
797 byte cond = *data & 0x0F;
798 byte b = *(data + 1);
799 byte* dest = data + static_cast<int8_t>(b) + 2;
800 const char* mnem = conditional_code_suffix[cond];
801 AppendToBuffer("j%s %s", mnem, NameOfAddress(dest));
802 return 2;
803}
804
805
806// Returns number of bytes used, including *data.
807int DisassemblerX64::SetCC(byte* data) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000808 DCHECK_EQ(0x0F, *data);
Steve Blocka7e24c12009-10-30 11:49:00 +0000809 byte cond = *(data + 1) & 0x0F;
810 const char* mnem = conditional_code_suffix[cond];
811 AppendToBuffer("set%s%c ", mnem, operand_size_code());
812 PrintRightByteOperand(data + 2);
813 return 3; // includes 0x0F
814}
815
816
817// Returns number of bytes used, including *data.
818int DisassemblerX64::FPUInstruction(byte* data) {
Steve Blockd0582a62009-12-15 09:54:21 +0000819 byte escape_opcode = *data;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000820 DCHECK_EQ(0xD8, escape_opcode & 0xF8);
Steve Blockd0582a62009-12-15 09:54:21 +0000821 byte modrm_byte = *(data+1);
822
823 if (modrm_byte >= 0xC0) {
824 return RegisterFPUInstruction(escape_opcode, modrm_byte);
825 } else {
826 return MemoryFPUInstruction(escape_opcode, modrm_byte, data+1);
Steve Blocka7e24c12009-10-30 11:49:00 +0000827 }
Steve Blockd0582a62009-12-15 09:54:21 +0000828}
829
830int DisassemblerX64::MemoryFPUInstruction(int escape_opcode,
831 int modrm_byte,
832 byte* modrm_start) {
833 const char* mnem = "?";
834 int regop = (modrm_byte >> 3) & 0x7; // reg/op field of modrm byte.
835 switch (escape_opcode) {
836 case 0xD9: switch (regop) {
837 case 0: mnem = "fld_s"; break;
838 case 3: mnem = "fstp_s"; break;
839 case 7: mnem = "fstcw"; break;
840 default: UnimplementedInstruction();
841 }
842 break;
843
844 case 0xDB: switch (regop) {
845 case 0: mnem = "fild_s"; break;
846 case 1: mnem = "fisttp_s"; break;
847 case 2: mnem = "fist_s"; break;
848 case 3: mnem = "fistp_s"; break;
849 default: UnimplementedInstruction();
850 }
851 break;
852
853 case 0xDD: switch (regop) {
854 case 0: mnem = "fld_d"; break;
855 case 3: mnem = "fstp_d"; break;
856 default: UnimplementedInstruction();
857 }
858 break;
859
860 case 0xDF: switch (regop) {
861 case 5: mnem = "fild_d"; break;
862 case 7: mnem = "fistp_d"; break;
863 default: UnimplementedInstruction();
864 }
865 break;
866
867 default: UnimplementedInstruction();
868 }
869 AppendToBuffer("%s ", mnem);
870 int count = PrintRightOperand(modrm_start);
871 return count + 1;
872}
873
874int DisassemblerX64::RegisterFPUInstruction(int escape_opcode,
875 byte modrm_byte) {
876 bool has_register = false; // Is the FPU register encoded in modrm_byte?
877 const char* mnem = "?";
878
879 switch (escape_opcode) {
880 case 0xD8:
881 UnimplementedInstruction();
882 break;
883
884 case 0xD9:
885 switch (modrm_byte & 0xF8) {
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100886 case 0xC0:
887 mnem = "fld";
888 has_register = true;
889 break;
Steve Blockd0582a62009-12-15 09:54:21 +0000890 case 0xC8:
891 mnem = "fxch";
892 has_register = true;
893 break;
894 default:
895 switch (modrm_byte) {
896 case 0xE0: mnem = "fchs"; break;
897 case 0xE1: mnem = "fabs"; break;
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100898 case 0xE3: mnem = "fninit"; break;
Steve Blockd0582a62009-12-15 09:54:21 +0000899 case 0xE4: mnem = "ftst"; break;
900 case 0xE8: mnem = "fld1"; break;
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100901 case 0xEB: mnem = "fldpi"; break;
Ben Murdochb0fe1622011-05-05 13:52:32 +0100902 case 0xED: mnem = "fldln2"; break;
Steve Blockd0582a62009-12-15 09:54:21 +0000903 case 0xEE: mnem = "fldz"; break;
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100904 case 0xF0: mnem = "f2xm1"; break;
Ben Murdochb0fe1622011-05-05 13:52:32 +0100905 case 0xF1: mnem = "fyl2x"; break;
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100906 case 0xF2: mnem = "fptan"; break;
Steve Blockd0582a62009-12-15 09:54:21 +0000907 case 0xF5: mnem = "fprem1"; break;
908 case 0xF7: mnem = "fincstp"; break;
909 case 0xF8: mnem = "fprem"; break;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000910 case 0xFC: mnem = "frndint"; break;
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100911 case 0xFD: mnem = "fscale"; break;
Steve Blockd0582a62009-12-15 09:54:21 +0000912 case 0xFE: mnem = "fsin"; break;
913 case 0xFF: mnem = "fcos"; break;
914 default: UnimplementedInstruction();
915 }
916 }
917 break;
918
919 case 0xDA:
920 if (modrm_byte == 0xE9) {
921 mnem = "fucompp";
922 } else {
923 UnimplementedInstruction();
924 }
925 break;
926
927 case 0xDB:
928 if ((modrm_byte & 0xF8) == 0xE8) {
929 mnem = "fucomi";
930 has_register = true;
931 } else if (modrm_byte == 0xE2) {
932 mnem = "fclex";
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000933 } else if (modrm_byte == 0xE3) {
934 mnem = "fninit";
Steve Blockd0582a62009-12-15 09:54:21 +0000935 } else {
936 UnimplementedInstruction();
937 }
938 break;
939
940 case 0xDC:
941 has_register = true;
942 switch (modrm_byte & 0xF8) {
943 case 0xC0: mnem = "fadd"; break;
944 case 0xE8: mnem = "fsub"; break;
945 case 0xC8: mnem = "fmul"; break;
946 case 0xF8: mnem = "fdiv"; break;
947 default: UnimplementedInstruction();
948 }
949 break;
950
951 case 0xDD:
952 has_register = true;
953 switch (modrm_byte & 0xF8) {
954 case 0xC0: mnem = "ffree"; break;
955 case 0xD8: mnem = "fstp"; break;
956 default: UnimplementedInstruction();
957 }
958 break;
959
960 case 0xDE:
961 if (modrm_byte == 0xD9) {
962 mnem = "fcompp";
963 } else {
964 has_register = true;
965 switch (modrm_byte & 0xF8) {
966 case 0xC0: mnem = "faddp"; break;
967 case 0xE8: mnem = "fsubp"; break;
968 case 0xC8: mnem = "fmulp"; break;
969 case 0xF8: mnem = "fdivp"; break;
970 default: UnimplementedInstruction();
971 }
972 }
973 break;
974
975 case 0xDF:
976 if (modrm_byte == 0xE0) {
977 mnem = "fnstsw_ax";
978 } else if ((modrm_byte & 0xF8) == 0xE8) {
979 mnem = "fucomip";
980 has_register = true;
981 }
982 break;
983
984 default: UnimplementedInstruction();
985 }
986
987 if (has_register) {
988 AppendToBuffer("%s st%d", mnem, modrm_byte & 0x7);
989 } else {
990 AppendToBuffer("%s", mnem);
991 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000992 return 2;
993}
994
995
Steve Blockd0582a62009-12-15 09:54:21 +0000996
Steve Blocka7e24c12009-10-30 11:49:00 +0000997// Handle all two-byte opcodes, which start with 0x0F.
998// These instructions may be affected by an 0x66, 0xF2, or 0xF3 prefix.
999// We do not use any three-byte opcodes, which start with 0x0F38 or 0x0F3A.
1000int DisassemblerX64::TwoByteOpcodeInstruction(byte* data) {
1001 byte opcode = *(data + 1);
1002 byte* current = data + 2;
1003 // At return, "current" points to the start of the next instruction.
1004 const char* mnemonic = TwoByteMnemonic(opcode);
Andrei Popescu402d9372010-02-26 13:31:12 +00001005 if (operand_size_ == 0x66) {
1006 // 0x66 0x0F prefix.
Steve Blocka7e24c12009-10-30 11:49:00 +00001007 int mod, regop, rm;
Steve Block6ded16b2010-05-10 14:33:55 +01001008 if (opcode == 0x3A) {
1009 byte third_byte = *current;
1010 current = data + 3;
1011 if (third_byte == 0x17) {
1012 get_modrm(*current, &mod, &regop, &rm);
1013 AppendToBuffer("extractps "); // reg/m32, xmm, imm8
1014 current += PrintRightOperand(current);
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001015 AppendToBuffer(",%s,%d", NameOfXMMRegister(regop), (*current) & 3);
Steve Block6ded16b2010-05-10 14:33:55 +01001016 current += 1;
Ben Murdoch257744e2011-11-30 15:57:28 +00001017 } else if (third_byte == 0x0b) {
1018 get_modrm(*current, &mod, &regop, &rm);
1019 // roundsd xmm, xmm/m64, imm8
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001020 AppendToBuffer("roundsd %s,", NameOfXMMRegister(regop));
1021 current += PrintRightXMMOperand(current);
1022 AppendToBuffer(",%d", (*current) & 3);
Ben Murdoch257744e2011-11-30 15:57:28 +00001023 current += 1;
Steve Block6ded16b2010-05-10 14:33:55 +01001024 } else {
1025 UnimplementedInstruction();
1026 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001027 } else {
Steve Block6ded16b2010-05-10 14:33:55 +01001028 get_modrm(*current, &mod, &regop, &rm);
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001029 if (opcode == 0x1f) {
1030 current++;
1031 if (rm == 4) { // SIB byte present.
1032 current++;
1033 }
1034 if (mod == 1) { // Byte displacement.
1035 current += 1;
1036 } else if (mod == 2) { // 32-bit displacement.
1037 current += 4;
1038 } // else no immediate displacement.
1039 AppendToBuffer("nop");
1040 } else if (opcode == 0x28) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001041 AppendToBuffer("movapd %s,", NameOfXMMRegister(regop));
Ben Murdoch257744e2011-11-30 15:57:28 +00001042 current += PrintRightXMMOperand(current);
1043 } else if (opcode == 0x29) {
1044 AppendToBuffer("movapd ");
1045 current += PrintRightXMMOperand(current);
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001046 AppendToBuffer(",%s", NameOfXMMRegister(regop));
Ben Murdoch257744e2011-11-30 15:57:28 +00001047 } else if (opcode == 0x6E) {
Steve Block6ded16b2010-05-10 14:33:55 +01001048 AppendToBuffer("mov%c %s,",
1049 rex_w() ? 'q' : 'd',
1050 NameOfXMMRegister(regop));
1051 current += PrintRightOperand(current);
Steve Block1e0659c2011-05-24 12:43:12 +01001052 } else if (opcode == 0x6F) {
1053 AppendToBuffer("movdqa %s,",
1054 NameOfXMMRegister(regop));
Steve Block44f0eee2011-05-26 01:26:41 +01001055 current += PrintRightXMMOperand(current);
Steve Block6ded16b2010-05-10 14:33:55 +01001056 } else if (opcode == 0x7E) {
Ben Murdochbb769b22010-08-11 14:56:33 +01001057 AppendToBuffer("mov%c ",
1058 rex_w() ? 'q' : 'd');
1059 current += PrintRightOperand(current);
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001060 AppendToBuffer(",%s", NameOfXMMRegister(regop));
Steve Block1e0659c2011-05-24 12:43:12 +01001061 } else if (opcode == 0x7F) {
1062 AppendToBuffer("movdqa ");
Steve Block44f0eee2011-05-26 01:26:41 +01001063 current += PrintRightXMMOperand(current);
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001064 AppendToBuffer(",%s", NameOfXMMRegister(regop));
Ben Murdoch257744e2011-11-30 15:57:28 +00001065 } else if (opcode == 0xD6) {
1066 AppendToBuffer("movq ");
1067 current += PrintRightXMMOperand(current);
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001068 AppendToBuffer(",%s", NameOfXMMRegister(regop));
Ben Murdoch3fb3ca82011-12-02 17:19:32 +00001069 } else if (opcode == 0x50) {
1070 AppendToBuffer("movmskpd %s,", NameOfCPURegister(regop));
1071 current += PrintRightXMMOperand(current);
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001072 } else if (opcode == 0x73) {
1073 current += 1;
1074 DCHECK(regop == 6);
1075 AppendToBuffer("psllq,%s,%d", NameOfXMMRegister(rm), *current & 0x7f);
1076 current += 1;
Steve Block6ded16b2010-05-10 14:33:55 +01001077 } else {
1078 const char* mnemonic = "?";
Ben Murdoch3fb3ca82011-12-02 17:19:32 +00001079 if (opcode == 0x54) {
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001080 mnemonic = "andpd";
1081 } else if (opcode == 0x56) {
1082 mnemonic = "orpd";
1083 } else if (opcode == 0x57) {
Steve Block6ded16b2010-05-10 14:33:55 +01001084 mnemonic = "xorpd";
1085 } else if (opcode == 0x2E) {
Steve Block6ded16b2010-05-10 14:33:55 +01001086 mnemonic = "ucomisd";
Steve Block8defd9f2010-07-08 12:39:36 +01001087 } else if (opcode == 0x2F) {
1088 mnemonic = "comisd";
Steve Block6ded16b2010-05-10 14:33:55 +01001089 } else {
1090 UnimplementedInstruction();
1091 }
1092 AppendToBuffer("%s %s,", mnemonic, NameOfXMMRegister(regop));
1093 current += PrintRightXMMOperand(current);
1094 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001095 }
1096 } else if (group_1_prefix_ == 0xF2) {
1097 // Beginning of instructions with prefix 0xF2.
1098
1099 if (opcode == 0x11 || opcode == 0x10) {
1100 // MOVSD: Move scalar double-precision fp to/from/between XMM registers.
1101 AppendToBuffer("movsd ");
1102 int mod, regop, rm;
1103 get_modrm(*current, &mod, &regop, &rm);
1104 if (opcode == 0x11) {
Steve Block44f0eee2011-05-26 01:26:41 +01001105 current += PrintRightXMMOperand(current);
Steve Blocka7e24c12009-10-30 11:49:00 +00001106 AppendToBuffer(",%s", NameOfXMMRegister(regop));
1107 } else {
1108 AppendToBuffer("%s,", NameOfXMMRegister(regop));
Steve Block44f0eee2011-05-26 01:26:41 +01001109 current += PrintRightXMMOperand(current);
Steve Blocka7e24c12009-10-30 11:49:00 +00001110 }
1111 } else if (opcode == 0x2A) {
1112 // CVTSI2SD: integer to XMM double conversion.
1113 int mod, regop, rm;
1114 get_modrm(*current, &mod, &regop, &rm);
Steve Block8defd9f2010-07-08 12:39:36 +01001115 AppendToBuffer("%sd %s,", mnemonic, NameOfXMMRegister(regop));
Steve Blockd0582a62009-12-15 09:54:21 +00001116 current += PrintRightOperand(current);
Kristian Monsen0d5e1162010-09-30 15:31:59 +01001117 } else if (opcode == 0x2C) {
1118 // CVTTSD2SI:
1119 // Convert with truncation scalar double-precision FP to integer.
1120 int mod, regop, rm;
1121 get_modrm(*current, &mod, &regop, &rm);
1122 AppendToBuffer("cvttsd2si%c %s,",
1123 operand_size_code(), NameOfCPURegister(regop));
1124 current += PrintRightXMMOperand(current);
1125 } else if (opcode == 0x2D) {
1126 // CVTSD2SI: Convert scalar double-precision FP to integer.
1127 int mod, regop, rm;
1128 get_modrm(*current, &mod, &regop, &rm);
1129 AppendToBuffer("cvtsd2si%c %s,",
1130 operand_size_code(), NameOfCPURegister(regop));
1131 current += PrintRightXMMOperand(current);
Steve Block6ded16b2010-05-10 14:33:55 +01001132 } else if ((opcode & 0xF8) == 0x58 || opcode == 0x51) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001133 // XMM arithmetic. Mnemonic was retrieved at the start of this function.
1134 int mod, regop, rm;
1135 get_modrm(*current, &mod, &regop, &rm);
Steve Blockd0582a62009-12-15 09:54:21 +00001136 AppendToBuffer("%s %s,", mnemonic, NameOfXMMRegister(regop));
1137 current += PrintRightXMMOperand(current);
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001138 } else if (opcode == 0xC2) {
1139 // Intel manual 2A, Table 3-18.
1140 int mod, regop, rm;
1141 get_modrm(*current, &mod, &regop, &rm);
1142 const char* const pseudo_op[] = {
1143 "cmpeqsd",
1144 "cmpltsd",
1145 "cmplesd",
1146 "cmpunordsd",
1147 "cmpneqsd",
1148 "cmpnltsd",
1149 "cmpnlesd",
1150 "cmpordsd"
1151 };
1152 AppendToBuffer("%s %s,%s",
1153 pseudo_op[current[1]],
1154 NameOfXMMRegister(regop),
1155 NameOfXMMRegister(rm));
1156 current += 2;
Steve Blocka7e24c12009-10-30 11:49:00 +00001157 } else {
1158 UnimplementedInstruction();
1159 }
Steve Block6ded16b2010-05-10 14:33:55 +01001160 } else if (group_1_prefix_ == 0xF3) {
1161 // Instructions with prefix 0xF3.
Steve Block8defd9f2010-07-08 12:39:36 +01001162 if (opcode == 0x11 || opcode == 0x10) {
1163 // MOVSS: Move scalar double-precision fp to/from/between XMM registers.
1164 AppendToBuffer("movss ");
1165 int mod, regop, rm;
1166 get_modrm(*current, &mod, &regop, &rm);
1167 if (opcode == 0x11) {
1168 current += PrintRightOperand(current);
1169 AppendToBuffer(",%s", NameOfXMMRegister(regop));
1170 } else {
1171 AppendToBuffer("%s,", NameOfXMMRegister(regop));
1172 current += PrintRightOperand(current);
1173 }
1174 } else if (opcode == 0x2A) {
1175 // CVTSI2SS: integer to XMM single conversion.
1176 int mod, regop, rm;
1177 get_modrm(*current, &mod, &regop, &rm);
1178 AppendToBuffer("%ss %s,", mnemonic, NameOfXMMRegister(regop));
1179 current += PrintRightOperand(current);
1180 } else if (opcode == 0x2C) {
Kristian Monsen0d5e1162010-09-30 15:31:59 +01001181 // CVTTSS2SI:
1182 // Convert with truncation scalar single-precision FP to dword integer.
Steve Block1e0659c2011-05-24 12:43:12 +01001183 int mod, regop, rm;
1184 get_modrm(*current, &mod, &regop, &rm);
1185 AppendToBuffer("cvttss2si%c %s,",
1186 operand_size_code(), NameOfCPURegister(regop));
1187 current += PrintRightXMMOperand(current);
Steve Block6ded16b2010-05-10 14:33:55 +01001188 } else if (opcode == 0x5A) {
Kristian Monsen0d5e1162010-09-30 15:31:59 +01001189 // CVTSS2SD:
1190 // Convert scalar single-precision FP to scalar double-precision FP.
Steve Block6ded16b2010-05-10 14:33:55 +01001191 int mod, regop, rm;
1192 get_modrm(*current, &mod, &regop, &rm);
1193 AppendToBuffer("cvtss2sd %s,", NameOfXMMRegister(regop));
1194 current += PrintRightXMMOperand(current);
Ben Murdoch257744e2011-11-30 15:57:28 +00001195 } else if (opcode == 0x7E) {
1196 int mod, regop, rm;
1197 get_modrm(*current, &mod, &regop, &rm);
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001198 AppendToBuffer("movq %s,", NameOfXMMRegister(regop));
Ben Murdoch257744e2011-11-30 15:57:28 +00001199 current += PrintRightXMMOperand(current);
Steve Block6ded16b2010-05-10 14:33:55 +01001200 } else {
1201 UnimplementedInstruction();
1202 }
Andrei Popescu402d9372010-02-26 13:31:12 +00001203 } else if (opcode == 0x1F) {
1204 // NOP
1205 int mod, regop, rm;
1206 get_modrm(*current, &mod, &regop, &rm);
1207 current++;
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001208 if (rm == 4) { // SIB byte present.
Andrei Popescu402d9372010-02-26 13:31:12 +00001209 current++;
1210 }
1211 if (mod == 1) { // Byte displacement.
1212 current += 1;
1213 } else if (mod == 2) { // 32-bit displacement.
1214 current += 4;
1215 } // else no immediate displacement.
1216 AppendToBuffer("nop");
Ben Murdoch257744e2011-11-30 15:57:28 +00001217
1218 } else if (opcode == 0x28) {
1219 // movaps xmm, xmm/m128
1220 int mod, regop, rm;
1221 get_modrm(*current, &mod, &regop, &rm);
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001222 AppendToBuffer("movaps %s,", NameOfXMMRegister(regop));
Ben Murdoch257744e2011-11-30 15:57:28 +00001223 current += PrintRightXMMOperand(current);
1224
1225 } else if (opcode == 0x29) {
1226 // movaps xmm/m128, xmm
1227 int mod, regop, rm;
1228 get_modrm(*current, &mod, &regop, &rm);
1229 AppendToBuffer("movaps ");
1230 current += PrintRightXMMOperand(current);
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001231 AppendToBuffer(",%s", NameOfXMMRegister(regop));
Ben Murdoch257744e2011-11-30 15:57:28 +00001232
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001233 } else if (opcode == 0xA2) {
1234 // CPUID
Andrei Popescu402d9372010-02-26 13:31:12 +00001235 AppendToBuffer("%s", mnemonic);
1236
1237 } else if ((opcode & 0xF0) == 0x40) {
1238 // CMOVcc: conditional move.
1239 int condition = opcode & 0x0F;
1240 const InstructionDesc& idesc = cmov_instructions[condition];
1241 byte_size_operand_ = idesc.byte_size_operation;
1242 current += PrintOperands(idesc.mnem, idesc.op_order_, current);
1243
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001244 } else if (opcode >= 0x53 && opcode <= 0x5F) {
1245 const char* const pseudo_op[] = {
1246 "rcpps",
1247 "andps",
1248 "andnps",
1249 "orps",
1250 "xorps",
1251 "addps",
1252 "mulps",
1253 "cvtps2pd",
1254 "cvtdq2ps",
1255 "subps",
1256 "minps",
1257 "divps",
1258 "maxps",
1259 };
Ben Murdoch257744e2011-11-30 15:57:28 +00001260 int mod, regop, rm;
1261 get_modrm(*current, &mod, &regop, &rm);
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001262 AppendToBuffer("%s %s,",
1263 pseudo_op[opcode - 0x53],
1264 NameOfXMMRegister(regop));
1265 current += PrintRightXMMOperand(current);
1266
1267 } else if (opcode == 0xC6) {
1268 // shufps xmm, xmm/m128, imm8
1269 int mod, regop, rm;
1270 get_modrm(*current, &mod, &regop, &rm);
1271 AppendToBuffer("shufps %s, ", NameOfXMMRegister(regop));
1272 current += PrintRightXMMOperand(current);
1273 AppendToBuffer(", %d", (*current) & 3);
1274 current += 1;
1275
1276 } else if (opcode == 0x50) {
1277 // movmskps reg, xmm
1278 int mod, regop, rm;
1279 get_modrm(*current, &mod, &regop, &rm);
1280 AppendToBuffer("movmskps %s,", NameOfCPURegister(regop));
Ben Murdoch257744e2011-11-30 15:57:28 +00001281 current += PrintRightXMMOperand(current);
1282
Andrei Popescu402d9372010-02-26 13:31:12 +00001283 } else if ((opcode & 0xF0) == 0x80) {
1284 // Jcc: Conditional jump (branch).
1285 current = data + JumpConditional(data);
1286
1287 } else if (opcode == 0xBE || opcode == 0xBF || opcode == 0xB6 ||
1288 opcode == 0xB7 || opcode == 0xAF) {
1289 // Size-extending moves, IMUL.
1290 current += PrintOperands(mnemonic, REG_OPER_OP_ORDER, current);
1291
1292 } else if ((opcode & 0xF0) == 0x90) {
1293 // SETcc: Set byte on condition. Needs pointer to beginning of instruction.
1294 current = data + SetCC(data);
1295
1296 } else if (opcode == 0xAB || opcode == 0xA5 || opcode == 0xAD) {
1297 // SHLD, SHRD (double-precision shift), BTS (bit set).
1298 AppendToBuffer("%s ", mnemonic);
1299 int mod, regop, rm;
1300 get_modrm(*current, &mod, &regop, &rm);
1301 current += PrintRightOperand(current);
1302 if (opcode == 0xAB) {
1303 AppendToBuffer(",%s", NameOfCPURegister(regop));
1304 } else {
1305 AppendToBuffer(",%s,cl", NameOfCPURegister(regop));
1306 }
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001307 } else if (opcode == 0xBD) {
1308 AppendToBuffer("%s%c ", mnemonic, operand_size_code());
1309 int mod, regop, rm;
1310 get_modrm(*current, &mod, &regop, &rm);
1311 AppendToBuffer("%s,", NameOfCPURegister(regop));
1312 current += PrintRightOperand(current);
Steve Blocka7e24c12009-10-30 11:49:00 +00001313 } else {
1314 UnimplementedInstruction();
1315 }
Steve Blockd0582a62009-12-15 09:54:21 +00001316 return static_cast<int>(current - data);
Steve Blocka7e24c12009-10-30 11:49:00 +00001317}
1318
1319
1320// Mnemonics for two-byte opcode instructions starting with 0x0F.
1321// The argument is the second byte of the two-byte opcode.
1322// Returns NULL if the instruction is not handled here.
1323const char* DisassemblerX64::TwoByteMnemonic(byte opcode) {
1324 switch (opcode) {
1325 case 0x1F:
1326 return "nop";
Steve Block8defd9f2010-07-08 12:39:36 +01001327 case 0x2A: // F2/F3 prefix.
1328 return "cvtsi2s";
Steve Block6ded16b2010-05-10 14:33:55 +01001329 case 0x51: // F2 prefix.
1330 return "sqrtsd";
Steve Blocka7e24c12009-10-30 11:49:00 +00001331 case 0x58: // F2 prefix.
1332 return "addsd";
1333 case 0x59: // F2 prefix.
1334 return "mulsd";
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001335 case 0x5A: // F2 prefix.
1336 return "cvtsd2ss";
Steve Blocka7e24c12009-10-30 11:49:00 +00001337 case 0x5C: // F2 prefix.
1338 return "subsd";
1339 case 0x5E: // F2 prefix.
1340 return "divsd";
1341 case 0xA2:
1342 return "cpuid";
1343 case 0xA5:
1344 return "shld";
1345 case 0xAB:
1346 return "bts";
1347 case 0xAD:
1348 return "shrd";
1349 case 0xAF:
1350 return "imul";
1351 case 0xB6:
1352 return "movzxb";
1353 case 0xB7:
1354 return "movzxw";
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001355 case 0xBD:
1356 return "bsr";
Steve Blocka7e24c12009-10-30 11:49:00 +00001357 case 0xBE:
1358 return "movsxb";
1359 case 0xBF:
1360 return "movsxw";
1361 default:
1362 return NULL;
1363 }
1364}
1365
1366
1367// Disassembles the instruction at instr, and writes it into out_buffer.
1368int DisassemblerX64::InstructionDecode(v8::internal::Vector<char> out_buffer,
1369 byte* instr) {
1370 tmp_buffer_pos_ = 0; // starting to write as position 0
1371 byte* data = instr;
1372 bool processed = true; // Will be set to false if the current instruction
1373 // is not in 'instructions' table.
1374 byte current;
1375
1376 // Scan for prefixes.
1377 while (true) {
1378 current = *data;
Leon Clarked91b9f72010-01-27 17:25:45 +00001379 if (current == OPERAND_SIZE_OVERRIDE_PREFIX) { // Group 3 prefix.
Steve Blocka7e24c12009-10-30 11:49:00 +00001380 operand_size_ = current;
1381 } else if ((current & 0xF0) == 0x40) { // REX prefix.
1382 setRex(current);
1383 if (rex_w()) AppendToBuffer("REX.W ");
Leon Clarked91b9f72010-01-27 17:25:45 +00001384 } else if ((current & 0xFE) == 0xF2) { // Group 1 prefix (0xF2 or 0xF3).
Steve Blocka7e24c12009-10-30 11:49:00 +00001385 group_1_prefix_ = current;
1386 } else { // Not a prefix - an opcode.
1387 break;
1388 }
1389 data++;
1390 }
1391
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001392 const InstructionDesc& idesc = instruction_table_->Get(current);
Steve Blocka7e24c12009-10-30 11:49:00 +00001393 byte_size_operand_ = idesc.byte_size_operation;
1394 switch (idesc.type) {
1395 case ZERO_OPERANDS_INSTR:
Leon Clarked91b9f72010-01-27 17:25:45 +00001396 if (current >= 0xA4 && current <= 0xA7) {
1397 // String move or compare operations.
1398 if (group_1_prefix_ == REP_PREFIX) {
1399 // REP.
1400 AppendToBuffer("rep ");
1401 }
1402 if (rex_w()) AppendToBuffer("REX.W ");
1403 AppendToBuffer("%s%c", idesc.mnem, operand_size_code());
1404 } else {
1405 AppendToBuffer("%s", idesc.mnem, operand_size_code());
1406 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001407 data++;
1408 break;
1409
1410 case TWO_OPERANDS_INSTR:
1411 data++;
1412 data += PrintOperands(idesc.mnem, idesc.op_order_, data);
1413 break;
1414
1415 case JUMP_CONDITIONAL_SHORT_INSTR:
1416 data += JumpConditionalShort(data);
1417 break;
1418
1419 case REGISTER_INSTR:
1420 AppendToBuffer("%s%c %s",
1421 idesc.mnem,
1422 operand_size_code(),
1423 NameOfCPURegister(base_reg(current & 0x07)));
1424 data++;
1425 break;
1426 case PUSHPOP_INSTR:
1427 AppendToBuffer("%s %s",
1428 idesc.mnem,
1429 NameOfCPURegister(base_reg(current & 0x07)));
1430 data++;
1431 break;
1432 case MOVE_REG_INSTR: {
1433 byte* addr = NULL;
1434 switch (operand_size()) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001435 case OPERAND_WORD_SIZE:
Steve Blocka7e24c12009-10-30 11:49:00 +00001436 addr = reinterpret_cast<byte*>(*reinterpret_cast<int16_t*>(data + 1));
1437 data += 3;
1438 break;
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001439 case OPERAND_DOUBLEWORD_SIZE:
1440 addr =
1441 reinterpret_cast<byte*>(*reinterpret_cast<uint32_t*>(data + 1));
Steve Blocka7e24c12009-10-30 11:49:00 +00001442 data += 5;
1443 break;
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001444 case OPERAND_QUADWORD_SIZE:
Steve Blocka7e24c12009-10-30 11:49:00 +00001445 addr = reinterpret_cast<byte*>(*reinterpret_cast<int64_t*>(data + 1));
1446 data += 9;
1447 break;
1448 default:
1449 UNREACHABLE();
1450 }
1451 AppendToBuffer("mov%c %s,%s",
1452 operand_size_code(),
1453 NameOfCPURegister(base_reg(current & 0x07)),
1454 NameOfAddress(addr));
1455 break;
1456 }
1457
1458 case CALL_JUMP_INSTR: {
1459 byte* addr = data + *reinterpret_cast<int32_t*>(data + 1) + 5;
1460 AppendToBuffer("%s %s", idesc.mnem, NameOfAddress(addr));
1461 data += 5;
1462 break;
1463 }
1464
1465 case SHORT_IMMEDIATE_INSTR: {
1466 byte* addr =
1467 reinterpret_cast<byte*>(*reinterpret_cast<int32_t*>(data + 1));
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001468 AppendToBuffer("%s rax,%s", idesc.mnem, NameOfAddress(addr));
Steve Blocka7e24c12009-10-30 11:49:00 +00001469 data += 5;
1470 break;
1471 }
1472
1473 case NO_INSTR:
1474 processed = false;
1475 break;
1476
1477 default:
1478 UNIMPLEMENTED(); // This type is not implemented.
1479 }
1480
1481 // The first byte didn't match any of the simple opcodes, so we
1482 // need to do special processing on it.
1483 if (!processed) {
1484 switch (*data) {
1485 case 0xC2:
1486 AppendToBuffer("ret 0x%x", *reinterpret_cast<uint16_t*>(data + 1));
1487 data += 3;
1488 break;
1489
1490 case 0x69: // fall through
1491 case 0x6B: {
1492 int mod, regop, rm;
1493 get_modrm(*(data + 1), &mod, &regop, &rm);
1494 int32_t imm = *data == 0x6B ? *(data + 2)
1495 : *reinterpret_cast<int32_t*>(data + 2);
Steve Block6ded16b2010-05-10 14:33:55 +01001496 AppendToBuffer("imul%c %s,%s,0x%x",
1497 operand_size_code(),
1498 NameOfCPURegister(regop),
Steve Blocka7e24c12009-10-30 11:49:00 +00001499 NameOfCPURegister(rm), imm);
1500 data += 2 + (*data == 0x6B ? 1 : 4);
1501 break;
1502 }
1503
Steve Blocka7e24c12009-10-30 11:49:00 +00001504 case 0x81: // fall through
1505 case 0x83: // 0x81 with sign extension bit set
1506 data += PrintImmediateOp(data);
1507 break;
1508
1509 case 0x0F:
1510 data += TwoByteOpcodeInstruction(data);
1511 break;
1512
1513 case 0x8F: {
1514 data++;
1515 int mod, regop, rm;
1516 get_modrm(*data, &mod, &regop, &rm);
1517 if (regop == 0) {
1518 AppendToBuffer("pop ");
1519 data += PrintRightOperand(data);
1520 }
1521 }
1522 break;
1523
1524 case 0xFF: {
1525 data++;
1526 int mod, regop, rm;
1527 get_modrm(*data, &mod, &regop, &rm);
1528 const char* mnem = NULL;
1529 switch (regop) {
1530 case 0:
1531 mnem = "inc";
1532 break;
1533 case 1:
1534 mnem = "dec";
1535 break;
1536 case 2:
1537 mnem = "call";
1538 break;
1539 case 4:
1540 mnem = "jmp";
1541 break;
1542 case 6:
1543 mnem = "push";
1544 break;
1545 default:
1546 mnem = "???";
1547 }
1548 AppendToBuffer(((regop <= 1) ? "%s%c " : "%s "),
1549 mnem,
1550 operand_size_code());
1551 data += PrintRightOperand(data);
1552 }
1553 break;
1554
1555 case 0xC7: // imm32, fall through
1556 case 0xC6: // imm8
1557 {
1558 bool is_byte = *data == 0xC6;
1559 data++;
Steve Block44f0eee2011-05-26 01:26:41 +01001560 if (is_byte) {
1561 AppendToBuffer("movb ");
1562 data += PrintRightByteOperand(data);
1563 int32_t imm = *data;
1564 AppendToBuffer(",0x%x", imm);
1565 data++;
1566 } else {
1567 AppendToBuffer("mov%c ", operand_size_code());
1568 data += PrintRightOperand(data);
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001569 if (operand_size() == OPERAND_WORD_SIZE) {
1570 int16_t imm = *reinterpret_cast<int16_t*>(data);
1571 AppendToBuffer(",0x%x", imm);
1572 data += 2;
1573 } else {
1574 int32_t imm = *reinterpret_cast<int32_t*>(data);
1575 AppendToBuffer(",0x%x", imm);
1576 data += 4;
1577 }
Steve Block44f0eee2011-05-26 01:26:41 +01001578 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001579 }
1580 break;
1581
1582 case 0x80: {
1583 data++;
1584 AppendToBuffer("cmpb ");
Steve Block44f0eee2011-05-26 01:26:41 +01001585 data += PrintRightByteOperand(data);
Steve Blocka7e24c12009-10-30 11:49:00 +00001586 int32_t imm = *data;
1587 AppendToBuffer(",0x%x", imm);
1588 data++;
1589 }
1590 break;
1591
1592 case 0x88: // 8bit, fall through
1593 case 0x89: // 32bit
1594 {
1595 bool is_byte = *data == 0x88;
1596 int mod, regop, rm;
1597 data++;
1598 get_modrm(*data, &mod, &regop, &rm);
Steve Block44f0eee2011-05-26 01:26:41 +01001599 if (is_byte) {
1600 AppendToBuffer("movb ");
1601 data += PrintRightByteOperand(data);
1602 AppendToBuffer(",%s", NameOfByteCPURegister(regop));
1603 } else {
1604 AppendToBuffer("mov%c ", operand_size_code());
1605 data += PrintRightOperand(data);
1606 AppendToBuffer(",%s", NameOfCPURegister(regop));
1607 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001608 }
1609 break;
1610
1611 case 0x90:
1612 case 0x91:
1613 case 0x92:
1614 case 0x93:
1615 case 0x94:
1616 case 0x95:
1617 case 0x96:
1618 case 0x97: {
Steve Blockd0582a62009-12-15 09:54:21 +00001619 int reg = (*data & 0x7) | (rex_b() ? 8 : 0);
Steve Blocka7e24c12009-10-30 11:49:00 +00001620 if (reg == 0) {
1621 AppendToBuffer("nop"); // Common name for xchg rax,rax.
1622 } else {
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001623 AppendToBuffer("xchg%c rax,%s",
Steve Blocka7e24c12009-10-30 11:49:00 +00001624 operand_size_code(),
1625 NameOfCPURegister(reg));
1626 }
Steve Blockd0582a62009-12-15 09:54:21 +00001627 data++;
Steve Blocka7e24c12009-10-30 11:49:00 +00001628 }
Steve Blockd0582a62009-12-15 09:54:21 +00001629 break;
Ben Murdoch8b112d22011-06-08 16:22:53 +01001630 case 0xB0:
1631 case 0xB1:
1632 case 0xB2:
1633 case 0xB3:
1634 case 0xB4:
1635 case 0xB5:
1636 case 0xB6:
1637 case 0xB7:
1638 case 0xB8:
1639 case 0xB9:
1640 case 0xBA:
1641 case 0xBB:
1642 case 0xBC:
1643 case 0xBD:
1644 case 0xBE:
1645 case 0xBF: {
1646 // mov reg8,imm8 or mov reg32,imm32
1647 byte opcode = *data;
1648 data++;
1649 bool is_32bit = (opcode >= 0xB8);
1650 int reg = (opcode & 0x7) | (rex_b() ? 8 : 0);
1651 if (is_32bit) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001652 AppendToBuffer("mov%c %s,",
Ben Murdoch8b112d22011-06-08 16:22:53 +01001653 operand_size_code(),
1654 NameOfCPURegister(reg));
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001655 data += PrintImmediate(data, OPERAND_DOUBLEWORD_SIZE);
Ben Murdoch8b112d22011-06-08 16:22:53 +01001656 } else {
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001657 AppendToBuffer("movb %s,",
Ben Murdoch8b112d22011-06-08 16:22:53 +01001658 NameOfByteCPURegister(reg));
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001659 data += PrintImmediate(data, OPERAND_BYTE_SIZE);
Ben Murdoch8b112d22011-06-08 16:22:53 +01001660 }
1661 break;
1662 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001663 case 0xFE: {
1664 data++;
1665 int mod, regop, rm;
1666 get_modrm(*data, &mod, &regop, &rm);
Kristian Monsen0d5e1162010-09-30 15:31:59 +01001667 if (regop == 1) {
1668 AppendToBuffer("decb ");
Steve Block44f0eee2011-05-26 01:26:41 +01001669 data += PrintRightByteOperand(data);
Steve Blocka7e24c12009-10-30 11:49:00 +00001670 } else {
1671 UnimplementedInstruction();
1672 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001673 break;
Ben Murdoch8b112d22011-06-08 16:22:53 +01001674 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001675 case 0x68:
1676 AppendToBuffer("push 0x%x", *reinterpret_cast<int32_t*>(data + 1));
1677 data += 5;
1678 break;
1679
1680 case 0x6A:
1681 AppendToBuffer("push 0x%x", *reinterpret_cast<int8_t*>(data + 1));
1682 data += 2;
1683 break;
1684
1685 case 0xA1: // Fall through.
1686 case 0xA3:
1687 switch (operand_size()) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001688 case OPERAND_DOUBLEWORD_SIZE: {
Steve Blocka7e24c12009-10-30 11:49:00 +00001689 const char* memory_location = NameOfAddress(
1690 reinterpret_cast<byte*>(
1691 *reinterpret_cast<int32_t*>(data + 1)));
1692 if (*data == 0xA1) { // Opcode 0xA1
1693 AppendToBuffer("movzxlq rax,(%s)", memory_location);
1694 } else { // Opcode 0xA3
1695 AppendToBuffer("movzxlq (%s),rax", memory_location);
1696 }
1697 data += 5;
1698 break;
1699 }
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001700 case OPERAND_QUADWORD_SIZE: {
Steve Blocka7e24c12009-10-30 11:49:00 +00001701 // New x64 instruction mov rax,(imm_64).
1702 const char* memory_location = NameOfAddress(
1703 *reinterpret_cast<byte**>(data + 1));
1704 if (*data == 0xA1) { // Opcode 0xA1
1705 AppendToBuffer("movq rax,(%s)", memory_location);
1706 } else { // Opcode 0xA3
1707 AppendToBuffer("movq (%s),rax", memory_location);
1708 }
1709 data += 9;
1710 break;
1711 }
1712 default:
1713 UnimplementedInstruction();
1714 data += 2;
1715 }
1716 break;
1717
1718 case 0xA8:
1719 AppendToBuffer("test al,0x%x", *reinterpret_cast<uint8_t*>(data + 1));
1720 data += 2;
1721 break;
1722
1723 case 0xA9: {
1724 int64_t value = 0;
1725 switch (operand_size()) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001726 case OPERAND_WORD_SIZE:
Steve Blocka7e24c12009-10-30 11:49:00 +00001727 value = *reinterpret_cast<uint16_t*>(data + 1);
1728 data += 3;
1729 break;
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001730 case OPERAND_DOUBLEWORD_SIZE:
Steve Blocka7e24c12009-10-30 11:49:00 +00001731 value = *reinterpret_cast<uint32_t*>(data + 1);
1732 data += 5;
1733 break;
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001734 case OPERAND_QUADWORD_SIZE:
Steve Blocka7e24c12009-10-30 11:49:00 +00001735 value = *reinterpret_cast<int32_t*>(data + 1);
1736 data += 5;
1737 break;
1738 default:
1739 UNREACHABLE();
1740 }
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001741 AppendToBuffer("test%c rax,0x%" V8_PTR_PREFIX "x",
Steve Blocka7e24c12009-10-30 11:49:00 +00001742 operand_size_code(),
1743 value);
1744 break;
1745 }
1746 case 0xD1: // fall through
1747 case 0xD3: // fall through
1748 case 0xC1:
1749 data += ShiftInstruction(data);
1750 break;
1751 case 0xD0: // fall through
1752 case 0xD2: // fall through
1753 case 0xC0:
1754 byte_size_operand_ = true;
1755 data += ShiftInstruction(data);
1756 break;
1757
1758 case 0xD9: // fall through
1759 case 0xDA: // fall through
1760 case 0xDB: // fall through
1761 case 0xDC: // fall through
1762 case 0xDD: // fall through
1763 case 0xDE: // fall through
1764 case 0xDF:
1765 data += FPUInstruction(data);
1766 break;
1767
1768 case 0xEB:
1769 data += JumpShort(data);
1770 break;
1771
Steve Blockd0582a62009-12-15 09:54:21 +00001772 case 0xF6:
1773 byte_size_operand_ = true; // fall through
Steve Blocka7e24c12009-10-30 11:49:00 +00001774 case 0xF7:
Steve Blockd0582a62009-12-15 09:54:21 +00001775 data += F6F7Instruction(data);
Steve Blocka7e24c12009-10-30 11:49:00 +00001776 break;
1777
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001778 case 0x3C:
1779 AppendToBuffer("cmp al,0x%x", *reinterpret_cast<int8_t*>(data + 1));
1780 data +=2;
1781 break;
1782
Steve Blocka7e24c12009-10-30 11:49:00 +00001783 default:
1784 UnimplementedInstruction();
1785 data += 1;
1786 }
1787 } // !processed
1788
1789 if (tmp_buffer_pos_ < sizeof tmp_buffer_) {
1790 tmp_buffer_[tmp_buffer_pos_] = '\0';
1791 }
1792
Steve Blockd0582a62009-12-15 09:54:21 +00001793 int instr_len = static_cast<int>(data - instr);
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001794 DCHECK(instr_len > 0); // Ensure progress.
Steve Blocka7e24c12009-10-30 11:49:00 +00001795
1796 int outp = 0;
1797 // Instruction bytes.
1798 for (byte* bp = instr; bp < data; bp++) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001799 outp += v8::internal::SNPrintF(out_buffer + outp, "%02x", *bp);
Steve Blocka7e24c12009-10-30 11:49:00 +00001800 }
1801 for (int i = 6 - instr_len; i >= 0; i--) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001802 outp += v8::internal::SNPrintF(out_buffer + outp, " ");
Steve Blocka7e24c12009-10-30 11:49:00 +00001803 }
1804
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001805 outp += v8::internal::SNPrintF(out_buffer + outp, " %s",
1806 tmp_buffer_.start());
Steve Blocka7e24c12009-10-30 11:49:00 +00001807 return instr_len;
1808}
1809
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001810
Steve Blocka7e24c12009-10-30 11:49:00 +00001811//------------------------------------------------------------------------------
1812
1813
1814static const char* cpu_regs[16] = {
1815 "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi",
1816 "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"
1817};
1818
1819
1820static const char* byte_cpu_regs[16] = {
1821 "al", "cl", "dl", "bl", "spl", "bpl", "sil", "dil",
1822 "r8l", "r9l", "r10l", "r11l", "r12l", "r13l", "r14l", "r15l"
1823};
1824
1825
1826static const char* xmm_regs[16] = {
1827 "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7",
1828 "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15"
1829};
1830
1831
1832const char* NameConverter::NameOfAddress(byte* addr) const {
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001833 v8::internal::SNPrintF(tmp_buffer_, "%p", addr);
Steve Block44f0eee2011-05-26 01:26:41 +01001834 return tmp_buffer_.start();
Steve Blocka7e24c12009-10-30 11:49:00 +00001835}
1836
1837
1838const char* NameConverter::NameOfConstant(byte* addr) const {
1839 return NameOfAddress(addr);
1840}
1841
1842
1843const char* NameConverter::NameOfCPURegister(int reg) const {
1844 if (0 <= reg && reg < 16)
1845 return cpu_regs[reg];
1846 return "noreg";
1847}
1848
1849
1850const char* NameConverter::NameOfByteCPURegister(int reg) const {
1851 if (0 <= reg && reg < 16)
1852 return byte_cpu_regs[reg];
1853 return "noreg";
1854}
1855
1856
1857const char* NameConverter::NameOfXMMRegister(int reg) const {
1858 if (0 <= reg && reg < 16)
1859 return xmm_regs[reg];
1860 return "noxmmreg";
1861}
1862
1863
1864const char* NameConverter::NameInCode(byte* addr) const {
1865 // X64 does not embed debug strings at the moment.
1866 UNREACHABLE();
1867 return "";
1868}
1869
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001870
Steve Blocka7e24c12009-10-30 11:49:00 +00001871//------------------------------------------------------------------------------
1872
1873Disassembler::Disassembler(const NameConverter& converter)
1874 : converter_(converter) { }
1875
1876Disassembler::~Disassembler() { }
1877
1878
1879int Disassembler::InstructionDecode(v8::internal::Vector<char> buffer,
1880 byte* instruction) {
1881 DisassemblerX64 d(converter_, CONTINUE_ON_UNIMPLEMENTED_OPCODE);
1882 return d.InstructionDecode(buffer, instruction);
1883}
1884
1885
1886// The X64 assembler does not use constant pools.
1887int Disassembler::ConstantPoolSizeAt(byte* instruction) {
1888 return -1;
1889}
1890
1891
1892void Disassembler::Disassemble(FILE* f, byte* begin, byte* end) {
1893 NameConverter converter;
1894 Disassembler d(converter);
1895 for (byte* pc = begin; pc < end;) {
1896 v8::internal::EmbeddedVector<char, 128> buffer;
1897 buffer[0] = '\0';
1898 byte* prev_pc = pc;
1899 pc += d.InstructionDecode(buffer, pc);
1900 fprintf(f, "%p", prev_pc);
1901 fprintf(f, " ");
1902
1903 for (byte* bp = prev_pc; bp < pc; bp++) {
1904 fprintf(f, "%02x", *bp);
1905 }
Steve Blockd0582a62009-12-15 09:54:21 +00001906 for (int i = 6 - static_cast<int>(pc - prev_pc); i >= 0; i--) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001907 fprintf(f, " ");
1908 }
1909 fprintf(f, " %s\n", buffer.start());
1910 }
1911}
1912
1913} // namespace disasm
Leon Clarkef7060e22010-06-03 12:02:55 +01001914
1915#endif // V8_TARGET_ARCH_X64