blob: adeda0bb08b293378791710509874694bdf3633f [file] [log] [blame]
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001// Copyright 2011 the V8 project authors. All rights reserved.
Steve Blocka7e24c12009-10-30 11:49:00 +00002// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28#include <assert.h>
29#include <stdio.h>
30#include <stdarg.h>
31
32#include "v8.h"
Leon Clarkef7060e22010-06-03 12:02:55 +010033
34#if defined(V8_TARGET_ARCH_X64)
35
Steve Blocka7e24c12009-10-30 11:49:00 +000036#include "disasm.h"
Ben Murdoch3ef787d2012-04-12 10:51:47 +010037#include "lazy-instance.h"
Steve Blocka7e24c12009-10-30 11:49:00 +000038
39namespace disasm {
40
41enum OperandType {
42 UNSET_OP_ORDER = 0,
43 // Operand size decides between 16, 32 and 64 bit operands.
44 REG_OPER_OP_ORDER = 1, // Register destination, operand source.
45 OPER_REG_OP_ORDER = 2, // Operand destination, register source.
46 // Fixed 8-bit operands.
47 BYTE_SIZE_OPERAND_FLAG = 4,
48 BYTE_REG_OPER_OP_ORDER = REG_OPER_OP_ORDER | BYTE_SIZE_OPERAND_FLAG,
49 BYTE_OPER_REG_OP_ORDER = OPER_REG_OP_ORDER | BYTE_SIZE_OPERAND_FLAG
50};
51
52//------------------------------------------------------------------
53// Tables
54//------------------------------------------------------------------
55struct ByteMnemonic {
56 int b; // -1 terminates, otherwise must be in range (0..255)
57 OperandType op_order_;
58 const char* mnem;
59};
60
61
Ben Murdoch69a99ed2011-11-30 16:03:39 +000062static const ByteMnemonic two_operands_instr[] = {
Steve Blocka7e24c12009-10-30 11:49:00 +000063 { 0x00, BYTE_OPER_REG_OP_ORDER, "add" },
64 { 0x01, OPER_REG_OP_ORDER, "add" },
65 { 0x02, BYTE_REG_OPER_OP_ORDER, "add" },
66 { 0x03, REG_OPER_OP_ORDER, "add" },
67 { 0x08, BYTE_OPER_REG_OP_ORDER, "or" },
68 { 0x09, OPER_REG_OP_ORDER, "or" },
69 { 0x0A, BYTE_REG_OPER_OP_ORDER, "or" },
70 { 0x0B, REG_OPER_OP_ORDER, "or" },
71 { 0x10, BYTE_OPER_REG_OP_ORDER, "adc" },
72 { 0x11, OPER_REG_OP_ORDER, "adc" },
73 { 0x12, BYTE_REG_OPER_OP_ORDER, "adc" },
74 { 0x13, REG_OPER_OP_ORDER, "adc" },
75 { 0x18, BYTE_OPER_REG_OP_ORDER, "sbb" },
76 { 0x19, OPER_REG_OP_ORDER, "sbb" },
77 { 0x1A, BYTE_REG_OPER_OP_ORDER, "sbb" },
78 { 0x1B, REG_OPER_OP_ORDER, "sbb" },
79 { 0x20, BYTE_OPER_REG_OP_ORDER, "and" },
80 { 0x21, OPER_REG_OP_ORDER, "and" },
81 { 0x22, BYTE_REG_OPER_OP_ORDER, "and" },
82 { 0x23, REG_OPER_OP_ORDER, "and" },
83 { 0x28, BYTE_OPER_REG_OP_ORDER, "sub" },
84 { 0x29, OPER_REG_OP_ORDER, "sub" },
85 { 0x2A, BYTE_REG_OPER_OP_ORDER, "sub" },
86 { 0x2B, REG_OPER_OP_ORDER, "sub" },
87 { 0x30, BYTE_OPER_REG_OP_ORDER, "xor" },
88 { 0x31, OPER_REG_OP_ORDER, "xor" },
89 { 0x32, BYTE_REG_OPER_OP_ORDER, "xor" },
90 { 0x33, REG_OPER_OP_ORDER, "xor" },
91 { 0x38, BYTE_OPER_REG_OP_ORDER, "cmp" },
92 { 0x39, OPER_REG_OP_ORDER, "cmp" },
93 { 0x3A, BYTE_REG_OPER_OP_ORDER, "cmp" },
94 { 0x3B, REG_OPER_OP_ORDER, "cmp" },
95 { 0x63, REG_OPER_OP_ORDER, "movsxlq" },
96 { 0x84, BYTE_REG_OPER_OP_ORDER, "test" },
97 { 0x85, REG_OPER_OP_ORDER, "test" },
98 { 0x86, BYTE_REG_OPER_OP_ORDER, "xchg" },
99 { 0x87, REG_OPER_OP_ORDER, "xchg" },
100 { 0x88, BYTE_OPER_REG_OP_ORDER, "mov" },
101 { 0x89, OPER_REG_OP_ORDER, "mov" },
102 { 0x8A, BYTE_REG_OPER_OP_ORDER, "mov" },
103 { 0x8B, REG_OPER_OP_ORDER, "mov" },
104 { 0x8D, REG_OPER_OP_ORDER, "lea" },
105 { -1, UNSET_OP_ORDER, "" }
106};
107
108
Ben Murdoch69a99ed2011-11-30 16:03:39 +0000109static const ByteMnemonic zero_operands_instr[] = {
Steve Blocka7e24c12009-10-30 11:49:00 +0000110 { 0xC3, UNSET_OP_ORDER, "ret" },
111 { 0xC9, UNSET_OP_ORDER, "leave" },
112 { 0xF4, UNSET_OP_ORDER, "hlt" },
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100113 { 0xFC, UNSET_OP_ORDER, "cld" },
Steve Blocka7e24c12009-10-30 11:49:00 +0000114 { 0xCC, UNSET_OP_ORDER, "int3" },
115 { 0x60, UNSET_OP_ORDER, "pushad" },
116 { 0x61, UNSET_OP_ORDER, "popad" },
117 { 0x9C, UNSET_OP_ORDER, "pushfd" },
118 { 0x9D, UNSET_OP_ORDER, "popfd" },
119 { 0x9E, UNSET_OP_ORDER, "sahf" },
120 { 0x99, UNSET_OP_ORDER, "cdq" },
121 { 0x9B, UNSET_OP_ORDER, "fwait" },
Leon Clarked91b9f72010-01-27 17:25:45 +0000122 { 0xA4, UNSET_OP_ORDER, "movs" },
123 { 0xA5, UNSET_OP_ORDER, "movs" },
124 { 0xA6, UNSET_OP_ORDER, "cmps" },
125 { 0xA7, UNSET_OP_ORDER, "cmps" },
Steve Blocka7e24c12009-10-30 11:49:00 +0000126 { -1, UNSET_OP_ORDER, "" }
127};
128
129
Ben Murdoch69a99ed2011-11-30 16:03:39 +0000130static const ByteMnemonic call_jump_instr[] = {
Steve Blocka7e24c12009-10-30 11:49:00 +0000131 { 0xE8, UNSET_OP_ORDER, "call" },
132 { 0xE9, UNSET_OP_ORDER, "jmp" },
133 { -1, UNSET_OP_ORDER, "" }
134};
135
136
Ben Murdoch69a99ed2011-11-30 16:03:39 +0000137static const ByteMnemonic short_immediate_instr[] = {
Steve Blocka7e24c12009-10-30 11:49:00 +0000138 { 0x05, UNSET_OP_ORDER, "add" },
139 { 0x0D, UNSET_OP_ORDER, "or" },
140 { 0x15, UNSET_OP_ORDER, "adc" },
141 { 0x1D, UNSET_OP_ORDER, "sbb" },
142 { 0x25, UNSET_OP_ORDER, "and" },
143 { 0x2D, UNSET_OP_ORDER, "sub" },
144 { 0x35, UNSET_OP_ORDER, "xor" },
145 { 0x3D, UNSET_OP_ORDER, "cmp" },
146 { -1, UNSET_OP_ORDER, "" }
147};
148
149
Ben Murdoch69a99ed2011-11-30 16:03:39 +0000150static const char* const conditional_code_suffix[] = {
Steve Blocka7e24c12009-10-30 11:49:00 +0000151 "o", "no", "c", "nc", "z", "nz", "na", "a",
152 "s", "ns", "pe", "po", "l", "ge", "le", "g"
153};
154
155
156enum InstructionType {
157 NO_INSTR,
158 ZERO_OPERANDS_INSTR,
159 TWO_OPERANDS_INSTR,
160 JUMP_CONDITIONAL_SHORT_INSTR,
161 REGISTER_INSTR,
162 PUSHPOP_INSTR, // Has implicit 64-bit operand size.
163 MOVE_REG_INSTR,
164 CALL_JUMP_INSTR,
165 SHORT_IMMEDIATE_INSTR
166};
167
168
Leon Clarked91b9f72010-01-27 17:25:45 +0000169enum Prefixes {
170 ESCAPE_PREFIX = 0x0F,
171 OPERAND_SIZE_OVERRIDE_PREFIX = 0x66,
172 ADDRESS_SIZE_OVERRIDE_PREFIX = 0x67,
173 REPNE_PREFIX = 0xF2,
174 REP_PREFIX = 0xF3,
175 REPEQ_PREFIX = REP_PREFIX
176};
177
178
Steve Blocka7e24c12009-10-30 11:49:00 +0000179struct InstructionDesc {
180 const char* mnem;
181 InstructionType type;
182 OperandType op_order_;
183 bool byte_size_operation; // Fixed 8-bit operation.
184};
185
186
187class InstructionTable {
188 public:
189 InstructionTable();
190 const InstructionDesc& Get(byte x) const {
191 return instructions_[x];
192 }
193
194 private:
195 InstructionDesc instructions_[256];
196 void Clear();
197 void Init();
Ben Murdoch69a99ed2011-11-30 16:03:39 +0000198 void CopyTable(const ByteMnemonic bm[], InstructionType type);
Steve Blocka7e24c12009-10-30 11:49:00 +0000199 void SetTableRange(InstructionType type, byte start, byte end, bool byte_size,
200 const char* mnem);
201 void AddJumpConditionalShort();
202};
203
204
205InstructionTable::InstructionTable() {
206 Clear();
207 Init();
208}
209
210
211void InstructionTable::Clear() {
212 for (int i = 0; i < 256; i++) {
213 instructions_[i].mnem = "(bad)";
214 instructions_[i].type = NO_INSTR;
215 instructions_[i].op_order_ = UNSET_OP_ORDER;
216 instructions_[i].byte_size_operation = false;
217 }
218}
219
220
221void InstructionTable::Init() {
222 CopyTable(two_operands_instr, TWO_OPERANDS_INSTR);
223 CopyTable(zero_operands_instr, ZERO_OPERANDS_INSTR);
224 CopyTable(call_jump_instr, CALL_JUMP_INSTR);
225 CopyTable(short_immediate_instr, SHORT_IMMEDIATE_INSTR);
226 AddJumpConditionalShort();
227 SetTableRange(PUSHPOP_INSTR, 0x50, 0x57, false, "push");
228 SetTableRange(PUSHPOP_INSTR, 0x58, 0x5F, false, "pop");
229 SetTableRange(MOVE_REG_INSTR, 0xB8, 0xBF, false, "mov");
230}
231
232
Ben Murdoch69a99ed2011-11-30 16:03:39 +0000233void InstructionTable::CopyTable(const ByteMnemonic bm[],
234 InstructionType type) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000235 for (int i = 0; bm[i].b >= 0; i++) {
236 InstructionDesc* id = &instructions_[bm[i].b];
237 id->mnem = bm[i].mnem;
238 OperandType op_order = bm[i].op_order_;
239 id->op_order_ =
240 static_cast<OperandType>(op_order & ~BYTE_SIZE_OPERAND_FLAG);
Steve Blockd0582a62009-12-15 09:54:21 +0000241 ASSERT_EQ(NO_INSTR, id->type); // Information not already entered
Steve Blocka7e24c12009-10-30 11:49:00 +0000242 id->type = type;
243 id->byte_size_operation = ((op_order & BYTE_SIZE_OPERAND_FLAG) != 0);
244 }
245}
246
247
248void InstructionTable::SetTableRange(InstructionType type,
249 byte start,
250 byte end,
251 bool byte_size,
252 const char* mnem) {
253 for (byte b = start; b <= end; b++) {
254 InstructionDesc* id = &instructions_[b];
Steve Blockd0582a62009-12-15 09:54:21 +0000255 ASSERT_EQ(NO_INSTR, id->type); // Information not already entered
Steve Blocka7e24c12009-10-30 11:49:00 +0000256 id->mnem = mnem;
257 id->type = type;
258 id->byte_size_operation = byte_size;
259 }
260}
261
262
263void InstructionTable::AddJumpConditionalShort() {
264 for (byte b = 0x70; b <= 0x7F; b++) {
265 InstructionDesc* id = &instructions_[b];
Steve Blockd0582a62009-12-15 09:54:21 +0000266 ASSERT_EQ(NO_INSTR, id->type); // Information not already entered
Steve Blocka7e24c12009-10-30 11:49:00 +0000267 id->mnem = NULL; // Computed depending on condition code.
268 id->type = JUMP_CONDITIONAL_SHORT_INSTR;
269 }
270}
271
272
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100273static v8::internal::LazyInstance<InstructionTable>::type instruction_table =
274 LAZY_INSTANCE_INITIALIZER;
Steve Blocka7e24c12009-10-30 11:49:00 +0000275
Steve Block44f0eee2011-05-26 01:26:41 +0100276
Steve Blocka7e24c12009-10-30 11:49:00 +0000277static InstructionDesc cmov_instructions[16] = {
278 {"cmovo", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
279 {"cmovno", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
280 {"cmovc", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
281 {"cmovnc", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
282 {"cmovz", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
283 {"cmovnz", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
284 {"cmovna", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
285 {"cmova", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
286 {"cmovs", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
287 {"cmovns", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
288 {"cmovpe", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
289 {"cmovpo", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
290 {"cmovl", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
291 {"cmovge", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
292 {"cmovle", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
293 {"cmovg", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false}
294};
295
296//------------------------------------------------------------------------------
297// DisassemblerX64 implementation.
298
299enum UnimplementedOpcodeAction {
300 CONTINUE_ON_UNIMPLEMENTED_OPCODE,
301 ABORT_ON_UNIMPLEMENTED_OPCODE
302};
303
304// A new DisassemblerX64 object is created to disassemble each instruction.
305// The object can only disassemble a single instruction.
306class DisassemblerX64 {
307 public:
308 DisassemblerX64(const NameConverter& converter,
309 UnimplementedOpcodeAction unimplemented_action =
310 ABORT_ON_UNIMPLEMENTED_OPCODE)
311 : converter_(converter),
312 tmp_buffer_pos_(0),
313 abort_on_unimplemented_(
314 unimplemented_action == ABORT_ON_UNIMPLEMENTED_OPCODE),
315 rex_(0),
316 operand_size_(0),
317 group_1_prefix_(0),
318 byte_size_operand_(false) {
319 tmp_buffer_[0] = '\0';
320 }
321
322 virtual ~DisassemblerX64() {
323 }
324
325 // Writes one disassembled instruction into 'buffer' (0-terminated).
326 // Returns the length of the disassembled machine instruction in bytes.
327 int InstructionDecode(v8::internal::Vector<char> buffer, byte* instruction);
328
329 private:
330 enum OperandSize {
331 BYTE_SIZE = 0,
332 WORD_SIZE = 1,
333 DOUBLEWORD_SIZE = 2,
334 QUADWORD_SIZE = 3
335 };
336
337 const NameConverter& converter_;
338 v8::internal::EmbeddedVector<char, 128> tmp_buffer_;
339 unsigned int tmp_buffer_pos_;
340 bool abort_on_unimplemented_;
341 // Prefixes parsed
342 byte rex_;
343 byte operand_size_; // 0x66 or (if no group 3 prefix is present) 0x0.
344 byte group_1_prefix_; // 0xF2, 0xF3, or (if no group 1 prefix is present) 0.
345 // Byte size operand override.
346 bool byte_size_operand_;
347
348 void setRex(byte rex) {
349 ASSERT_EQ(0x40, rex & 0xF0);
350 rex_ = rex;
351 }
352
353 bool rex() { return rex_ != 0; }
354
355 bool rex_b() { return (rex_ & 0x01) != 0; }
356
357 // Actual number of base register given the low bits and the rex.b state.
358 int base_reg(int low_bits) { return low_bits | ((rex_ & 0x01) << 3); }
359
360 bool rex_x() { return (rex_ & 0x02) != 0; }
361
362 bool rex_r() { return (rex_ & 0x04) != 0; }
363
364 bool rex_w() { return (rex_ & 0x08) != 0; }
365
366 OperandSize operand_size() {
367 if (byte_size_operand_) return BYTE_SIZE;
368 if (rex_w()) return QUADWORD_SIZE;
369 if (operand_size_ != 0) return WORD_SIZE;
370 return DOUBLEWORD_SIZE;
371 }
372
373 char operand_size_code() {
374 return "bwlq"[operand_size()];
375 }
376
377 const char* NameOfCPURegister(int reg) const {
378 return converter_.NameOfCPURegister(reg);
379 }
380
381 const char* NameOfByteCPURegister(int reg) const {
382 return converter_.NameOfByteCPURegister(reg);
383 }
384
385 const char* NameOfXMMRegister(int reg) const {
386 return converter_.NameOfXMMRegister(reg);
387 }
388
389 const char* NameOfAddress(byte* addr) const {
390 return converter_.NameOfAddress(addr);
391 }
392
393 // Disassembler helper functions.
394 void get_modrm(byte data,
395 int* mod,
396 int* regop,
397 int* rm) {
398 *mod = (data >> 6) & 3;
399 *regop = ((data & 0x38) >> 3) | (rex_r() ? 8 : 0);
400 *rm = (data & 7) | (rex_b() ? 8 : 0);
401 }
402
403 void get_sib(byte data,
404 int* scale,
405 int* index,
406 int* base) {
407 *scale = (data >> 6) & 3;
408 *index = ((data >> 3) & 7) | (rex_x() ? 8 : 0);
409 *base = (data & 7) | (rex_b() ? 8 : 0);
410 }
411
412 typedef const char* (DisassemblerX64::*RegisterNameMapping)(int reg) const;
413
414 int PrintRightOperandHelper(byte* modrmp,
415 RegisterNameMapping register_name);
416 int PrintRightOperand(byte* modrmp);
417 int PrintRightByteOperand(byte* modrmp);
Steve Blockd0582a62009-12-15 09:54:21 +0000418 int PrintRightXMMOperand(byte* modrmp);
Steve Blocka7e24c12009-10-30 11:49:00 +0000419 int PrintOperands(const char* mnem,
420 OperandType op_order,
421 byte* data);
422 int PrintImmediate(byte* data, OperandSize size);
423 int PrintImmediateOp(byte* data);
424 const char* TwoByteMnemonic(byte opcode);
425 int TwoByteOpcodeInstruction(byte* data);
Steve Blockd0582a62009-12-15 09:54:21 +0000426 int F6F7Instruction(byte* data);
Steve Blocka7e24c12009-10-30 11:49:00 +0000427 int ShiftInstruction(byte* data);
428 int JumpShort(byte* data);
429 int JumpConditional(byte* data);
430 int JumpConditionalShort(byte* data);
431 int SetCC(byte* data);
432 int FPUInstruction(byte* data);
Steve Blockd0582a62009-12-15 09:54:21 +0000433 int MemoryFPUInstruction(int escape_opcode, int regop, byte* modrm_start);
434 int RegisterFPUInstruction(int escape_opcode, byte modrm_byte);
Steve Blocka7e24c12009-10-30 11:49:00 +0000435 void AppendToBuffer(const char* format, ...);
436
437 void UnimplementedInstruction() {
438 if (abort_on_unimplemented_) {
439 CHECK(false);
440 } else {
441 AppendToBuffer("'Unimplemented Instruction'");
442 }
443 }
444};
445
446
447void DisassemblerX64::AppendToBuffer(const char* format, ...) {
448 v8::internal::Vector<char> buf = tmp_buffer_ + tmp_buffer_pos_;
449 va_list args;
450 va_start(args, format);
451 int result = v8::internal::OS::VSNPrintF(buf, format, args);
452 va_end(args);
453 tmp_buffer_pos_ += result;
454}
455
456
457int DisassemblerX64::PrintRightOperandHelper(
458 byte* modrmp,
Steve Block44f0eee2011-05-26 01:26:41 +0100459 RegisterNameMapping direct_register_name) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000460 int mod, regop, rm;
461 get_modrm(*modrmp, &mod, &regop, &rm);
Steve Block44f0eee2011-05-26 01:26:41 +0100462 RegisterNameMapping register_name = (mod == 3) ? direct_register_name :
463 &DisassemblerX64::NameOfCPURegister;
Steve Blocka7e24c12009-10-30 11:49:00 +0000464 switch (mod) {
465 case 0:
466 if ((rm & 7) == 5) {
467 int32_t disp = *reinterpret_cast<int32_t*>(modrmp + 1);
468 AppendToBuffer("[0x%x]", disp);
469 return 5;
470 } else if ((rm & 7) == 4) {
471 // Codes for SIB byte.
472 byte sib = *(modrmp + 1);
473 int scale, index, base;
474 get_sib(sib, &scale, &index, &base);
475 if (index == 4 && (base & 7) == 4 && scale == 0 /*times_1*/) {
476 // index == rsp means no index. Only use sib byte with no index for
477 // rsp and r12 base.
Steve Block8defd9f2010-07-08 12:39:36 +0100478 AppendToBuffer("[%s]", NameOfCPURegister(base));
Steve Blocka7e24c12009-10-30 11:49:00 +0000479 return 2;
480 } else if (base == 5) {
481 // base == rbp means no base register (when mod == 0).
482 int32_t disp = *reinterpret_cast<int32_t*>(modrmp + 2);
483 AppendToBuffer("[%s*%d+0x%x]",
Steve Block8defd9f2010-07-08 12:39:36 +0100484 NameOfCPURegister(index),
Steve Blocka7e24c12009-10-30 11:49:00 +0000485 1 << scale, disp);
486 return 6;
487 } else if (index != 4 && base != 5) {
488 // [base+index*scale]
489 AppendToBuffer("[%s+%s*%d]",
Steve Block8defd9f2010-07-08 12:39:36 +0100490 NameOfCPURegister(base),
491 NameOfCPURegister(index),
Steve Blocka7e24c12009-10-30 11:49:00 +0000492 1 << scale);
493 return 2;
494 } else {
495 UnimplementedInstruction();
496 return 1;
497 }
498 } else {
Steve Block8defd9f2010-07-08 12:39:36 +0100499 AppendToBuffer("[%s]", NameOfCPURegister(rm));
Steve Blocka7e24c12009-10-30 11:49:00 +0000500 return 1;
501 }
502 break;
503 case 1: // fall through
504 case 2:
505 if ((rm & 7) == 4) {
506 byte sib = *(modrmp + 1);
507 int scale, index, base;
508 get_sib(sib, &scale, &index, &base);
509 int disp = (mod == 2) ? *reinterpret_cast<int32_t*>(modrmp + 2)
510 : *reinterpret_cast<char*>(modrmp + 2);
511 if (index == 4 && (base & 7) == 4 && scale == 0 /*times_1*/) {
512 if (-disp > 0) {
Steve Block8defd9f2010-07-08 12:39:36 +0100513 AppendToBuffer("[%s-0x%x]", NameOfCPURegister(base), -disp);
Steve Blocka7e24c12009-10-30 11:49:00 +0000514 } else {
Steve Block8defd9f2010-07-08 12:39:36 +0100515 AppendToBuffer("[%s+0x%x]", NameOfCPURegister(base), disp);
Steve Blocka7e24c12009-10-30 11:49:00 +0000516 }
517 } else {
518 if (-disp > 0) {
519 AppendToBuffer("[%s+%s*%d-0x%x]",
Steve Block8defd9f2010-07-08 12:39:36 +0100520 NameOfCPURegister(base),
521 NameOfCPURegister(index),
Steve Blocka7e24c12009-10-30 11:49:00 +0000522 1 << scale,
523 -disp);
524 } else {
525 AppendToBuffer("[%s+%s*%d+0x%x]",
Steve Block8defd9f2010-07-08 12:39:36 +0100526 NameOfCPURegister(base),
527 NameOfCPURegister(index),
Steve Blocka7e24c12009-10-30 11:49:00 +0000528 1 << scale,
529 disp);
530 }
531 }
532 return mod == 2 ? 6 : 3;
533 } else {
534 // No sib.
535 int disp = (mod == 2) ? *reinterpret_cast<int32_t*>(modrmp + 1)
536 : *reinterpret_cast<char*>(modrmp + 1);
537 if (-disp > 0) {
Steve Block8defd9f2010-07-08 12:39:36 +0100538 AppendToBuffer("[%s-0x%x]", NameOfCPURegister(rm), -disp);
Steve Blocka7e24c12009-10-30 11:49:00 +0000539 } else {
Steve Block8defd9f2010-07-08 12:39:36 +0100540 AppendToBuffer("[%s+0x%x]", NameOfCPURegister(rm), disp);
Steve Blocka7e24c12009-10-30 11:49:00 +0000541 }
542 return (mod == 2) ? 5 : 2;
543 }
544 break;
545 case 3:
546 AppendToBuffer("%s", (this->*register_name)(rm));
547 return 1;
548 default:
549 UnimplementedInstruction();
550 return 1;
551 }
552 UNREACHABLE();
553}
554
555
556int DisassemblerX64::PrintImmediate(byte* data, OperandSize size) {
557 int64_t value;
558 int count;
559 switch (size) {
560 case BYTE_SIZE:
561 value = *data;
562 count = 1;
563 break;
564 case WORD_SIZE:
565 value = *reinterpret_cast<int16_t*>(data);
566 count = 2;
567 break;
568 case DOUBLEWORD_SIZE:
569 value = *reinterpret_cast<uint32_t*>(data);
570 count = 4;
571 break;
572 case QUADWORD_SIZE:
573 value = *reinterpret_cast<int32_t*>(data);
574 count = 4;
575 break;
576 default:
577 UNREACHABLE();
578 value = 0; // Initialize variables on all paths to satisfy the compiler.
579 count = 0;
580 }
581 AppendToBuffer("%" V8_PTR_PREFIX "x", value);
582 return count;
583}
584
585
586int DisassemblerX64::PrintRightOperand(byte* modrmp) {
587 return PrintRightOperandHelper(modrmp,
588 &DisassemblerX64::NameOfCPURegister);
589}
590
591
592int DisassemblerX64::PrintRightByteOperand(byte* modrmp) {
593 return PrintRightOperandHelper(modrmp,
594 &DisassemblerX64::NameOfByteCPURegister);
595}
596
597
Steve Blockd0582a62009-12-15 09:54:21 +0000598int DisassemblerX64::PrintRightXMMOperand(byte* modrmp) {
599 return PrintRightOperandHelper(modrmp,
600 &DisassemblerX64::NameOfXMMRegister);
601}
602
603
Steve Blocka7e24c12009-10-30 11:49:00 +0000604// Returns number of bytes used including the current *data.
605// Writes instruction's mnemonic, left and right operands to 'tmp_buffer_'.
606int DisassemblerX64::PrintOperands(const char* mnem,
607 OperandType op_order,
608 byte* data) {
609 byte modrm = *data;
610 int mod, regop, rm;
611 get_modrm(modrm, &mod, &regop, &rm);
612 int advance = 0;
613 const char* register_name =
614 byte_size_operand_ ? NameOfByteCPURegister(regop)
615 : NameOfCPURegister(regop);
616 switch (op_order) {
617 case REG_OPER_OP_ORDER: {
618 AppendToBuffer("%s%c %s,",
619 mnem,
620 operand_size_code(),
621 register_name);
622 advance = byte_size_operand_ ? PrintRightByteOperand(data)
623 : PrintRightOperand(data);
624 break;
625 }
626 case OPER_REG_OP_ORDER: {
627 AppendToBuffer("%s%c ", mnem, operand_size_code());
628 advance = byte_size_operand_ ? PrintRightByteOperand(data)
629 : PrintRightOperand(data);
630 AppendToBuffer(",%s", register_name);
631 break;
632 }
633 default:
634 UNREACHABLE();
635 break;
636 }
637 return advance;
638}
639
640
641// Returns number of bytes used by machine instruction, including *data byte.
642// Writes immediate instructions to 'tmp_buffer_'.
643int DisassemblerX64::PrintImmediateOp(byte* data) {
644 bool byte_size_immediate = (*data & 0x02) != 0;
645 byte modrm = *(data + 1);
646 int mod, regop, rm;
647 get_modrm(modrm, &mod, &regop, &rm);
648 const char* mnem = "Imm???";
649 switch (regop) {
650 case 0:
651 mnem = "add";
652 break;
653 case 1:
654 mnem = "or";
655 break;
656 case 2:
657 mnem = "adc";
658 break;
Ben Murdoch8b112d22011-06-08 16:22:53 +0100659 case 3:
660 mnem = "sbb";
661 break;
Steve Blocka7e24c12009-10-30 11:49:00 +0000662 case 4:
663 mnem = "and";
664 break;
665 case 5:
666 mnem = "sub";
667 break;
668 case 6:
669 mnem = "xor";
670 break;
671 case 7:
672 mnem = "cmp";
673 break;
674 default:
675 UnimplementedInstruction();
676 }
677 AppendToBuffer("%s%c ", mnem, operand_size_code());
678 int count = PrintRightOperand(data + 1);
679 AppendToBuffer(",0x");
680 OperandSize immediate_size = byte_size_immediate ? BYTE_SIZE : operand_size();
681 count += PrintImmediate(data + 1 + count, immediate_size);
682 return 1 + count;
683}
684
685
686// Returns number of bytes used, including *data.
Steve Blockd0582a62009-12-15 09:54:21 +0000687int DisassemblerX64::F6F7Instruction(byte* data) {
688 ASSERT(*data == 0xF7 || *data == 0xF6);
Steve Blocka7e24c12009-10-30 11:49:00 +0000689 byte modrm = *(data + 1);
690 int mod, regop, rm;
691 get_modrm(modrm, &mod, &regop, &rm);
692 if (mod == 3 && regop != 0) {
693 const char* mnem = NULL;
694 switch (regop) {
695 case 2:
696 mnem = "not";
697 break;
698 case 3:
699 mnem = "neg";
700 break;
701 case 4:
702 mnem = "mul";
703 break;
704 case 7:
705 mnem = "idiv";
706 break;
707 default:
708 UnimplementedInstruction();
709 }
710 AppendToBuffer("%s%c %s",
711 mnem,
712 operand_size_code(),
713 NameOfCPURegister(rm));
714 return 2;
Steve Blocka7e24c12009-10-30 11:49:00 +0000715 } else if (regop == 0) {
716 AppendToBuffer("test%c ", operand_size_code());
Steve Blockd0582a62009-12-15 09:54:21 +0000717 int count = PrintRightOperand(data + 1); // Use name of 64-bit register.
718 AppendToBuffer(",0x");
719 count += PrintImmediate(data + 1 + count, operand_size());
720 return 1 + count;
Steve Blocka7e24c12009-10-30 11:49:00 +0000721 } else {
722 UnimplementedInstruction();
723 return 2;
724 }
725}
726
727
728int DisassemblerX64::ShiftInstruction(byte* data) {
729 byte op = *data & (~1);
730 if (op != 0xD0 && op != 0xD2 && op != 0xC0) {
731 UnimplementedInstruction();
732 return 1;
733 }
734 byte modrm = *(data + 1);
735 int mod, regop, rm;
736 get_modrm(modrm, &mod, &regop, &rm);
737 regop &= 0x7; // The REX.R bit does not affect the operation.
738 int imm8 = -1;
739 int num_bytes = 2;
740 if (mod != 3) {
741 UnimplementedInstruction();
742 return num_bytes;
743 }
744 const char* mnem = NULL;
745 switch (regop) {
746 case 0:
747 mnem = "rol";
748 break;
749 case 1:
750 mnem = "ror";
751 break;
752 case 2:
753 mnem = "rcl";
754 break;
755 case 3:
756 mnem = "rcr";
757 break;
758 case 4:
759 mnem = "shl";
760 break;
761 case 5:
762 mnem = "shr";
763 break;
764 case 7:
765 mnem = "sar";
766 break;
767 default:
768 UnimplementedInstruction();
769 return num_bytes;
770 }
Steve Blockd0582a62009-12-15 09:54:21 +0000771 ASSERT_NE(NULL, mnem);
Steve Blocka7e24c12009-10-30 11:49:00 +0000772 if (op == 0xD0) {
773 imm8 = 1;
774 } else if (op == 0xC0) {
775 imm8 = *(data + 2);
776 num_bytes = 3;
777 }
778 AppendToBuffer("%s%c %s,",
779 mnem,
780 operand_size_code(),
781 byte_size_operand_ ? NameOfByteCPURegister(rm)
782 : NameOfCPURegister(rm));
783 if (op == 0xD2) {
784 AppendToBuffer("cl");
785 } else {
786 AppendToBuffer("%d", imm8);
787 }
788 return num_bytes;
789}
790
791
792// Returns number of bytes used, including *data.
793int DisassemblerX64::JumpShort(byte* data) {
Steve Blockd0582a62009-12-15 09:54:21 +0000794 ASSERT_EQ(0xEB, *data);
Steve Blocka7e24c12009-10-30 11:49:00 +0000795 byte b = *(data + 1);
796 byte* dest = data + static_cast<int8_t>(b) + 2;
797 AppendToBuffer("jmp %s", NameOfAddress(dest));
798 return 2;
799}
800
801
802// Returns number of bytes used, including *data.
803int DisassemblerX64::JumpConditional(byte* data) {
Steve Blockd0582a62009-12-15 09:54:21 +0000804 ASSERT_EQ(0x0F, *data);
Steve Blocka7e24c12009-10-30 11:49:00 +0000805 byte cond = *(data + 1) & 0x0F;
806 byte* dest = data + *reinterpret_cast<int32_t*>(data + 2) + 6;
807 const char* mnem = conditional_code_suffix[cond];
808 AppendToBuffer("j%s %s", mnem, NameOfAddress(dest));
809 return 6; // includes 0x0F
810}
811
812
813// Returns number of bytes used, including *data.
814int DisassemblerX64::JumpConditionalShort(byte* data) {
815 byte cond = *data & 0x0F;
816 byte b = *(data + 1);
817 byte* dest = data + static_cast<int8_t>(b) + 2;
818 const char* mnem = conditional_code_suffix[cond];
819 AppendToBuffer("j%s %s", mnem, NameOfAddress(dest));
820 return 2;
821}
822
823
824// Returns number of bytes used, including *data.
825int DisassemblerX64::SetCC(byte* data) {
Steve Blockd0582a62009-12-15 09:54:21 +0000826 ASSERT_EQ(0x0F, *data);
Steve Blocka7e24c12009-10-30 11:49:00 +0000827 byte cond = *(data + 1) & 0x0F;
828 const char* mnem = conditional_code_suffix[cond];
829 AppendToBuffer("set%s%c ", mnem, operand_size_code());
830 PrintRightByteOperand(data + 2);
831 return 3; // includes 0x0F
832}
833
834
835// Returns number of bytes used, including *data.
836int DisassemblerX64::FPUInstruction(byte* data) {
Steve Blockd0582a62009-12-15 09:54:21 +0000837 byte escape_opcode = *data;
838 ASSERT_EQ(0xD8, escape_opcode & 0xF8);
839 byte modrm_byte = *(data+1);
840
841 if (modrm_byte >= 0xC0) {
842 return RegisterFPUInstruction(escape_opcode, modrm_byte);
843 } else {
844 return MemoryFPUInstruction(escape_opcode, modrm_byte, data+1);
Steve Blocka7e24c12009-10-30 11:49:00 +0000845 }
Steve Blockd0582a62009-12-15 09:54:21 +0000846}
847
848int DisassemblerX64::MemoryFPUInstruction(int escape_opcode,
849 int modrm_byte,
850 byte* modrm_start) {
851 const char* mnem = "?";
852 int regop = (modrm_byte >> 3) & 0x7; // reg/op field of modrm byte.
853 switch (escape_opcode) {
854 case 0xD9: switch (regop) {
855 case 0: mnem = "fld_s"; break;
856 case 3: mnem = "fstp_s"; break;
857 case 7: mnem = "fstcw"; break;
858 default: UnimplementedInstruction();
859 }
860 break;
861
862 case 0xDB: switch (regop) {
863 case 0: mnem = "fild_s"; break;
864 case 1: mnem = "fisttp_s"; break;
865 case 2: mnem = "fist_s"; break;
866 case 3: mnem = "fistp_s"; break;
867 default: UnimplementedInstruction();
868 }
869 break;
870
871 case 0xDD: switch (regop) {
872 case 0: mnem = "fld_d"; break;
873 case 3: mnem = "fstp_d"; break;
874 default: UnimplementedInstruction();
875 }
876 break;
877
878 case 0xDF: switch (regop) {
879 case 5: mnem = "fild_d"; break;
880 case 7: mnem = "fistp_d"; break;
881 default: UnimplementedInstruction();
882 }
883 break;
884
885 default: UnimplementedInstruction();
886 }
887 AppendToBuffer("%s ", mnem);
888 int count = PrintRightOperand(modrm_start);
889 return count + 1;
890}
891
892int DisassemblerX64::RegisterFPUInstruction(int escape_opcode,
893 byte modrm_byte) {
894 bool has_register = false; // Is the FPU register encoded in modrm_byte?
895 const char* mnem = "?";
896
897 switch (escape_opcode) {
898 case 0xD8:
899 UnimplementedInstruction();
900 break;
901
902 case 0xD9:
903 switch (modrm_byte & 0xF8) {
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100904 case 0xC0:
905 mnem = "fld";
906 has_register = true;
907 break;
Steve Blockd0582a62009-12-15 09:54:21 +0000908 case 0xC8:
909 mnem = "fxch";
910 has_register = true;
911 break;
912 default:
913 switch (modrm_byte) {
914 case 0xE0: mnem = "fchs"; break;
915 case 0xE1: mnem = "fabs"; break;
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100916 case 0xE3: mnem = "fninit"; break;
Steve Blockd0582a62009-12-15 09:54:21 +0000917 case 0xE4: mnem = "ftst"; break;
918 case 0xE8: mnem = "fld1"; break;
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100919 case 0xEB: mnem = "fldpi"; break;
Ben Murdochb0fe1622011-05-05 13:52:32 +0100920 case 0xED: mnem = "fldln2"; break;
Steve Blockd0582a62009-12-15 09:54:21 +0000921 case 0xEE: mnem = "fldz"; break;
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100922 case 0xF0: mnem = "f2xm1"; break;
Ben Murdochb0fe1622011-05-05 13:52:32 +0100923 case 0xF1: mnem = "fyl2x"; break;
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100924 case 0xF2: mnem = "fptan"; break;
Steve Blockd0582a62009-12-15 09:54:21 +0000925 case 0xF5: mnem = "fprem1"; break;
926 case 0xF7: mnem = "fincstp"; break;
927 case 0xF8: mnem = "fprem"; break;
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100928 case 0xFD: mnem = "fscale"; break;
Steve Blockd0582a62009-12-15 09:54:21 +0000929 case 0xFE: mnem = "fsin"; break;
930 case 0xFF: mnem = "fcos"; break;
931 default: UnimplementedInstruction();
932 }
933 }
934 break;
935
936 case 0xDA:
937 if (modrm_byte == 0xE9) {
938 mnem = "fucompp";
939 } else {
940 UnimplementedInstruction();
941 }
942 break;
943
944 case 0xDB:
945 if ((modrm_byte & 0xF8) == 0xE8) {
946 mnem = "fucomi";
947 has_register = true;
948 } else if (modrm_byte == 0xE2) {
949 mnem = "fclex";
950 } else {
951 UnimplementedInstruction();
952 }
953 break;
954
955 case 0xDC:
956 has_register = true;
957 switch (modrm_byte & 0xF8) {
958 case 0xC0: mnem = "fadd"; break;
959 case 0xE8: mnem = "fsub"; break;
960 case 0xC8: mnem = "fmul"; break;
961 case 0xF8: mnem = "fdiv"; break;
962 default: UnimplementedInstruction();
963 }
964 break;
965
966 case 0xDD:
967 has_register = true;
968 switch (modrm_byte & 0xF8) {
969 case 0xC0: mnem = "ffree"; break;
970 case 0xD8: mnem = "fstp"; break;
971 default: UnimplementedInstruction();
972 }
973 break;
974
975 case 0xDE:
976 if (modrm_byte == 0xD9) {
977 mnem = "fcompp";
978 } else {
979 has_register = true;
980 switch (modrm_byte & 0xF8) {
981 case 0xC0: mnem = "faddp"; break;
982 case 0xE8: mnem = "fsubp"; break;
983 case 0xC8: mnem = "fmulp"; break;
984 case 0xF8: mnem = "fdivp"; break;
985 default: UnimplementedInstruction();
986 }
987 }
988 break;
989
990 case 0xDF:
991 if (modrm_byte == 0xE0) {
992 mnem = "fnstsw_ax";
993 } else if ((modrm_byte & 0xF8) == 0xE8) {
994 mnem = "fucomip";
995 has_register = true;
996 }
997 break;
998
999 default: UnimplementedInstruction();
1000 }
1001
1002 if (has_register) {
1003 AppendToBuffer("%s st%d", mnem, modrm_byte & 0x7);
1004 } else {
1005 AppendToBuffer("%s", mnem);
1006 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001007 return 2;
1008}
1009
1010
Steve Blockd0582a62009-12-15 09:54:21 +00001011
Steve Blocka7e24c12009-10-30 11:49:00 +00001012// Handle all two-byte opcodes, which start with 0x0F.
1013// These instructions may be affected by an 0x66, 0xF2, or 0xF3 prefix.
1014// We do not use any three-byte opcodes, which start with 0x0F38 or 0x0F3A.
1015int DisassemblerX64::TwoByteOpcodeInstruction(byte* data) {
1016 byte opcode = *(data + 1);
1017 byte* current = data + 2;
1018 // At return, "current" points to the start of the next instruction.
1019 const char* mnemonic = TwoByteMnemonic(opcode);
Andrei Popescu402d9372010-02-26 13:31:12 +00001020 if (operand_size_ == 0x66) {
1021 // 0x66 0x0F prefix.
Steve Blocka7e24c12009-10-30 11:49:00 +00001022 int mod, regop, rm;
Steve Block6ded16b2010-05-10 14:33:55 +01001023 if (opcode == 0x3A) {
1024 byte third_byte = *current;
1025 current = data + 3;
1026 if (third_byte == 0x17) {
1027 get_modrm(*current, &mod, &regop, &rm);
1028 AppendToBuffer("extractps "); // reg/m32, xmm, imm8
1029 current += PrintRightOperand(current);
1030 AppendToBuffer(", %s, %d", NameOfCPURegister(regop), (*current) & 3);
1031 current += 1;
Ben Murdoch257744e2011-11-30 15:57:28 +00001032 } else if (third_byte == 0x0b) {
1033 get_modrm(*current, &mod, &regop, &rm);
1034 // roundsd xmm, xmm/m64, imm8
1035 AppendToBuffer("roundsd %s, ", NameOfCPURegister(regop));
1036 current += PrintRightOperand(current);
1037 AppendToBuffer(", %d", (*current) & 3);
1038 current += 1;
Steve Block6ded16b2010-05-10 14:33:55 +01001039 } else {
1040 UnimplementedInstruction();
1041 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001042 } else {
Steve Block6ded16b2010-05-10 14:33:55 +01001043 get_modrm(*current, &mod, &regop, &rm);
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001044 if (opcode == 0x1f) {
1045 current++;
1046 if (rm == 4) { // SIB byte present.
1047 current++;
1048 }
1049 if (mod == 1) { // Byte displacement.
1050 current += 1;
1051 } else if (mod == 2) { // 32-bit displacement.
1052 current += 4;
1053 } // else no immediate displacement.
1054 AppendToBuffer("nop");
1055 } else if (opcode == 0x28) {
Ben Murdoch257744e2011-11-30 15:57:28 +00001056 AppendToBuffer("movapd %s, ", NameOfXMMRegister(regop));
1057 current += PrintRightXMMOperand(current);
1058 } else if (opcode == 0x29) {
1059 AppendToBuffer("movapd ");
1060 current += PrintRightXMMOperand(current);
1061 AppendToBuffer(", %s", NameOfXMMRegister(regop));
1062 } else if (opcode == 0x6E) {
Steve Block6ded16b2010-05-10 14:33:55 +01001063 AppendToBuffer("mov%c %s,",
1064 rex_w() ? 'q' : 'd',
1065 NameOfXMMRegister(regop));
1066 current += PrintRightOperand(current);
Steve Block1e0659c2011-05-24 12:43:12 +01001067 } else if (opcode == 0x6F) {
1068 AppendToBuffer("movdqa %s,",
1069 NameOfXMMRegister(regop));
Steve Block44f0eee2011-05-26 01:26:41 +01001070 current += PrintRightXMMOperand(current);
Steve Block6ded16b2010-05-10 14:33:55 +01001071 } else if (opcode == 0x7E) {
Ben Murdochbb769b22010-08-11 14:56:33 +01001072 AppendToBuffer("mov%c ",
1073 rex_w() ? 'q' : 'd');
1074 current += PrintRightOperand(current);
1075 AppendToBuffer(", %s", NameOfXMMRegister(regop));
Steve Block1e0659c2011-05-24 12:43:12 +01001076 } else if (opcode == 0x7F) {
1077 AppendToBuffer("movdqa ");
Steve Block44f0eee2011-05-26 01:26:41 +01001078 current += PrintRightXMMOperand(current);
Steve Block1e0659c2011-05-24 12:43:12 +01001079 AppendToBuffer(", %s", NameOfXMMRegister(regop));
Ben Murdoch257744e2011-11-30 15:57:28 +00001080 } else if (opcode == 0xD6) {
1081 AppendToBuffer("movq ");
1082 current += PrintRightXMMOperand(current);
1083 AppendToBuffer(", %s", NameOfXMMRegister(regop));
Ben Murdoch3fb3ca82011-12-02 17:19:32 +00001084 } else if (opcode == 0x50) {
1085 AppendToBuffer("movmskpd %s,", NameOfCPURegister(regop));
1086 current += PrintRightXMMOperand(current);
Steve Block6ded16b2010-05-10 14:33:55 +01001087 } else {
1088 const char* mnemonic = "?";
Ben Murdoch3fb3ca82011-12-02 17:19:32 +00001089 if (opcode == 0x54) {
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001090 mnemonic = "andpd";
1091 } else if (opcode == 0x56) {
1092 mnemonic = "orpd";
1093 } else if (opcode == 0x57) {
Steve Block6ded16b2010-05-10 14:33:55 +01001094 mnemonic = "xorpd";
1095 } else if (opcode == 0x2E) {
Steve Block6ded16b2010-05-10 14:33:55 +01001096 mnemonic = "ucomisd";
Steve Block8defd9f2010-07-08 12:39:36 +01001097 } else if (opcode == 0x2F) {
1098 mnemonic = "comisd";
Steve Block6ded16b2010-05-10 14:33:55 +01001099 } else {
1100 UnimplementedInstruction();
1101 }
1102 AppendToBuffer("%s %s,", mnemonic, NameOfXMMRegister(regop));
1103 current += PrintRightXMMOperand(current);
1104 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001105 }
1106 } else if (group_1_prefix_ == 0xF2) {
1107 // Beginning of instructions with prefix 0xF2.
1108
1109 if (opcode == 0x11 || opcode == 0x10) {
1110 // MOVSD: Move scalar double-precision fp to/from/between XMM registers.
1111 AppendToBuffer("movsd ");
1112 int mod, regop, rm;
1113 get_modrm(*current, &mod, &regop, &rm);
1114 if (opcode == 0x11) {
Steve Block44f0eee2011-05-26 01:26:41 +01001115 current += PrintRightXMMOperand(current);
Steve Blocka7e24c12009-10-30 11:49:00 +00001116 AppendToBuffer(",%s", NameOfXMMRegister(regop));
1117 } else {
1118 AppendToBuffer("%s,", NameOfXMMRegister(regop));
Steve Block44f0eee2011-05-26 01:26:41 +01001119 current += PrintRightXMMOperand(current);
Steve Blocka7e24c12009-10-30 11:49:00 +00001120 }
1121 } else if (opcode == 0x2A) {
1122 // CVTSI2SD: integer to XMM double conversion.
1123 int mod, regop, rm;
1124 get_modrm(*current, &mod, &regop, &rm);
Steve Block8defd9f2010-07-08 12:39:36 +01001125 AppendToBuffer("%sd %s,", mnemonic, NameOfXMMRegister(regop));
Steve Blockd0582a62009-12-15 09:54:21 +00001126 current += PrintRightOperand(current);
Kristian Monsen0d5e1162010-09-30 15:31:59 +01001127 } else if (opcode == 0x2C) {
1128 // CVTTSD2SI:
1129 // Convert with truncation scalar double-precision FP to integer.
1130 int mod, regop, rm;
1131 get_modrm(*current, &mod, &regop, &rm);
1132 AppendToBuffer("cvttsd2si%c %s,",
1133 operand_size_code(), NameOfCPURegister(regop));
1134 current += PrintRightXMMOperand(current);
1135 } else if (opcode == 0x2D) {
1136 // CVTSD2SI: Convert scalar double-precision FP to integer.
1137 int mod, regop, rm;
1138 get_modrm(*current, &mod, &regop, &rm);
1139 AppendToBuffer("cvtsd2si%c %s,",
1140 operand_size_code(), NameOfCPURegister(regop));
1141 current += PrintRightXMMOperand(current);
Steve Block6ded16b2010-05-10 14:33:55 +01001142 } else if ((opcode & 0xF8) == 0x58 || opcode == 0x51) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001143 // XMM arithmetic. Mnemonic was retrieved at the start of this function.
1144 int mod, regop, rm;
1145 get_modrm(*current, &mod, &regop, &rm);
Steve Blockd0582a62009-12-15 09:54:21 +00001146 AppendToBuffer("%s %s,", mnemonic, NameOfXMMRegister(regop));
1147 current += PrintRightXMMOperand(current);
Steve Blocka7e24c12009-10-30 11:49:00 +00001148 } else {
1149 UnimplementedInstruction();
1150 }
Steve Block6ded16b2010-05-10 14:33:55 +01001151 } else if (group_1_prefix_ == 0xF3) {
1152 // Instructions with prefix 0xF3.
Steve Block8defd9f2010-07-08 12:39:36 +01001153 if (opcode == 0x11 || opcode == 0x10) {
1154 // MOVSS: Move scalar double-precision fp to/from/between XMM registers.
1155 AppendToBuffer("movss ");
1156 int mod, regop, rm;
1157 get_modrm(*current, &mod, &regop, &rm);
1158 if (opcode == 0x11) {
1159 current += PrintRightOperand(current);
1160 AppendToBuffer(",%s", NameOfXMMRegister(regop));
1161 } else {
1162 AppendToBuffer("%s,", NameOfXMMRegister(regop));
1163 current += PrintRightOperand(current);
1164 }
1165 } else if (opcode == 0x2A) {
1166 // CVTSI2SS: integer to XMM single conversion.
1167 int mod, regop, rm;
1168 get_modrm(*current, &mod, &regop, &rm);
1169 AppendToBuffer("%ss %s,", mnemonic, NameOfXMMRegister(regop));
1170 current += PrintRightOperand(current);
1171 } else if (opcode == 0x2C) {
Kristian Monsen0d5e1162010-09-30 15:31:59 +01001172 // CVTTSS2SI:
1173 // Convert with truncation scalar single-precision FP to dword integer.
Steve Block1e0659c2011-05-24 12:43:12 +01001174 int mod, regop, rm;
1175 get_modrm(*current, &mod, &regop, &rm);
1176 AppendToBuffer("cvttss2si%c %s,",
1177 operand_size_code(), NameOfCPURegister(regop));
1178 current += PrintRightXMMOperand(current);
Steve Block6ded16b2010-05-10 14:33:55 +01001179 } else if (opcode == 0x5A) {
Kristian Monsen0d5e1162010-09-30 15:31:59 +01001180 // CVTSS2SD:
1181 // Convert scalar single-precision FP to scalar double-precision FP.
Steve Block6ded16b2010-05-10 14:33:55 +01001182 int mod, regop, rm;
1183 get_modrm(*current, &mod, &regop, &rm);
1184 AppendToBuffer("cvtss2sd %s,", NameOfXMMRegister(regop));
1185 current += PrintRightXMMOperand(current);
Ben Murdoch257744e2011-11-30 15:57:28 +00001186 } else if (opcode == 0x7E) {
1187 int mod, regop, rm;
1188 get_modrm(*current, &mod, &regop, &rm);
1189 AppendToBuffer("movq %s, ", NameOfXMMRegister(regop));
1190 current += PrintRightXMMOperand(current);
Steve Block6ded16b2010-05-10 14:33:55 +01001191 } else {
1192 UnimplementedInstruction();
1193 }
Andrei Popescu402d9372010-02-26 13:31:12 +00001194 } else if (opcode == 0x1F) {
1195 // NOP
1196 int mod, regop, rm;
1197 get_modrm(*current, &mod, &regop, &rm);
1198 current++;
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001199 if (rm == 4) { // SIB byte present.
Andrei Popescu402d9372010-02-26 13:31:12 +00001200 current++;
1201 }
1202 if (mod == 1) { // Byte displacement.
1203 current += 1;
1204 } else if (mod == 2) { // 32-bit displacement.
1205 current += 4;
1206 } // else no immediate displacement.
1207 AppendToBuffer("nop");
Ben Murdoch257744e2011-11-30 15:57:28 +00001208
1209 } else if (opcode == 0x28) {
1210 // movaps xmm, xmm/m128
1211 int mod, regop, rm;
1212 get_modrm(*current, &mod, &regop, &rm);
1213 AppendToBuffer("movaps %s, ", NameOfXMMRegister(regop));
1214 current += PrintRightXMMOperand(current);
1215
1216 } else if (opcode == 0x29) {
1217 // movaps xmm/m128, xmm
1218 int mod, regop, rm;
1219 get_modrm(*current, &mod, &regop, &rm);
1220 AppendToBuffer("movaps ");
1221 current += PrintRightXMMOperand(current);
1222 AppendToBuffer(", %s", NameOfXMMRegister(regop));
1223
Andrei Popescu402d9372010-02-26 13:31:12 +00001224 } else if (opcode == 0xA2 || opcode == 0x31) {
1225 // RDTSC or CPUID
1226 AppendToBuffer("%s", mnemonic);
1227
1228 } else if ((opcode & 0xF0) == 0x40) {
1229 // CMOVcc: conditional move.
1230 int condition = opcode & 0x0F;
1231 const InstructionDesc& idesc = cmov_instructions[condition];
1232 byte_size_operand_ = idesc.byte_size_operation;
1233 current += PrintOperands(idesc.mnem, idesc.op_order_, current);
1234
Ben Murdoch257744e2011-11-30 15:57:28 +00001235 } else if (opcode == 0x57) {
1236 // xorps xmm, xmm/m128
1237 int mod, regop, rm;
1238 get_modrm(*current, &mod, &regop, &rm);
1239 AppendToBuffer("xorps %s, ", NameOfXMMRegister(regop));
1240 current += PrintRightXMMOperand(current);
1241
Andrei Popescu402d9372010-02-26 13:31:12 +00001242 } else if ((opcode & 0xF0) == 0x80) {
1243 // Jcc: Conditional jump (branch).
1244 current = data + JumpConditional(data);
1245
1246 } else if (opcode == 0xBE || opcode == 0xBF || opcode == 0xB6 ||
1247 opcode == 0xB7 || opcode == 0xAF) {
1248 // Size-extending moves, IMUL.
1249 current += PrintOperands(mnemonic, REG_OPER_OP_ORDER, current);
1250
1251 } else if ((opcode & 0xF0) == 0x90) {
1252 // SETcc: Set byte on condition. Needs pointer to beginning of instruction.
1253 current = data + SetCC(data);
1254
1255 } else if (opcode == 0xAB || opcode == 0xA5 || opcode == 0xAD) {
1256 // SHLD, SHRD (double-precision shift), BTS (bit set).
1257 AppendToBuffer("%s ", mnemonic);
1258 int mod, regop, rm;
1259 get_modrm(*current, &mod, &regop, &rm);
1260 current += PrintRightOperand(current);
1261 if (opcode == 0xAB) {
1262 AppendToBuffer(",%s", NameOfCPURegister(regop));
1263 } else {
1264 AppendToBuffer(",%s,cl", NameOfCPURegister(regop));
1265 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001266 } else {
1267 UnimplementedInstruction();
1268 }
Steve Blockd0582a62009-12-15 09:54:21 +00001269 return static_cast<int>(current - data);
Steve Blocka7e24c12009-10-30 11:49:00 +00001270}
1271
1272
1273// Mnemonics for two-byte opcode instructions starting with 0x0F.
1274// The argument is the second byte of the two-byte opcode.
1275// Returns NULL if the instruction is not handled here.
1276const char* DisassemblerX64::TwoByteMnemonic(byte opcode) {
1277 switch (opcode) {
1278 case 0x1F:
1279 return "nop";
Steve Block8defd9f2010-07-08 12:39:36 +01001280 case 0x2A: // F2/F3 prefix.
1281 return "cvtsi2s";
Steve Blocka7e24c12009-10-30 11:49:00 +00001282 case 0x31:
1283 return "rdtsc";
Steve Block6ded16b2010-05-10 14:33:55 +01001284 case 0x51: // F2 prefix.
1285 return "sqrtsd";
Steve Blocka7e24c12009-10-30 11:49:00 +00001286 case 0x58: // F2 prefix.
1287 return "addsd";
1288 case 0x59: // F2 prefix.
1289 return "mulsd";
1290 case 0x5C: // F2 prefix.
1291 return "subsd";
1292 case 0x5E: // F2 prefix.
1293 return "divsd";
1294 case 0xA2:
1295 return "cpuid";
1296 case 0xA5:
1297 return "shld";
1298 case 0xAB:
1299 return "bts";
1300 case 0xAD:
1301 return "shrd";
1302 case 0xAF:
1303 return "imul";
1304 case 0xB6:
1305 return "movzxb";
1306 case 0xB7:
1307 return "movzxw";
1308 case 0xBE:
1309 return "movsxb";
1310 case 0xBF:
1311 return "movsxw";
1312 default:
1313 return NULL;
1314 }
1315}
1316
1317
1318// Disassembles the instruction at instr, and writes it into out_buffer.
1319int DisassemblerX64::InstructionDecode(v8::internal::Vector<char> out_buffer,
1320 byte* instr) {
1321 tmp_buffer_pos_ = 0; // starting to write as position 0
1322 byte* data = instr;
1323 bool processed = true; // Will be set to false if the current instruction
1324 // is not in 'instructions' table.
1325 byte current;
1326
1327 // Scan for prefixes.
1328 while (true) {
1329 current = *data;
Leon Clarked91b9f72010-01-27 17:25:45 +00001330 if (current == OPERAND_SIZE_OVERRIDE_PREFIX) { // Group 3 prefix.
Steve Blocka7e24c12009-10-30 11:49:00 +00001331 operand_size_ = current;
1332 } else if ((current & 0xF0) == 0x40) { // REX prefix.
1333 setRex(current);
1334 if (rex_w()) AppendToBuffer("REX.W ");
Leon Clarked91b9f72010-01-27 17:25:45 +00001335 } else if ((current & 0xFE) == 0xF2) { // Group 1 prefix (0xF2 or 0xF3).
Steve Blocka7e24c12009-10-30 11:49:00 +00001336 group_1_prefix_ = current;
1337 } else { // Not a prefix - an opcode.
1338 break;
1339 }
1340 data++;
1341 }
1342
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001343 const InstructionDesc& idesc = instruction_table.Get().Get(current);
Steve Blocka7e24c12009-10-30 11:49:00 +00001344 byte_size_operand_ = idesc.byte_size_operation;
1345 switch (idesc.type) {
1346 case ZERO_OPERANDS_INSTR:
Leon Clarked91b9f72010-01-27 17:25:45 +00001347 if (current >= 0xA4 && current <= 0xA7) {
1348 // String move or compare operations.
1349 if (group_1_prefix_ == REP_PREFIX) {
1350 // REP.
1351 AppendToBuffer("rep ");
1352 }
1353 if (rex_w()) AppendToBuffer("REX.W ");
1354 AppendToBuffer("%s%c", idesc.mnem, operand_size_code());
1355 } else {
1356 AppendToBuffer("%s", idesc.mnem, operand_size_code());
1357 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001358 data++;
1359 break;
1360
1361 case TWO_OPERANDS_INSTR:
1362 data++;
1363 data += PrintOperands(idesc.mnem, idesc.op_order_, data);
1364 break;
1365
1366 case JUMP_CONDITIONAL_SHORT_INSTR:
1367 data += JumpConditionalShort(data);
1368 break;
1369
1370 case REGISTER_INSTR:
1371 AppendToBuffer("%s%c %s",
1372 idesc.mnem,
1373 operand_size_code(),
1374 NameOfCPURegister(base_reg(current & 0x07)));
1375 data++;
1376 break;
1377 case PUSHPOP_INSTR:
1378 AppendToBuffer("%s %s",
1379 idesc.mnem,
1380 NameOfCPURegister(base_reg(current & 0x07)));
1381 data++;
1382 break;
1383 case MOVE_REG_INSTR: {
1384 byte* addr = NULL;
1385 switch (operand_size()) {
1386 case WORD_SIZE:
1387 addr = reinterpret_cast<byte*>(*reinterpret_cast<int16_t*>(data + 1));
1388 data += 3;
1389 break;
1390 case DOUBLEWORD_SIZE:
1391 addr = reinterpret_cast<byte*>(*reinterpret_cast<int32_t*>(data + 1));
1392 data += 5;
1393 break;
1394 case QUADWORD_SIZE:
1395 addr = reinterpret_cast<byte*>(*reinterpret_cast<int64_t*>(data + 1));
1396 data += 9;
1397 break;
1398 default:
1399 UNREACHABLE();
1400 }
1401 AppendToBuffer("mov%c %s,%s",
1402 operand_size_code(),
1403 NameOfCPURegister(base_reg(current & 0x07)),
1404 NameOfAddress(addr));
1405 break;
1406 }
1407
1408 case CALL_JUMP_INSTR: {
1409 byte* addr = data + *reinterpret_cast<int32_t*>(data + 1) + 5;
1410 AppendToBuffer("%s %s", idesc.mnem, NameOfAddress(addr));
1411 data += 5;
1412 break;
1413 }
1414
1415 case SHORT_IMMEDIATE_INSTR: {
1416 byte* addr =
1417 reinterpret_cast<byte*>(*reinterpret_cast<int32_t*>(data + 1));
1418 AppendToBuffer("%s rax, %s", idesc.mnem, NameOfAddress(addr));
1419 data += 5;
1420 break;
1421 }
1422
1423 case NO_INSTR:
1424 processed = false;
1425 break;
1426
1427 default:
1428 UNIMPLEMENTED(); // This type is not implemented.
1429 }
1430
1431 // The first byte didn't match any of the simple opcodes, so we
1432 // need to do special processing on it.
1433 if (!processed) {
1434 switch (*data) {
1435 case 0xC2:
1436 AppendToBuffer("ret 0x%x", *reinterpret_cast<uint16_t*>(data + 1));
1437 data += 3;
1438 break;
1439
1440 case 0x69: // fall through
1441 case 0x6B: {
1442 int mod, regop, rm;
1443 get_modrm(*(data + 1), &mod, &regop, &rm);
1444 int32_t imm = *data == 0x6B ? *(data + 2)
1445 : *reinterpret_cast<int32_t*>(data + 2);
Steve Block6ded16b2010-05-10 14:33:55 +01001446 AppendToBuffer("imul%c %s,%s,0x%x",
1447 operand_size_code(),
1448 NameOfCPURegister(regop),
Steve Blocka7e24c12009-10-30 11:49:00 +00001449 NameOfCPURegister(rm), imm);
1450 data += 2 + (*data == 0x6B ? 1 : 4);
1451 break;
1452 }
1453
Steve Blocka7e24c12009-10-30 11:49:00 +00001454 case 0x81: // fall through
1455 case 0x83: // 0x81 with sign extension bit set
1456 data += PrintImmediateOp(data);
1457 break;
1458
1459 case 0x0F:
1460 data += TwoByteOpcodeInstruction(data);
1461 break;
1462
1463 case 0x8F: {
1464 data++;
1465 int mod, regop, rm;
1466 get_modrm(*data, &mod, &regop, &rm);
1467 if (regop == 0) {
1468 AppendToBuffer("pop ");
1469 data += PrintRightOperand(data);
1470 }
1471 }
1472 break;
1473
1474 case 0xFF: {
1475 data++;
1476 int mod, regop, rm;
1477 get_modrm(*data, &mod, &regop, &rm);
1478 const char* mnem = NULL;
1479 switch (regop) {
1480 case 0:
1481 mnem = "inc";
1482 break;
1483 case 1:
1484 mnem = "dec";
1485 break;
1486 case 2:
1487 mnem = "call";
1488 break;
1489 case 4:
1490 mnem = "jmp";
1491 break;
1492 case 6:
1493 mnem = "push";
1494 break;
1495 default:
1496 mnem = "???";
1497 }
1498 AppendToBuffer(((regop <= 1) ? "%s%c " : "%s "),
1499 mnem,
1500 operand_size_code());
1501 data += PrintRightOperand(data);
1502 }
1503 break;
1504
1505 case 0xC7: // imm32, fall through
1506 case 0xC6: // imm8
1507 {
1508 bool is_byte = *data == 0xC6;
1509 data++;
Steve Block44f0eee2011-05-26 01:26:41 +01001510 if (is_byte) {
1511 AppendToBuffer("movb ");
1512 data += PrintRightByteOperand(data);
1513 int32_t imm = *data;
1514 AppendToBuffer(",0x%x", imm);
1515 data++;
1516 } else {
1517 AppendToBuffer("mov%c ", operand_size_code());
1518 data += PrintRightOperand(data);
1519 int32_t imm = *reinterpret_cast<int32_t*>(data);
1520 AppendToBuffer(",0x%x", imm);
1521 data += 4;
1522 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001523 }
1524 break;
1525
1526 case 0x80: {
1527 data++;
1528 AppendToBuffer("cmpb ");
Steve Block44f0eee2011-05-26 01:26:41 +01001529 data += PrintRightByteOperand(data);
Steve Blocka7e24c12009-10-30 11:49:00 +00001530 int32_t imm = *data;
1531 AppendToBuffer(",0x%x", imm);
1532 data++;
1533 }
1534 break;
1535
1536 case 0x88: // 8bit, fall through
1537 case 0x89: // 32bit
1538 {
1539 bool is_byte = *data == 0x88;
1540 int mod, regop, rm;
1541 data++;
1542 get_modrm(*data, &mod, &regop, &rm);
Steve Block44f0eee2011-05-26 01:26:41 +01001543 if (is_byte) {
1544 AppendToBuffer("movb ");
1545 data += PrintRightByteOperand(data);
1546 AppendToBuffer(",%s", NameOfByteCPURegister(regop));
1547 } else {
1548 AppendToBuffer("mov%c ", operand_size_code());
1549 data += PrintRightOperand(data);
1550 AppendToBuffer(",%s", NameOfCPURegister(regop));
1551 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001552 }
1553 break;
1554
1555 case 0x90:
1556 case 0x91:
1557 case 0x92:
1558 case 0x93:
1559 case 0x94:
1560 case 0x95:
1561 case 0x96:
1562 case 0x97: {
Steve Blockd0582a62009-12-15 09:54:21 +00001563 int reg = (*data & 0x7) | (rex_b() ? 8 : 0);
Steve Blocka7e24c12009-10-30 11:49:00 +00001564 if (reg == 0) {
1565 AppendToBuffer("nop"); // Common name for xchg rax,rax.
1566 } else {
1567 AppendToBuffer("xchg%c rax, %s",
1568 operand_size_code(),
1569 NameOfCPURegister(reg));
1570 }
Steve Blockd0582a62009-12-15 09:54:21 +00001571 data++;
Steve Blocka7e24c12009-10-30 11:49:00 +00001572 }
Steve Blockd0582a62009-12-15 09:54:21 +00001573 break;
Ben Murdoch8b112d22011-06-08 16:22:53 +01001574 case 0xB0:
1575 case 0xB1:
1576 case 0xB2:
1577 case 0xB3:
1578 case 0xB4:
1579 case 0xB5:
1580 case 0xB6:
1581 case 0xB7:
1582 case 0xB8:
1583 case 0xB9:
1584 case 0xBA:
1585 case 0xBB:
1586 case 0xBC:
1587 case 0xBD:
1588 case 0xBE:
1589 case 0xBF: {
1590 // mov reg8,imm8 or mov reg32,imm32
1591 byte opcode = *data;
1592 data++;
1593 bool is_32bit = (opcode >= 0xB8);
1594 int reg = (opcode & 0x7) | (rex_b() ? 8 : 0);
1595 if (is_32bit) {
1596 AppendToBuffer("mov%c %s, ",
1597 operand_size_code(),
1598 NameOfCPURegister(reg));
1599 data += PrintImmediate(data, DOUBLEWORD_SIZE);
1600 } else {
1601 AppendToBuffer("movb %s, ",
1602 NameOfByteCPURegister(reg));
1603 data += PrintImmediate(data, BYTE_SIZE);
1604 }
1605 break;
1606 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001607 case 0xFE: {
1608 data++;
1609 int mod, regop, rm;
1610 get_modrm(*data, &mod, &regop, &rm);
Kristian Monsen0d5e1162010-09-30 15:31:59 +01001611 if (regop == 1) {
1612 AppendToBuffer("decb ");
Steve Block44f0eee2011-05-26 01:26:41 +01001613 data += PrintRightByteOperand(data);
Steve Blocka7e24c12009-10-30 11:49:00 +00001614 } else {
1615 UnimplementedInstruction();
1616 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001617 break;
Ben Murdoch8b112d22011-06-08 16:22:53 +01001618 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001619 case 0x68:
1620 AppendToBuffer("push 0x%x", *reinterpret_cast<int32_t*>(data + 1));
1621 data += 5;
1622 break;
1623
1624 case 0x6A:
1625 AppendToBuffer("push 0x%x", *reinterpret_cast<int8_t*>(data + 1));
1626 data += 2;
1627 break;
1628
1629 case 0xA1: // Fall through.
1630 case 0xA3:
1631 switch (operand_size()) {
1632 case DOUBLEWORD_SIZE: {
1633 const char* memory_location = NameOfAddress(
1634 reinterpret_cast<byte*>(
1635 *reinterpret_cast<int32_t*>(data + 1)));
1636 if (*data == 0xA1) { // Opcode 0xA1
1637 AppendToBuffer("movzxlq rax,(%s)", memory_location);
1638 } else { // Opcode 0xA3
1639 AppendToBuffer("movzxlq (%s),rax", memory_location);
1640 }
1641 data += 5;
1642 break;
1643 }
1644 case QUADWORD_SIZE: {
1645 // New x64 instruction mov rax,(imm_64).
1646 const char* memory_location = NameOfAddress(
1647 *reinterpret_cast<byte**>(data + 1));
1648 if (*data == 0xA1) { // Opcode 0xA1
1649 AppendToBuffer("movq rax,(%s)", memory_location);
1650 } else { // Opcode 0xA3
1651 AppendToBuffer("movq (%s),rax", memory_location);
1652 }
1653 data += 9;
1654 break;
1655 }
1656 default:
1657 UnimplementedInstruction();
1658 data += 2;
1659 }
1660 break;
1661
1662 case 0xA8:
1663 AppendToBuffer("test al,0x%x", *reinterpret_cast<uint8_t*>(data + 1));
1664 data += 2;
1665 break;
1666
1667 case 0xA9: {
1668 int64_t value = 0;
1669 switch (operand_size()) {
1670 case WORD_SIZE:
1671 value = *reinterpret_cast<uint16_t*>(data + 1);
1672 data += 3;
1673 break;
1674 case DOUBLEWORD_SIZE:
1675 value = *reinterpret_cast<uint32_t*>(data + 1);
1676 data += 5;
1677 break;
1678 case QUADWORD_SIZE:
1679 value = *reinterpret_cast<int32_t*>(data + 1);
1680 data += 5;
1681 break;
1682 default:
1683 UNREACHABLE();
1684 }
1685 AppendToBuffer("test%c rax,0x%"V8_PTR_PREFIX"x",
1686 operand_size_code(),
1687 value);
1688 break;
1689 }
1690 case 0xD1: // fall through
1691 case 0xD3: // fall through
1692 case 0xC1:
1693 data += ShiftInstruction(data);
1694 break;
1695 case 0xD0: // fall through
1696 case 0xD2: // fall through
1697 case 0xC0:
1698 byte_size_operand_ = true;
1699 data += ShiftInstruction(data);
1700 break;
1701
1702 case 0xD9: // fall through
1703 case 0xDA: // fall through
1704 case 0xDB: // fall through
1705 case 0xDC: // fall through
1706 case 0xDD: // fall through
1707 case 0xDE: // fall through
1708 case 0xDF:
1709 data += FPUInstruction(data);
1710 break;
1711
1712 case 0xEB:
1713 data += JumpShort(data);
1714 break;
1715
Steve Blockd0582a62009-12-15 09:54:21 +00001716 case 0xF6:
1717 byte_size_operand_ = true; // fall through
Steve Blocka7e24c12009-10-30 11:49:00 +00001718 case 0xF7:
Steve Blockd0582a62009-12-15 09:54:21 +00001719 data += F6F7Instruction(data);
Steve Blocka7e24c12009-10-30 11:49:00 +00001720 break;
1721
1722 default:
1723 UnimplementedInstruction();
1724 data += 1;
1725 }
1726 } // !processed
1727
1728 if (tmp_buffer_pos_ < sizeof tmp_buffer_) {
1729 tmp_buffer_[tmp_buffer_pos_] = '\0';
1730 }
1731
Steve Blockd0582a62009-12-15 09:54:21 +00001732 int instr_len = static_cast<int>(data - instr);
Steve Blocka7e24c12009-10-30 11:49:00 +00001733 ASSERT(instr_len > 0); // Ensure progress.
1734
1735 int outp = 0;
1736 // Instruction bytes.
1737 for (byte* bp = instr; bp < data; bp++) {
1738 outp += v8::internal::OS::SNPrintF(out_buffer + outp, "%02x", *bp);
1739 }
1740 for (int i = 6 - instr_len; i >= 0; i--) {
1741 outp += v8::internal::OS::SNPrintF(out_buffer + outp, " ");
1742 }
1743
1744 outp += v8::internal::OS::SNPrintF(out_buffer + outp, " %s",
1745 tmp_buffer_.start());
1746 return instr_len;
1747}
1748
1749//------------------------------------------------------------------------------
1750
1751
1752static const char* cpu_regs[16] = {
1753 "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi",
1754 "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"
1755};
1756
1757
1758static const char* byte_cpu_regs[16] = {
1759 "al", "cl", "dl", "bl", "spl", "bpl", "sil", "dil",
1760 "r8l", "r9l", "r10l", "r11l", "r12l", "r13l", "r14l", "r15l"
1761};
1762
1763
1764static const char* xmm_regs[16] = {
1765 "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7",
1766 "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15"
1767};
1768
1769
1770const char* NameConverter::NameOfAddress(byte* addr) const {
Steve Block44f0eee2011-05-26 01:26:41 +01001771 v8::internal::OS::SNPrintF(tmp_buffer_, "%p", addr);
1772 return tmp_buffer_.start();
Steve Blocka7e24c12009-10-30 11:49:00 +00001773}
1774
1775
1776const char* NameConverter::NameOfConstant(byte* addr) const {
1777 return NameOfAddress(addr);
1778}
1779
1780
1781const char* NameConverter::NameOfCPURegister(int reg) const {
1782 if (0 <= reg && reg < 16)
1783 return cpu_regs[reg];
1784 return "noreg";
1785}
1786
1787
1788const char* NameConverter::NameOfByteCPURegister(int reg) const {
1789 if (0 <= reg && reg < 16)
1790 return byte_cpu_regs[reg];
1791 return "noreg";
1792}
1793
1794
1795const char* NameConverter::NameOfXMMRegister(int reg) const {
1796 if (0 <= reg && reg < 16)
1797 return xmm_regs[reg];
1798 return "noxmmreg";
1799}
1800
1801
1802const char* NameConverter::NameInCode(byte* addr) const {
1803 // X64 does not embed debug strings at the moment.
1804 UNREACHABLE();
1805 return "";
1806}
1807
1808//------------------------------------------------------------------------------
1809
1810Disassembler::Disassembler(const NameConverter& converter)
1811 : converter_(converter) { }
1812
1813Disassembler::~Disassembler() { }
1814
1815
1816int Disassembler::InstructionDecode(v8::internal::Vector<char> buffer,
1817 byte* instruction) {
1818 DisassemblerX64 d(converter_, CONTINUE_ON_UNIMPLEMENTED_OPCODE);
1819 return d.InstructionDecode(buffer, instruction);
1820}
1821
1822
1823// The X64 assembler does not use constant pools.
1824int Disassembler::ConstantPoolSizeAt(byte* instruction) {
1825 return -1;
1826}
1827
1828
1829void Disassembler::Disassemble(FILE* f, byte* begin, byte* end) {
1830 NameConverter converter;
1831 Disassembler d(converter);
1832 for (byte* pc = begin; pc < end;) {
1833 v8::internal::EmbeddedVector<char, 128> buffer;
1834 buffer[0] = '\0';
1835 byte* prev_pc = pc;
1836 pc += d.InstructionDecode(buffer, pc);
1837 fprintf(f, "%p", prev_pc);
1838 fprintf(f, " ");
1839
1840 for (byte* bp = prev_pc; bp < pc; bp++) {
1841 fprintf(f, "%02x", *bp);
1842 }
Steve Blockd0582a62009-12-15 09:54:21 +00001843 for (int i = 6 - static_cast<int>(pc - prev_pc); i >= 0; i--) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001844 fprintf(f, " ");
1845 }
1846 fprintf(f, " %s\n", buffer.start());
1847 }
1848}
1849
1850} // namespace disasm
Leon Clarkef7060e22010-06-03 12:02:55 +01001851
1852#endif // V8_TARGET_ARCH_X64