blob: a936277b2fc1fe29f504f02335c81113a65ae364 [file] [log] [blame]
Ben Murdoch257744e2011-11-30 15:57:28 +00001// 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_IA32)
35
Steve Blocka7e24c12009-10-30 11:49:00 +000036#include "disasm.h"
37
38namespace disasm {
39
40enum OperandOrder {
41 UNSET_OP_ORDER = 0,
42 REG_OPER_OP_ORDER,
43 OPER_REG_OP_ORDER
44};
45
46
47//------------------------------------------------------------------
48// Tables
49//------------------------------------------------------------------
50struct ByteMnemonic {
51 int b; // -1 terminates, otherwise must be in range (0..255)
52 const char* mnem;
53 OperandOrder op_order_;
54};
55
56
Ben Murdoch69a99ed2011-11-30 16:03:39 +000057static const ByteMnemonic two_operands_instr[] = {
Steve Blocka7e24c12009-10-30 11:49:00 +000058 {0x03, "add", REG_OPER_OP_ORDER},
Steve Blocka7e24c12009-10-30 11:49:00 +000059 {0x09, "or", OPER_REG_OP_ORDER},
60 {0x0B, "or", REG_OPER_OP_ORDER},
61 {0x1B, "sbb", REG_OPER_OP_ORDER},
Leon Clarked91b9f72010-01-27 17:25:45 +000062 {0x21, "and", OPER_REG_OP_ORDER},
63 {0x23, "and", REG_OPER_OP_ORDER},
Steve Blocka7e24c12009-10-30 11:49:00 +000064 {0x29, "sub", OPER_REG_OP_ORDER},
Leon Clarkee46be812010-01-19 14:06:41 +000065 {0x2A, "subb", REG_OPER_OP_ORDER},
Steve Blocka7e24c12009-10-30 11:49:00 +000066 {0x2B, "sub", REG_OPER_OP_ORDER},
Leon Clarkeeab96aa2010-01-27 16:31:12 +000067 {0x31, "xor", OPER_REG_OP_ORDER},
68 {0x33, "xor", REG_OPER_OP_ORDER},
Leon Clarked91b9f72010-01-27 17:25:45 +000069 {0x38, "cmpb", OPER_REG_OP_ORDER},
70 {0x3A, "cmpb", REG_OPER_OP_ORDER},
71 {0x3B, "cmp", REG_OPER_OP_ORDER},
72 {0x84, "test_b", REG_OPER_OP_ORDER},
73 {0x85, "test", REG_OPER_OP_ORDER},
Steve Blocka7e24c12009-10-30 11:49:00 +000074 {0x87, "xchg", REG_OPER_OP_ORDER},
75 {0x8A, "mov_b", REG_OPER_OP_ORDER},
76 {0x8B, "mov", REG_OPER_OP_ORDER},
Leon Clarked91b9f72010-01-27 17:25:45 +000077 {0x8D, "lea", REG_OPER_OP_ORDER},
Steve Blocka7e24c12009-10-30 11:49:00 +000078 {-1, "", UNSET_OP_ORDER}
79};
80
81
Ben Murdoch69a99ed2011-11-30 16:03:39 +000082static const ByteMnemonic zero_operands_instr[] = {
Steve Blocka7e24c12009-10-30 11:49:00 +000083 {0xC3, "ret", UNSET_OP_ORDER},
84 {0xC9, "leave", UNSET_OP_ORDER},
85 {0x90, "nop", UNSET_OP_ORDER},
86 {0xF4, "hlt", UNSET_OP_ORDER},
87 {0xCC, "int3", UNSET_OP_ORDER},
88 {0x60, "pushad", UNSET_OP_ORDER},
89 {0x61, "popad", UNSET_OP_ORDER},
90 {0x9C, "pushfd", UNSET_OP_ORDER},
91 {0x9D, "popfd", UNSET_OP_ORDER},
92 {0x9E, "sahf", UNSET_OP_ORDER},
93 {0x99, "cdq", UNSET_OP_ORDER},
94 {0x9B, "fwait", UNSET_OP_ORDER},
Steve Block6ded16b2010-05-10 14:33:55 +010095 {0xFC, "cld", UNSET_OP_ORDER},
Leon Clarkef7060e22010-06-03 12:02:55 +010096 {0xAB, "stos", UNSET_OP_ORDER},
Steve Blocka7e24c12009-10-30 11:49:00 +000097 {-1, "", UNSET_OP_ORDER}
98};
99
100
Ben Murdoch69a99ed2011-11-30 16:03:39 +0000101static const ByteMnemonic call_jump_instr[] = {
Steve Blocka7e24c12009-10-30 11:49:00 +0000102 {0xE8, "call", UNSET_OP_ORDER},
103 {0xE9, "jmp", UNSET_OP_ORDER},
104 {-1, "", UNSET_OP_ORDER}
105};
106
107
Ben Murdoch69a99ed2011-11-30 16:03:39 +0000108static const ByteMnemonic short_immediate_instr[] = {
Steve Blocka7e24c12009-10-30 11:49:00 +0000109 {0x05, "add", UNSET_OP_ORDER},
110 {0x0D, "or", UNSET_OP_ORDER},
111 {0x15, "adc", UNSET_OP_ORDER},
112 {0x25, "and", UNSET_OP_ORDER},
113 {0x2D, "sub", UNSET_OP_ORDER},
114 {0x35, "xor", UNSET_OP_ORDER},
115 {0x3D, "cmp", UNSET_OP_ORDER},
116 {-1, "", UNSET_OP_ORDER}
117};
118
119
Ben Murdoch69a99ed2011-11-30 16:03:39 +0000120static const char* const jump_conditional_mnem[] = {
Steve Blocka7e24c12009-10-30 11:49:00 +0000121 /*0*/ "jo", "jno", "jc", "jnc",
122 /*4*/ "jz", "jnz", "jna", "ja",
123 /*8*/ "js", "jns", "jpe", "jpo",
124 /*12*/ "jl", "jnl", "jng", "jg"
125};
126
127
Ben Murdoch69a99ed2011-11-30 16:03:39 +0000128static const char* const set_conditional_mnem[] = {
Steve Blocka7e24c12009-10-30 11:49:00 +0000129 /*0*/ "seto", "setno", "setc", "setnc",
130 /*4*/ "setz", "setnz", "setna", "seta",
131 /*8*/ "sets", "setns", "setpe", "setpo",
132 /*12*/ "setl", "setnl", "setng", "setg"
133};
134
135
Ben Murdoch69a99ed2011-11-30 16:03:39 +0000136static const char* const conditional_move_mnem[] = {
Steve Block3ce2e202009-11-05 08:53:23 +0000137 /*0*/ "cmovo", "cmovno", "cmovc", "cmovnc",
138 /*4*/ "cmovz", "cmovnz", "cmovna", "cmova",
139 /*8*/ "cmovs", "cmovns", "cmovpe", "cmovpo",
140 /*12*/ "cmovl", "cmovnl", "cmovng", "cmovg"
141};
142
143
Steve Blocka7e24c12009-10-30 11:49:00 +0000144enum InstructionType {
145 NO_INSTR,
146 ZERO_OPERANDS_INSTR,
147 TWO_OPERANDS_INSTR,
148 JUMP_CONDITIONAL_SHORT_INSTR,
149 REGISTER_INSTR,
150 MOVE_REG_INSTR,
151 CALL_JUMP_INSTR,
152 SHORT_IMMEDIATE_INSTR
153};
154
155
156struct InstructionDesc {
157 const char* mnem;
158 InstructionType type;
159 OperandOrder op_order_;
160};
161
162
163class InstructionTable {
164 public:
165 InstructionTable();
166 const InstructionDesc& Get(byte x) const { return instructions_[x]; }
167
168 private:
169 InstructionDesc instructions_[256];
170 void Clear();
171 void Init();
Ben Murdoch69a99ed2011-11-30 16:03:39 +0000172 void CopyTable(const ByteMnemonic bm[], InstructionType type);
Steve Blocka7e24c12009-10-30 11:49:00 +0000173 void SetTableRange(InstructionType type,
174 byte start,
175 byte end,
176 const char* mnem);
177 void AddJumpConditionalShort();
178};
179
180
181InstructionTable::InstructionTable() {
182 Clear();
183 Init();
184}
185
186
187void InstructionTable::Clear() {
188 for (int i = 0; i < 256; i++) {
189 instructions_[i].mnem = "";
190 instructions_[i].type = NO_INSTR;
191 instructions_[i].op_order_ = UNSET_OP_ORDER;
192 }
193}
194
195
196void InstructionTable::Init() {
197 CopyTable(two_operands_instr, TWO_OPERANDS_INSTR);
198 CopyTable(zero_operands_instr, ZERO_OPERANDS_INSTR);
199 CopyTable(call_jump_instr, CALL_JUMP_INSTR);
200 CopyTable(short_immediate_instr, SHORT_IMMEDIATE_INSTR);
201 AddJumpConditionalShort();
202 SetTableRange(REGISTER_INSTR, 0x40, 0x47, "inc");
203 SetTableRange(REGISTER_INSTR, 0x48, 0x4F, "dec");
204 SetTableRange(REGISTER_INSTR, 0x50, 0x57, "push");
205 SetTableRange(REGISTER_INSTR, 0x58, 0x5F, "pop");
206 SetTableRange(REGISTER_INSTR, 0x91, 0x97, "xchg eax,"); // 0x90 is nop.
207 SetTableRange(MOVE_REG_INSTR, 0xB8, 0xBF, "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 id->op_order_ = bm[i].op_order_;
Steve Blockd0582a62009-12-15 09:54:21 +0000217 ASSERT_EQ(NO_INSTR, id->type); // Information not already entered.
Steve Blocka7e24c12009-10-30 11:49:00 +0000218 id->type = type;
219 }
220}
221
222
223void InstructionTable::SetTableRange(InstructionType type,
224 byte start,
225 byte end,
226 const char* mnem) {
227 for (byte b = start; b <= end; b++) {
228 InstructionDesc* id = &instructions_[b];
Steve Blockd0582a62009-12-15 09:54:21 +0000229 ASSERT_EQ(NO_INSTR, id->type); // Information not already entered.
Steve Blocka7e24c12009-10-30 11:49:00 +0000230 id->mnem = mnem;
231 id->type = type;
232 }
233}
234
235
236void InstructionTable::AddJumpConditionalShort() {
237 for (byte b = 0x70; b <= 0x7F; b++) {
238 InstructionDesc* id = &instructions_[b];
Steve Blockd0582a62009-12-15 09:54:21 +0000239 ASSERT_EQ(NO_INSTR, id->type); // Information not already entered.
Steve Blocka7e24c12009-10-30 11:49:00 +0000240 id->mnem = jump_conditional_mnem[b & 0x0F];
241 id->type = JUMP_CONDITIONAL_SHORT_INSTR;
242 }
243}
244
245
246static InstructionTable instruction_table;
247
248
249// The IA32 disassembler implementation.
250class DisassemblerIA32 {
251 public:
252 DisassemblerIA32(const NameConverter& converter,
253 bool abort_on_unimplemented = true)
254 : converter_(converter),
255 tmp_buffer_pos_(0),
256 abort_on_unimplemented_(abort_on_unimplemented) {
257 tmp_buffer_[0] = '\0';
258 }
259
260 virtual ~DisassemblerIA32() {}
261
262 // Writes one disassembled instruction into 'buffer' (0-terminated).
263 // Returns the length of the disassembled machine instruction in bytes.
264 int InstructionDecode(v8::internal::Vector<char> buffer, byte* instruction);
265
266 private:
267 const NameConverter& converter_;
268 v8::internal::EmbeddedVector<char, 128> tmp_buffer_;
269 unsigned int tmp_buffer_pos_;
270 bool abort_on_unimplemented_;
271
272
273 enum {
274 eax = 0,
275 ecx = 1,
276 edx = 2,
277 ebx = 3,
278 esp = 4,
279 ebp = 5,
280 esi = 6,
281 edi = 7
282 };
283
284
Steve Blockd0582a62009-12-15 09:54:21 +0000285 enum ShiftOpcodeExtension {
286 kROL = 0,
287 kROR = 1,
288 kRCL = 2,
289 kRCR = 3,
290 kSHL = 4,
291 KSHR = 5,
292 kSAR = 7
293 };
294
295
Steve Blocka7e24c12009-10-30 11:49:00 +0000296 const char* NameOfCPURegister(int reg) const {
297 return converter_.NameOfCPURegister(reg);
298 }
299
300
301 const char* NameOfByteCPURegister(int reg) const {
302 return converter_.NameOfByteCPURegister(reg);
303 }
304
305
306 const char* NameOfXMMRegister(int reg) const {
307 return converter_.NameOfXMMRegister(reg);
308 }
309
310
311 const char* NameOfAddress(byte* addr) const {
312 return converter_.NameOfAddress(addr);
313 }
314
315
316 // Disassembler helper functions.
317 static void get_modrm(byte data, int* mod, int* regop, int* rm) {
318 *mod = (data >> 6) & 3;
319 *regop = (data & 0x38) >> 3;
320 *rm = data & 7;
321 }
322
323
324 static void get_sib(byte data, int* scale, int* index, int* base) {
325 *scale = (data >> 6) & 3;
326 *index = (data >> 3) & 7;
327 *base = data & 7;
328 }
329
330 typedef const char* (DisassemblerIA32::*RegisterNameMapping)(int reg) const;
331
332 int PrintRightOperandHelper(byte* modrmp, RegisterNameMapping register_name);
333 int PrintRightOperand(byte* modrmp);
334 int PrintRightByteOperand(byte* modrmp);
Steve Block44f0eee2011-05-26 01:26:41 +0100335 int PrintRightXMMOperand(byte* modrmp);
Steve Blocka7e24c12009-10-30 11:49:00 +0000336 int PrintOperands(const char* mnem, OperandOrder op_order, byte* data);
337 int PrintImmediateOp(byte* data);
338 int F7Instruction(byte* data);
339 int D1D3C1Instruction(byte* data);
340 int JumpShort(byte* data);
341 int JumpConditional(byte* data, const char* comment);
342 int JumpConditionalShort(byte* data, const char* comment);
343 int SetCC(byte* data);
Steve Block3ce2e202009-11-05 08:53:23 +0000344 int CMov(byte* data);
Steve Blocka7e24c12009-10-30 11:49:00 +0000345 int FPUInstruction(byte* data);
Steve Blockd0582a62009-12-15 09:54:21 +0000346 int MemoryFPUInstruction(int escape_opcode, int regop, byte* modrm_start);
347 int RegisterFPUInstruction(int escape_opcode, byte modrm_byte);
Steve Blocka7e24c12009-10-30 11:49:00 +0000348 void AppendToBuffer(const char* format, ...);
349
350
351 void UnimplementedInstruction() {
352 if (abort_on_unimplemented_) {
353 UNIMPLEMENTED();
354 } else {
355 AppendToBuffer("'Unimplemented Instruction'");
356 }
357 }
358};
359
360
361void DisassemblerIA32::AppendToBuffer(const char* format, ...) {
362 v8::internal::Vector<char> buf = tmp_buffer_ + tmp_buffer_pos_;
363 va_list args;
364 va_start(args, format);
365 int result = v8::internal::OS::VSNPrintF(buf, format, args);
366 va_end(args);
367 tmp_buffer_pos_ += result;
368}
369
370int DisassemblerIA32::PrintRightOperandHelper(
371 byte* modrmp,
Steve Block44f0eee2011-05-26 01:26:41 +0100372 RegisterNameMapping direct_register_name) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000373 int mod, regop, rm;
374 get_modrm(*modrmp, &mod, &regop, &rm);
Steve Block44f0eee2011-05-26 01:26:41 +0100375 RegisterNameMapping register_name = (mod == 3) ? direct_register_name :
376 &DisassemblerIA32::NameOfCPURegister;
Steve Blocka7e24c12009-10-30 11:49:00 +0000377 switch (mod) {
378 case 0:
379 if (rm == ebp) {
380 int32_t disp = *reinterpret_cast<int32_t*>(modrmp+1);
381 AppendToBuffer("[0x%x]", disp);
382 return 5;
383 } else if (rm == esp) {
384 byte sib = *(modrmp + 1);
385 int scale, index, base;
386 get_sib(sib, &scale, &index, &base);
387 if (index == esp && base == esp && scale == 0 /*times_1*/) {
388 AppendToBuffer("[%s]", (this->*register_name)(rm));
389 return 2;
390 } else if (base == ebp) {
391 int32_t disp = *reinterpret_cast<int32_t*>(modrmp + 2);
392 AppendToBuffer("[%s*%d+0x%x]",
393 (this->*register_name)(index),
394 1 << scale,
395 disp);
396 return 6;
397 } else if (index != esp && base != ebp) {
398 // [base+index*scale]
399 AppendToBuffer("[%s+%s*%d]",
400 (this->*register_name)(base),
401 (this->*register_name)(index),
402 1 << scale);
403 return 2;
404 } else {
405 UnimplementedInstruction();
406 return 1;
407 }
408 } else {
409 AppendToBuffer("[%s]", (this->*register_name)(rm));
410 return 1;
411 }
412 break;
413 case 1: // fall through
414 case 2:
415 if (rm == esp) {
416 byte sib = *(modrmp + 1);
417 int scale, index, base;
418 get_sib(sib, &scale, &index, &base);
419 int disp =
420 mod == 2 ? *reinterpret_cast<int32_t*>(modrmp + 2) : *(modrmp + 2);
421 if (index == base && index == rm /*esp*/ && scale == 0 /*times_1*/) {
422 AppendToBuffer("[%s+0x%x]", (this->*register_name)(rm), disp);
423 } else {
424 AppendToBuffer("[%s+%s*%d+0x%x]",
425 (this->*register_name)(base),
426 (this->*register_name)(index),
427 1 << scale,
428 disp);
429 }
430 return mod == 2 ? 6 : 3;
431 } else {
432 // No sib.
433 int disp =
434 mod == 2 ? *reinterpret_cast<int32_t*>(modrmp + 1) : *(modrmp + 1);
435 AppendToBuffer("[%s+0x%x]", (this->*register_name)(rm), disp);
436 return mod == 2 ? 5 : 2;
437 }
438 break;
439 case 3:
440 AppendToBuffer("%s", (this->*register_name)(rm));
441 return 1;
442 default:
443 UnimplementedInstruction();
444 return 1;
445 }
446 UNREACHABLE();
447}
448
449
450int DisassemblerIA32::PrintRightOperand(byte* modrmp) {
451 return PrintRightOperandHelper(modrmp, &DisassemblerIA32::NameOfCPURegister);
452}
453
454
455int DisassemblerIA32::PrintRightByteOperand(byte* modrmp) {
456 return PrintRightOperandHelper(modrmp,
457 &DisassemblerIA32::NameOfByteCPURegister);
458}
459
460
Steve Block44f0eee2011-05-26 01:26:41 +0100461int DisassemblerIA32::PrintRightXMMOperand(byte* modrmp) {
462 return PrintRightOperandHelper(modrmp,
463 &DisassemblerIA32::NameOfXMMRegister);
464}
465
466
Steve Blocka7e24c12009-10-30 11:49:00 +0000467// Returns number of bytes used including the current *data.
468// Writes instruction's mnemonic, left and right operands to 'tmp_buffer_'.
469int DisassemblerIA32::PrintOperands(const char* mnem,
470 OperandOrder op_order,
471 byte* data) {
472 byte modrm = *data;
473 int mod, regop, rm;
474 get_modrm(modrm, &mod, &regop, &rm);
475 int advance = 0;
476 switch (op_order) {
477 case REG_OPER_OP_ORDER: {
478 AppendToBuffer("%s %s,", mnem, NameOfCPURegister(regop));
479 advance = PrintRightOperand(data);
480 break;
481 }
482 case OPER_REG_OP_ORDER: {
483 AppendToBuffer("%s ", mnem);
484 advance = PrintRightOperand(data);
485 AppendToBuffer(",%s", NameOfCPURegister(regop));
486 break;
487 }
488 default:
489 UNREACHABLE();
490 break;
491 }
492 return advance;
493}
494
495
496// Returns number of bytes used by machine instruction, including *data byte.
497// Writes immediate instructions to 'tmp_buffer_'.
498int DisassemblerIA32::PrintImmediateOp(byte* data) {
499 bool sign_extension_bit = (*data & 0x02) != 0;
500 byte modrm = *(data+1);
501 int mod, regop, rm;
502 get_modrm(modrm, &mod, &regop, &rm);
503 const char* mnem = "Imm???";
504 switch (regop) {
505 case 0: mnem = "add"; break;
506 case 1: mnem = "or"; break;
507 case 2: mnem = "adc"; break;
508 case 4: mnem = "and"; break;
509 case 5: mnem = "sub"; break;
510 case 6: mnem = "xor"; break;
511 case 7: mnem = "cmp"; break;
512 default: UnimplementedInstruction();
513 }
514 AppendToBuffer("%s ", mnem);
515 int count = PrintRightOperand(data+1);
516 if (sign_extension_bit) {
517 AppendToBuffer(",0x%x", *(data + 1 + count));
518 return 1 + count + 1 /*int8*/;
519 } else {
520 AppendToBuffer(",0x%x", *reinterpret_cast<int32_t*>(data + 1 + count));
521 return 1 + count + 4 /*int32_t*/;
522 }
523}
524
525
526// Returns number of bytes used, including *data.
527int DisassemblerIA32::F7Instruction(byte* data) {
Steve Blockd0582a62009-12-15 09:54:21 +0000528 ASSERT_EQ(0xF7, *data);
Steve Blocka7e24c12009-10-30 11:49:00 +0000529 byte modrm = *(data+1);
530 int mod, regop, rm;
531 get_modrm(modrm, &mod, &regop, &rm);
532 if (mod == 3 && regop != 0) {
533 const char* mnem = NULL;
534 switch (regop) {
535 case 2: mnem = "not"; break;
536 case 3: mnem = "neg"; break;
537 case 4: mnem = "mul"; break;
538 case 7: mnem = "idiv"; break;
539 default: UnimplementedInstruction();
540 }
541 AppendToBuffer("%s %s", mnem, NameOfCPURegister(rm));
542 return 2;
543 } else if (mod == 3 && regop == eax) {
544 int32_t imm = *reinterpret_cast<int32_t*>(data+2);
545 AppendToBuffer("test %s,0x%x", NameOfCPURegister(rm), imm);
546 return 6;
547 } else if (regop == eax) {
548 AppendToBuffer("test ");
549 int count = PrintRightOperand(data+1);
550 int32_t imm = *reinterpret_cast<int32_t*>(data+1+count);
551 AppendToBuffer(",0x%x", imm);
552 return 1+count+4 /*int32_t*/;
553 } else {
554 UnimplementedInstruction();
555 return 2;
556 }
557}
558
559int DisassemblerIA32::D1D3C1Instruction(byte* data) {
560 byte op = *data;
Steve Blockd0582a62009-12-15 09:54:21 +0000561 ASSERT(op == 0xD1 || op == 0xD3 || op == 0xC1);
Steve Blocka7e24c12009-10-30 11:49:00 +0000562 byte modrm = *(data+1);
563 int mod, regop, rm;
564 get_modrm(modrm, &mod, &regop, &rm);
565 int imm8 = -1;
566 int num_bytes = 2;
567 if (mod == 3) {
568 const char* mnem = NULL;
Steve Blockd0582a62009-12-15 09:54:21 +0000569 switch (regop) {
570 case kROL: mnem = "rol"; break;
571 case kROR: mnem = "ror"; break;
572 case kRCL: mnem = "rcl"; break;
Iain Merrick75681382010-08-19 15:07:18 +0100573 case kRCR: mnem = "rcr"; break;
Steve Blockd0582a62009-12-15 09:54:21 +0000574 case kSHL: mnem = "shl"; break;
575 case KSHR: mnem = "shr"; break;
576 case kSAR: mnem = "sar"; break;
577 default: UnimplementedInstruction();
578 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000579 if (op == 0xD1) {
580 imm8 = 1;
Steve Blocka7e24c12009-10-30 11:49:00 +0000581 } else if (op == 0xC1) {
582 imm8 = *(data+2);
583 num_bytes = 3;
Steve Blocka7e24c12009-10-30 11:49:00 +0000584 } else if (op == 0xD3) {
Steve Blockd0582a62009-12-15 09:54:21 +0000585 // Shift/rotate by cl.
Steve Blocka7e24c12009-10-30 11:49:00 +0000586 }
Steve Blockd0582a62009-12-15 09:54:21 +0000587 ASSERT_NE(NULL, mnem);
Steve Blocka7e24c12009-10-30 11:49:00 +0000588 AppendToBuffer("%s %s,", mnem, NameOfCPURegister(rm));
589 if (imm8 > 0) {
590 AppendToBuffer("%d", imm8);
591 } else {
592 AppendToBuffer("cl");
593 }
594 } else {
595 UnimplementedInstruction();
596 }
597 return num_bytes;
598}
599
600
601// Returns number of bytes used, including *data.
602int DisassemblerIA32::JumpShort(byte* data) {
Steve Blockd0582a62009-12-15 09:54:21 +0000603 ASSERT_EQ(0xEB, *data);
Steve Blocka7e24c12009-10-30 11:49:00 +0000604 byte b = *(data+1);
605 byte* dest = data + static_cast<int8_t>(b) + 2;
606 AppendToBuffer("jmp %s", NameOfAddress(dest));
607 return 2;
608}
609
610
611// Returns number of bytes used, including *data.
612int DisassemblerIA32::JumpConditional(byte* data, const char* comment) {
Steve Blockd0582a62009-12-15 09:54:21 +0000613 ASSERT_EQ(0x0F, *data);
Steve Blocka7e24c12009-10-30 11:49:00 +0000614 byte cond = *(data+1) & 0x0F;
615 byte* dest = data + *reinterpret_cast<int32_t*>(data+2) + 6;
616 const char* mnem = jump_conditional_mnem[cond];
617 AppendToBuffer("%s %s", mnem, NameOfAddress(dest));
618 if (comment != NULL) {
619 AppendToBuffer(", %s", comment);
620 }
621 return 6; // includes 0x0F
622}
623
624
625// Returns number of bytes used, including *data.
626int DisassemblerIA32::JumpConditionalShort(byte* data, const char* comment) {
627 byte cond = *data & 0x0F;
628 byte b = *(data+1);
629 byte* dest = data + static_cast<int8_t>(b) + 2;
630 const char* mnem = jump_conditional_mnem[cond];
631 AppendToBuffer("%s %s", mnem, NameOfAddress(dest));
632 if (comment != NULL) {
633 AppendToBuffer(", %s", comment);
634 }
635 return 2;
636}
637
638
639// Returns number of bytes used, including *data.
640int DisassemblerIA32::SetCC(byte* data) {
Steve Blockd0582a62009-12-15 09:54:21 +0000641 ASSERT_EQ(0x0F, *data);
Steve Blocka7e24c12009-10-30 11:49:00 +0000642 byte cond = *(data+1) & 0x0F;
643 const char* mnem = set_conditional_mnem[cond];
644 AppendToBuffer("%s ", mnem);
645 PrintRightByteOperand(data+2);
Steve Blockd0582a62009-12-15 09:54:21 +0000646 return 3; // Includes 0x0F.
Steve Blocka7e24c12009-10-30 11:49:00 +0000647}
648
649
650// Returns number of bytes used, including *data.
Steve Block3ce2e202009-11-05 08:53:23 +0000651int DisassemblerIA32::CMov(byte* data) {
Steve Blockd0582a62009-12-15 09:54:21 +0000652 ASSERT_EQ(0x0F, *data);
Steve Block3ce2e202009-11-05 08:53:23 +0000653 byte cond = *(data + 1) & 0x0F;
654 const char* mnem = conditional_move_mnem[cond];
655 int op_size = PrintOperands(mnem, REG_OPER_OP_ORDER, data + 2);
656 return 2 + op_size; // includes 0x0F
657}
658
659
660// Returns number of bytes used, including *data.
Steve Blocka7e24c12009-10-30 11:49:00 +0000661int DisassemblerIA32::FPUInstruction(byte* data) {
Steve Blockd0582a62009-12-15 09:54:21 +0000662 byte escape_opcode = *data;
663 ASSERT_EQ(0xD8, escape_opcode & 0xF8);
664 byte modrm_byte = *(data+1);
665
666 if (modrm_byte >= 0xC0) {
667 return RegisterFPUInstruction(escape_opcode, modrm_byte);
668 } else {
669 return MemoryFPUInstruction(escape_opcode, modrm_byte, data+1);
Steve Blocka7e24c12009-10-30 11:49:00 +0000670 }
Steve Blockd0582a62009-12-15 09:54:21 +0000671}
672
673int DisassemblerIA32::MemoryFPUInstruction(int escape_opcode,
674 int modrm_byte,
675 byte* modrm_start) {
676 const char* mnem = "?";
677 int regop = (modrm_byte >> 3) & 0x7; // reg/op field of modrm byte.
678 switch (escape_opcode) {
679 case 0xD9: switch (regop) {
680 case 0: mnem = "fld_s"; break;
681 case 3: mnem = "fstp_s"; break;
682 case 7: mnem = "fstcw"; break;
683 default: UnimplementedInstruction();
684 }
685 break;
686
687 case 0xDB: switch (regop) {
688 case 0: mnem = "fild_s"; break;
689 case 1: mnem = "fisttp_s"; break;
690 case 2: mnem = "fist_s"; break;
691 case 3: mnem = "fistp_s"; break;
692 default: UnimplementedInstruction();
693 }
694 break;
695
696 case 0xDD: switch (regop) {
697 case 0: mnem = "fld_d"; break;
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100698 case 1: mnem = "fisttp_d"; break;
699 case 2: mnem = "fst_d"; break;
Steve Blockd0582a62009-12-15 09:54:21 +0000700 case 3: mnem = "fstp_d"; break;
701 default: UnimplementedInstruction();
702 }
703 break;
704
705 case 0xDF: switch (regop) {
706 case 5: mnem = "fild_d"; break;
707 case 7: mnem = "fistp_d"; break;
708 default: UnimplementedInstruction();
709 }
710 break;
711
712 default: UnimplementedInstruction();
713 }
714 AppendToBuffer("%s ", mnem);
715 int count = PrintRightOperand(modrm_start);
716 return count + 1;
717}
718
719int DisassemblerIA32::RegisterFPUInstruction(int escape_opcode,
720 byte modrm_byte) {
721 bool has_register = false; // Is the FPU register encoded in modrm_byte?
722 const char* mnem = "?";
723
724 switch (escape_opcode) {
725 case 0xD8:
726 UnimplementedInstruction();
727 break;
728
729 case 0xD9:
730 switch (modrm_byte & 0xF8) {
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100731 case 0xC0:
732 mnem = "fld";
733 has_register = true;
734 break;
Steve Blockd0582a62009-12-15 09:54:21 +0000735 case 0xC8:
736 mnem = "fxch";
737 has_register = true;
738 break;
739 default:
740 switch (modrm_byte) {
741 case 0xE0: mnem = "fchs"; break;
742 case 0xE1: mnem = "fabs"; break;
743 case 0xE4: mnem = "ftst"; break;
744 case 0xE8: mnem = "fld1"; break;
Andrei Popescu402d9372010-02-26 13:31:12 +0000745 case 0xEB: mnem = "fldpi"; break;
Ben Murdochb0fe1622011-05-05 13:52:32 +0100746 case 0xED: mnem = "fldln2"; break;
Steve Blockd0582a62009-12-15 09:54:21 +0000747 case 0xEE: mnem = "fldz"; break;
Ben Murdochb0fe1622011-05-05 13:52:32 +0100748 case 0xF1: mnem = "fyl2x"; break;
Steve Blockd0582a62009-12-15 09:54:21 +0000749 case 0xF5: mnem = "fprem1"; break;
750 case 0xF7: mnem = "fincstp"; break;
751 case 0xF8: mnem = "fprem"; break;
752 case 0xFE: mnem = "fsin"; break;
753 case 0xFF: mnem = "fcos"; break;
754 default: UnimplementedInstruction();
755 }
756 }
757 break;
758
759 case 0xDA:
760 if (modrm_byte == 0xE9) {
761 mnem = "fucompp";
762 } else {
763 UnimplementedInstruction();
764 }
765 break;
766
767 case 0xDB:
768 if ((modrm_byte & 0xF8) == 0xE8) {
769 mnem = "fucomi";
770 has_register = true;
771 } else if (modrm_byte == 0xE2) {
772 mnem = "fclex";
773 } else {
774 UnimplementedInstruction();
775 }
776 break;
777
778 case 0xDC:
779 has_register = true;
780 switch (modrm_byte & 0xF8) {
781 case 0xC0: mnem = "fadd"; break;
782 case 0xE8: mnem = "fsub"; break;
783 case 0xC8: mnem = "fmul"; break;
784 case 0xF8: mnem = "fdiv"; break;
785 default: UnimplementedInstruction();
786 }
787 break;
788
789 case 0xDD:
790 has_register = true;
791 switch (modrm_byte & 0xF8) {
792 case 0xC0: mnem = "ffree"; break;
793 case 0xD8: mnem = "fstp"; break;
794 default: UnimplementedInstruction();
795 }
796 break;
797
798 case 0xDE:
799 if (modrm_byte == 0xD9) {
800 mnem = "fcompp";
801 } else {
802 has_register = true;
803 switch (modrm_byte & 0xF8) {
804 case 0xC0: mnem = "faddp"; break;
805 case 0xE8: mnem = "fsubp"; break;
806 case 0xC8: mnem = "fmulp"; break;
807 case 0xF8: mnem = "fdivp"; break;
808 default: UnimplementedInstruction();
809 }
810 }
811 break;
812
813 case 0xDF:
814 if (modrm_byte == 0xE0) {
815 mnem = "fnstsw_ax";
816 } else if ((modrm_byte & 0xF8) == 0xE8) {
817 mnem = "fucomip";
818 has_register = true;
819 }
820 break;
821
822 default: UnimplementedInstruction();
823 }
824
825 if (has_register) {
826 AppendToBuffer("%s st%d", mnem, modrm_byte & 0x7);
827 } else {
828 AppendToBuffer("%s", mnem);
829 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000830 return 2;
831}
832
833
834// Mnemonics for instructions 0xF0 byte.
835// Returns NULL if the instruction is not handled here.
836static const char* F0Mnem(byte f0byte) {
837 switch (f0byte) {
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100838 case 0x18: return "prefetch";
Steve Blocka7e24c12009-10-30 11:49:00 +0000839 case 0xA2: return "cpuid";
840 case 0x31: return "rdtsc";
841 case 0xBE: return "movsx_b";
842 case 0xBF: return "movsx_w";
843 case 0xB6: return "movzx_b";
844 case 0xB7: return "movzx_w";
845 case 0xAF: return "imul";
846 case 0xA5: return "shld";
847 case 0xAD: return "shrd";
848 case 0xAB: return "bts";
849 default: return NULL;
850 }
851}
852
853
854// Disassembled instruction '*instr' and writes it into 'out_buffer'.
855int DisassemblerIA32::InstructionDecode(v8::internal::Vector<char> out_buffer,
856 byte* instr) {
857 tmp_buffer_pos_ = 0; // starting to write as position 0
858 byte* data = instr;
859 // Check for hints.
860 const char* branch_hint = NULL;
861 // We use these two prefixes only with branch prediction
862 if (*data == 0x3E /*ds*/) {
863 branch_hint = "predicted taken";
864 data++;
865 } else if (*data == 0x2E /*cs*/) {
866 branch_hint = "predicted not taken";
867 data++;
868 }
869 bool processed = true; // Will be set to false if the current instruction
870 // is not in 'instructions' table.
871 const InstructionDesc& idesc = instruction_table.Get(*data);
872 switch (idesc.type) {
873 case ZERO_OPERANDS_INSTR:
874 AppendToBuffer(idesc.mnem);
875 data++;
876 break;
877
878 case TWO_OPERANDS_INSTR:
879 data++;
880 data += PrintOperands(idesc.mnem, idesc.op_order_, data);
881 break;
882
883 case JUMP_CONDITIONAL_SHORT_INSTR:
884 data += JumpConditionalShort(data, branch_hint);
885 break;
886
887 case REGISTER_INSTR:
888 AppendToBuffer("%s %s", idesc.mnem, NameOfCPURegister(*data & 0x07));
889 data++;
890 break;
891
892 case MOVE_REG_INSTR: {
893 byte* addr = reinterpret_cast<byte*>(*reinterpret_cast<int32_t*>(data+1));
894 AppendToBuffer("mov %s,%s",
895 NameOfCPURegister(*data & 0x07),
896 NameOfAddress(addr));
897 data += 5;
898 break;
899 }
900
901 case CALL_JUMP_INSTR: {
902 byte* addr = data + *reinterpret_cast<int32_t*>(data+1) + 5;
903 AppendToBuffer("%s %s", idesc.mnem, NameOfAddress(addr));
904 data += 5;
905 break;
906 }
907
908 case SHORT_IMMEDIATE_INSTR: {
909 byte* addr = reinterpret_cast<byte*>(*reinterpret_cast<int32_t*>(data+1));
910 AppendToBuffer("%s eax, %s", idesc.mnem, NameOfAddress(addr));
911 data += 5;
912 break;
913 }
914
915 case NO_INSTR:
916 processed = false;
917 break;
918
919 default:
920 UNIMPLEMENTED(); // This type is not implemented.
921 }
922 //----------------------------
923 if (!processed) {
924 switch (*data) {
925 case 0xC2:
926 AppendToBuffer("ret 0x%x", *reinterpret_cast<uint16_t*>(data+1));
927 data += 3;
928 break;
929
930 case 0x69: // fall through
931 case 0x6B:
932 { int mod, regop, rm;
933 get_modrm(*(data+1), &mod, &regop, &rm);
934 int32_t imm =
935 *data == 0x6B ? *(data+2) : *reinterpret_cast<int32_t*>(data+2);
936 AppendToBuffer("imul %s,%s,0x%x",
937 NameOfCPURegister(regop),
938 NameOfCPURegister(rm),
939 imm);
940 data += 2 + (*data == 0x6B ? 1 : 4);
941 }
942 break;
943
944 case 0xF6:
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100945 { data++;
946 int mod, regop, rm;
947 get_modrm(*data, &mod, &regop, &rm);
948 if (regop == eax) {
949 AppendToBuffer("test_b ");
Steve Block44f0eee2011-05-26 01:26:41 +0100950 data += PrintRightByteOperand(data);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100951 int32_t imm = *data;
952 AppendToBuffer(",0x%x", imm);
953 data++;
Steve Blocka7e24c12009-10-30 11:49:00 +0000954 } else {
955 UnimplementedInstruction();
956 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000957 }
958 break;
959
960 case 0x81: // fall through
961 case 0x83: // 0x81 with sign extension bit set
962 data += PrintImmediateOp(data);
963 break;
964
965 case 0x0F:
966 { byte f0byte = *(data+1);
967 const char* f0mnem = F0Mnem(f0byte);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100968 if (f0byte == 0x18) {
969 int mod, regop, rm;
970 get_modrm(*data, &mod, &regop, &rm);
971 const char* suffix[] = {"nta", "1", "2", "3"};
972 AppendToBuffer("%s%s ", f0mnem, suffix[regop & 0x03]);
973 data += PrintRightOperand(data);
974 } else if (f0byte == 0xA2 || f0byte == 0x31) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000975 AppendToBuffer("%s", f0mnem);
976 data += 2;
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100977 } else if (f0byte == 0x28) {
978 data += 2;
979 int mod, regop, rm;
980 get_modrm(*data, &mod, &regop, &rm);
981 AppendToBuffer("movaps %s,%s",
982 NameOfXMMRegister(regop),
983 NameOfXMMRegister(rm));
984 data++;
Ben Murdoch257744e2011-11-30 15:57:28 +0000985 } else if (f0byte == 0x57) {
986 data += 2;
987 int mod, regop, rm;
988 get_modrm(*data, &mod, &regop, &rm);
989 AppendToBuffer("xorps %s,%s",
990 NameOfXMMRegister(regop),
991 NameOfXMMRegister(rm));
992 data++;
Steve Blocka7e24c12009-10-30 11:49:00 +0000993 } else if ((f0byte & 0xF0) == 0x80) {
994 data += JumpConditional(data, branch_hint);
995 } else if (f0byte == 0xBE || f0byte == 0xBF || f0byte == 0xB6 ||
996 f0byte == 0xB7 || f0byte == 0xAF) {
997 data += 2;
998 data += PrintOperands(f0mnem, REG_OPER_OP_ORDER, data);
999 } else if ((f0byte & 0xF0) == 0x90) {
1000 data += SetCC(data);
Steve Block3ce2e202009-11-05 08:53:23 +00001001 } else if ((f0byte & 0xF0) == 0x40) {
1002 data += CMov(data);
Steve Blocka7e24c12009-10-30 11:49:00 +00001003 } else {
1004 data += 2;
1005 if (f0byte == 0xAB || f0byte == 0xA5 || f0byte == 0xAD) {
1006 // shrd, shld, bts
1007 AppendToBuffer("%s ", f0mnem);
1008 int mod, regop, rm;
1009 get_modrm(*data, &mod, &regop, &rm);
1010 data += PrintRightOperand(data);
1011 if (f0byte == 0xAB) {
1012 AppendToBuffer(",%s", NameOfCPURegister(regop));
1013 } else {
1014 AppendToBuffer(",%s,cl", NameOfCPURegister(regop));
1015 }
1016 } else {
1017 UnimplementedInstruction();
1018 }
1019 }
1020 }
1021 break;
1022
1023 case 0x8F:
1024 { data++;
1025 int mod, regop, rm;
1026 get_modrm(*data, &mod, &regop, &rm);
1027 if (regop == eax) {
1028 AppendToBuffer("pop ");
1029 data += PrintRightOperand(data);
1030 }
1031 }
1032 break;
1033
1034 case 0xFF:
1035 { data++;
1036 int mod, regop, rm;
1037 get_modrm(*data, &mod, &regop, &rm);
1038 const char* mnem = NULL;
1039 switch (regop) {
1040 case esi: mnem = "push"; break;
1041 case eax: mnem = "inc"; break;
1042 case ecx: mnem = "dec"; break;
1043 case edx: mnem = "call"; break;
1044 case esp: mnem = "jmp"; break;
1045 default: mnem = "???";
1046 }
1047 AppendToBuffer("%s ", mnem);
1048 data += PrintRightOperand(data);
1049 }
1050 break;
1051
1052 case 0xC7: // imm32, fall through
1053 case 0xC6: // imm8
1054 { bool is_byte = *data == 0xC6;
1055 data++;
Steve Block44f0eee2011-05-26 01:26:41 +01001056 if (is_byte) {
1057 AppendToBuffer("%s ", "mov_b");
1058 data += PrintRightByteOperand(data);
1059 int32_t imm = *data;
1060 AppendToBuffer(",0x%x", imm);
1061 data++;
1062 } else {
1063 AppendToBuffer("%s ", "mov");
1064 data += PrintRightOperand(data);
1065 int32_t imm = *reinterpret_cast<int32_t*>(data);
1066 AppendToBuffer(",0x%x", imm);
1067 data += 4;
1068 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001069 }
1070 break;
1071
1072 case 0x80:
1073 { data++;
Leon Clarkee46be812010-01-19 14:06:41 +00001074 int mod, regop, rm;
1075 get_modrm(*data, &mod, &regop, &rm);
1076 const char* mnem = NULL;
Leon Clarkee46be812010-01-19 14:06:41 +00001077 switch (regop) {
1078 case 5: mnem = "subb"; break;
1079 case 7: mnem = "cmpb"; break;
1080 default: UnimplementedInstruction();
1081 }
1082 AppendToBuffer("%s ", mnem);
Steve Block44f0eee2011-05-26 01:26:41 +01001083 data += PrintRightByteOperand(data);
Steve Blocka7e24c12009-10-30 11:49:00 +00001084 int32_t imm = *data;
1085 AppendToBuffer(",0x%x", imm);
1086 data++;
1087 }
1088 break;
1089
1090 case 0x88: // 8bit, fall through
1091 case 0x89: // 32bit
1092 { bool is_byte = *data == 0x88;
1093 int mod, regop, rm;
1094 data++;
1095 get_modrm(*data, &mod, &regop, &rm);
Steve Block44f0eee2011-05-26 01:26:41 +01001096 if (is_byte) {
1097 AppendToBuffer("%s ", "mov_b");
1098 data += PrintRightByteOperand(data);
1099 AppendToBuffer(",%s", NameOfByteCPURegister(regop));
1100 } else {
1101 AppendToBuffer("%s ", "mov");
1102 data += PrintRightOperand(data);
1103 AppendToBuffer(",%s", NameOfCPURegister(regop));
1104 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001105 }
1106 break;
1107
1108 case 0x66: // prefix
1109 data++;
1110 if (*data == 0x8B) {
1111 data++;
1112 data += PrintOperands("mov_w", REG_OPER_OP_ORDER, data);
1113 } else if (*data == 0x89) {
1114 data++;
1115 int mod, regop, rm;
1116 get_modrm(*data, &mod, &regop, &rm);
1117 AppendToBuffer("mov_w ");
1118 data += PrintRightOperand(data);
1119 AppendToBuffer(",%s", NameOfCPURegister(regop));
Steve Block3ce2e202009-11-05 08:53:23 +00001120 } else if (*data == 0x0F) {
1121 data++;
Steve Block6ded16b2010-05-10 14:33:55 +01001122 if (*data == 0x38) {
1123 data++;
1124 if (*data == 0x17) {
1125 data++;
1126 int mod, regop, rm;
1127 get_modrm(*data, &mod, &regop, &rm);
1128 AppendToBuffer("ptest %s,%s",
1129 NameOfXMMRegister(regop),
1130 NameOfXMMRegister(rm));
1131 data++;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001132 } else if (*data == 0x2A) {
1133 // movntdqa
1134 data++;
1135 int mod, regop, rm;
1136 get_modrm(*data, &mod, &regop, &rm);
1137 AppendToBuffer("movntdqa %s,", NameOfXMMRegister(regop));
1138 data += PrintRightOperand(data);
Steve Block6ded16b2010-05-10 14:33:55 +01001139 } else {
1140 UnimplementedInstruction();
1141 }
Ben Murdochb0fe1622011-05-05 13:52:32 +01001142 } else if (*data == 0x3A) {
1143 data++;
Ben Murdoch69a99ed2011-11-30 16:03:39 +00001144 if (*data == 0x0B) {
1145 data++;
1146 int mod, regop, rm;
1147 get_modrm(*data, &mod, &regop, &rm);
1148 int8_t imm8 = static_cast<int8_t>(data[1]);
1149 AppendToBuffer("roundsd %s,%s,%d",
1150 NameOfXMMRegister(regop),
1151 NameOfXMMRegister(rm),
1152 static_cast<int>(imm8));
1153 data += 2;
1154 } else if (*data == 0x16) {
Ben Murdochb0fe1622011-05-05 13:52:32 +01001155 data++;
1156 int mod, regop, rm;
1157 get_modrm(*data, &mod, &regop, &rm);
1158 int8_t imm8 = static_cast<int8_t>(data[1]);
1159 AppendToBuffer("pextrd %s,%s,%d",
Steve Block1e0659c2011-05-24 12:43:12 +01001160 NameOfCPURegister(regop),
Ben Murdochb0fe1622011-05-05 13:52:32 +01001161 NameOfXMMRegister(rm),
1162 static_cast<int>(imm8));
1163 data += 2;
Steve Block1e0659c2011-05-24 12:43:12 +01001164 } else if (*data == 0x22) {
1165 data++;
1166 int mod, regop, rm;
1167 get_modrm(*data, &mod, &regop, &rm);
1168 int8_t imm8 = static_cast<int8_t>(data[1]);
1169 AppendToBuffer("pinsrd %s,%s,%d",
1170 NameOfXMMRegister(regop),
1171 NameOfCPURegister(rm),
1172 static_cast<int>(imm8));
1173 data += 2;
Ben Murdochb0fe1622011-05-05 13:52:32 +01001174 } else {
1175 UnimplementedInstruction();
1176 }
Steve Block6ded16b2010-05-10 14:33:55 +01001177 } else if (*data == 0x2E || *data == 0x2F) {
1178 const char* mnem = (*data == 0x2E) ? "ucomisd" : "comisd";
Steve Block3ce2e202009-11-05 08:53:23 +00001179 data++;
1180 int mod, regop, rm;
1181 get_modrm(*data, &mod, &regop, &rm);
Steve Block6ded16b2010-05-10 14:33:55 +01001182 if (mod == 0x3) {
1183 AppendToBuffer("%s %s,%s", mnem,
1184 NameOfXMMRegister(regop),
1185 NameOfXMMRegister(rm));
1186 data++;
1187 } else {
1188 AppendToBuffer("%s %s,", mnem, NameOfXMMRegister(regop));
1189 data += PrintRightOperand(data);
1190 }
1191 } else if (*data == 0x50) {
1192 data++;
1193 int mod, regop, rm;
1194 get_modrm(*data, &mod, &regop, &rm);
1195 AppendToBuffer("movmskpd %s,%s",
1196 NameOfCPURegister(regop),
Steve Block3ce2e202009-11-05 08:53:23 +00001197 NameOfXMMRegister(rm));
1198 data++;
Ben Murdochb0fe1622011-05-05 13:52:32 +01001199 } else if (*data == 0x54) {
1200 data++;
1201 int mod, regop, rm;
1202 get_modrm(*data, &mod, &regop, &rm);
1203 AppendToBuffer("andpd %s,%s",
1204 NameOfXMMRegister(regop),
1205 NameOfXMMRegister(rm));
1206 data++;
Leon Clarkee46be812010-01-19 14:06:41 +00001207 } else if (*data == 0x57) {
1208 data++;
1209 int mod, regop, rm;
1210 get_modrm(*data, &mod, &regop, &rm);
1211 AppendToBuffer("xorpd %s,%s",
1212 NameOfXMMRegister(regop),
1213 NameOfXMMRegister(rm));
1214 data++;
Steve Block6ded16b2010-05-10 14:33:55 +01001215 } else if (*data == 0x6E) {
1216 data++;
1217 int mod, regop, rm;
1218 get_modrm(*data, &mod, &regop, &rm);
1219 AppendToBuffer("movd %s,", NameOfXMMRegister(regop));
1220 data += PrintRightOperand(data);
Leon Clarkee46be812010-01-19 14:06:41 +00001221 } else if (*data == 0x6F) {
1222 data++;
1223 int mod, regop, rm;
1224 get_modrm(*data, &mod, &regop, &rm);
1225 AppendToBuffer("movdqa %s,", NameOfXMMRegister(regop));
Steve Block44f0eee2011-05-26 01:26:41 +01001226 data += PrintRightXMMOperand(data);
Ben Murdochb0fe1622011-05-05 13:52:32 +01001227 } else if (*data == 0x70) {
1228 data++;
1229 int mod, regop, rm;
1230 get_modrm(*data, &mod, &regop, &rm);
1231 int8_t imm8 = static_cast<int8_t>(data[1]);
1232 AppendToBuffer("pshufd %s,%s,%d",
1233 NameOfXMMRegister(regop),
1234 NameOfXMMRegister(rm),
1235 static_cast<int>(imm8));
1236 data += 2;
Ben Murdochb8e0da22011-05-16 14:20:40 +01001237 } else if (*data == 0xF3) {
1238 data++;
1239 int mod, regop, rm;
1240 get_modrm(*data, &mod, &regop, &rm);
1241 AppendToBuffer("psllq %s,%s",
1242 NameOfXMMRegister(regop),
1243 NameOfXMMRegister(rm));
1244 data++;
Ben Murdochb0fe1622011-05-05 13:52:32 +01001245 } else if (*data == 0x73) {
1246 data++;
1247 int mod, regop, rm;
1248 get_modrm(*data, &mod, &regop, &rm);
1249 int8_t imm8 = static_cast<int8_t>(data[1]);
Ben Murdochb8e0da22011-05-16 14:20:40 +01001250 ASSERT(regop == esi || regop == edx);
1251 AppendToBuffer("%s %s,%d",
1252 (regop == esi) ? "psllq" : "psrlq",
Ben Murdochb0fe1622011-05-05 13:52:32 +01001253 NameOfXMMRegister(rm),
1254 static_cast<int>(imm8));
1255 data += 2;
Ben Murdochb8e0da22011-05-16 14:20:40 +01001256 } else if (*data == 0xD3) {
1257 data++;
1258 int mod, regop, rm;
1259 get_modrm(*data, &mod, &regop, &rm);
1260 AppendToBuffer("psrlq %s,%s",
1261 NameOfXMMRegister(regop),
1262 NameOfXMMRegister(rm));
1263 data++;
Leon Clarkee46be812010-01-19 14:06:41 +00001264 } else if (*data == 0x7F) {
1265 AppendToBuffer("movdqa ");
1266 data++;
1267 int mod, regop, rm;
1268 get_modrm(*data, &mod, &regop, &rm);
Steve Block44f0eee2011-05-26 01:26:41 +01001269 data += PrintRightXMMOperand(data);
Leon Clarkee46be812010-01-19 14:06:41 +00001270 AppendToBuffer(",%s", NameOfXMMRegister(regop));
Ben Murdochb0fe1622011-05-05 13:52:32 +01001271 } else if (*data == 0x7E) {
1272 data++;
1273 int mod, regop, rm;
1274 get_modrm(*data, &mod, &regop, &rm);
1275 AppendToBuffer("movd ");
1276 data += PrintRightOperand(data);
1277 AppendToBuffer(",%s", NameOfXMMRegister(regop));
1278 } else if (*data == 0xDB) {
1279 data++;
1280 int mod, regop, rm;
1281 get_modrm(*data, &mod, &regop, &rm);
1282 AppendToBuffer("pand %s,%s",
1283 NameOfXMMRegister(regop),
1284 NameOfXMMRegister(rm));
1285 data++;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001286 } else if (*data == 0xE7) {
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001287 data++;
1288 int mod, regop, rm;
1289 get_modrm(*data, &mod, &regop, &rm);
Steve Block44f0eee2011-05-26 01:26:41 +01001290 if (mod == 3) {
1291 AppendToBuffer("movntdq ");
1292 data += PrintRightOperand(data);
1293 AppendToBuffer(",%s", NameOfXMMRegister(regop));
1294 } else {
1295 UnimplementedInstruction();
1296 }
Steve Block6ded16b2010-05-10 14:33:55 +01001297 } else if (*data == 0xEF) {
Ben Murdochb0fe1622011-05-05 13:52:32 +01001298 data++;
1299 int mod, regop, rm;
1300 get_modrm(*data, &mod, &regop, &rm);
1301 AppendToBuffer("pxor %s,%s",
1302 NameOfXMMRegister(regop),
1303 NameOfXMMRegister(rm));
1304 data++;
Ben Murdochb8e0da22011-05-16 14:20:40 +01001305 } else if (*data == 0xEB) {
1306 data++;
1307 int mod, regop, rm;
1308 get_modrm(*data, &mod, &regop, &rm);
1309 AppendToBuffer("por %s,%s",
1310 NameOfXMMRegister(regop),
1311 NameOfXMMRegister(rm));
1312 data++;
Steve Block3ce2e202009-11-05 08:53:23 +00001313 } else {
1314 UnimplementedInstruction();
1315 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001316 } else {
1317 UnimplementedInstruction();
1318 }
1319 break;
1320
1321 case 0xFE:
1322 { data++;
1323 int mod, regop, rm;
1324 get_modrm(*data, &mod, &regop, &rm);
Kristian Monsen0d5e1162010-09-30 15:31:59 +01001325 if (regop == ecx) {
1326 AppendToBuffer("dec_b ");
1327 data += PrintRightOperand(data);
Steve Blocka7e24c12009-10-30 11:49:00 +00001328 } else {
1329 UnimplementedInstruction();
1330 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001331 }
1332 break;
1333
1334 case 0x68:
1335 AppendToBuffer("push 0x%x", *reinterpret_cast<int32_t*>(data+1));
1336 data += 5;
1337 break;
1338
1339 case 0x6A:
1340 AppendToBuffer("push 0x%x", *reinterpret_cast<int8_t*>(data + 1));
1341 data += 2;
1342 break;
1343
1344 case 0xA8:
1345 AppendToBuffer("test al,0x%x", *reinterpret_cast<uint8_t*>(data+1));
1346 data += 2;
1347 break;
1348
Leon Clarkee46be812010-01-19 14:06:41 +00001349 case 0x2C:
1350 AppendToBuffer("subb eax,0x%x", *reinterpret_cast<uint8_t*>(data+1));
1351 data += 2;
1352 break;
1353
Steve Blocka7e24c12009-10-30 11:49:00 +00001354 case 0xA9:
1355 AppendToBuffer("test eax,0x%x", *reinterpret_cast<int32_t*>(data+1));
1356 data += 5;
1357 break;
1358
1359 case 0xD1: // fall through
1360 case 0xD3: // fall through
1361 case 0xC1:
1362 data += D1D3C1Instruction(data);
1363 break;
1364
1365 case 0xD9: // fall through
1366 case 0xDA: // fall through
1367 case 0xDB: // fall through
1368 case 0xDC: // fall through
1369 case 0xDD: // fall through
1370 case 0xDE: // fall through
1371 case 0xDF:
1372 data += FPUInstruction(data);
1373 break;
1374
1375 case 0xEB:
1376 data += JumpShort(data);
1377 break;
1378
1379 case 0xF2:
1380 if (*(data+1) == 0x0F) {
1381 byte b2 = *(data+2);
1382 if (b2 == 0x11) {
1383 AppendToBuffer("movsd ");
1384 data += 3;
1385 int mod, regop, rm;
1386 get_modrm(*data, &mod, &regop, &rm);
Steve Block44f0eee2011-05-26 01:26:41 +01001387 data += PrintRightXMMOperand(data);
Steve Blocka7e24c12009-10-30 11:49:00 +00001388 AppendToBuffer(",%s", NameOfXMMRegister(regop));
1389 } else if (b2 == 0x10) {
1390 data += 3;
1391 int mod, regop, rm;
1392 get_modrm(*data, &mod, &regop, &rm);
1393 AppendToBuffer("movsd %s,", NameOfXMMRegister(regop));
Steve Block44f0eee2011-05-26 01:26:41 +01001394 data += PrintRightXMMOperand(data);
1395 } else if (b2 == 0x5A) {
1396 data += 3;
1397 int mod, regop, rm;
1398 get_modrm(*data, &mod, &regop, &rm);
1399 AppendToBuffer("cvtsd2ss %s,", NameOfXMMRegister(regop));
1400 data += PrintRightXMMOperand(data);
Steve Blocka7e24c12009-10-30 11:49:00 +00001401 } else {
1402 const char* mnem = "?";
1403 switch (b2) {
1404 case 0x2A: mnem = "cvtsi2sd"; break;
Steve Block6ded16b2010-05-10 14:33:55 +01001405 case 0x2C: mnem = "cvttsd2si"; break;
1406 case 0x51: mnem = "sqrtsd"; break;
Steve Blocka7e24c12009-10-30 11:49:00 +00001407 case 0x58: mnem = "addsd"; break;
1408 case 0x59: mnem = "mulsd"; break;
1409 case 0x5C: mnem = "subsd"; break;
1410 case 0x5E: mnem = "divsd"; break;
1411 }
1412 data += 3;
1413 int mod, regop, rm;
1414 get_modrm(*data, &mod, &regop, &rm);
1415 if (b2 == 0x2A) {
Steve Block44f0eee2011-05-26 01:26:41 +01001416 AppendToBuffer("%s %s,", mnem, NameOfXMMRegister(regop));
1417 data += PrintRightOperand(data);
Steve Block6ded16b2010-05-10 14:33:55 +01001418 } else if (b2 == 0x2C) {
Steve Block44f0eee2011-05-26 01:26:41 +01001419 AppendToBuffer("%s %s,", mnem, NameOfCPURegister(regop));
1420 data += PrintRightXMMOperand(data);
Kristian Monsen0d5e1162010-09-30 15:31:59 +01001421 } else if (b2 == 0xC2) {
1422 // Intel manual 2A, Table 3-18.
1423 const char* const pseudo_op[] = {
1424 "cmpeqsd",
1425 "cmpltsd",
1426 "cmplesd",
1427 "cmpunordsd",
1428 "cmpneqsd",
1429 "cmpnltsd",
1430 "cmpnlesd",
1431 "cmpordsd"
1432 };
1433 AppendToBuffer("%s %s,%s",
1434 pseudo_op[data[1]],
1435 NameOfXMMRegister(regop),
1436 NameOfXMMRegister(rm));
1437 data += 2;
Steve Blocka7e24c12009-10-30 11:49:00 +00001438 } else {
Steve Block44f0eee2011-05-26 01:26:41 +01001439 AppendToBuffer("%s %s,", mnem, NameOfXMMRegister(regop));
1440 data += PrintRightXMMOperand(data);
Steve Blocka7e24c12009-10-30 11:49:00 +00001441 }
1442 }
1443 } else {
1444 UnimplementedInstruction();
1445 }
1446 break;
1447
1448 case 0xF3:
Leon Clarkee46be812010-01-19 14:06:41 +00001449 if (*(data+1) == 0x0F) {
Steve Block44f0eee2011-05-26 01:26:41 +01001450 byte b2 = *(data+2);
1451 if (b2 == 0x11) {
1452 AppendToBuffer("movss ");
Steve Block6ded16b2010-05-10 14:33:55 +01001453 data += 3;
1454 int mod, regop, rm;
1455 get_modrm(*data, &mod, &regop, &rm);
Steve Block44f0eee2011-05-26 01:26:41 +01001456 data += PrintRightXMMOperand(data);
1457 AppendToBuffer(",%s", NameOfXMMRegister(regop));
1458 } else if (b2 == 0x10) {
1459 data += 3;
1460 int mod, regop, rm;
1461 get_modrm(*data, &mod, &regop, &rm);
1462 AppendToBuffer("movss %s,", NameOfXMMRegister(regop));
1463 data += PrintRightXMMOperand(data);
1464 } else if (b2 == 0x2C) {
1465 data += 3;
1466 int mod, regop, rm;
1467 get_modrm(*data, &mod, &regop, &rm);
1468 AppendToBuffer("cvttss2si %s,", NameOfCPURegister(regop));
1469 data += PrintRightXMMOperand(data);
1470 } else if (b2 == 0x5A) {
1471 data += 3;
1472 int mod, regop, rm;
1473 get_modrm(*data, &mod, &regop, &rm);
1474 AppendToBuffer("cvtss2sd %s,", NameOfXMMRegister(regop));
1475 data += PrintRightXMMOperand(data);
1476 } else if (b2 == 0x6F) {
Leon Clarkee46be812010-01-19 14:06:41 +00001477 data += 3;
1478 int mod, regop, rm;
1479 get_modrm(*data, &mod, &regop, &rm);
1480 AppendToBuffer("movdqu %s,", NameOfXMMRegister(regop));
Steve Block44f0eee2011-05-26 01:26:41 +01001481 data += PrintRightXMMOperand(data);
1482 } else if (b2 == 0x7F) {
Leon Clarkee46be812010-01-19 14:06:41 +00001483 AppendToBuffer("movdqu ");
1484 data += 3;
1485 int mod, regop, rm;
1486 get_modrm(*data, &mod, &regop, &rm);
Steve Block44f0eee2011-05-26 01:26:41 +01001487 data += PrintRightXMMOperand(data);
Leon Clarkee46be812010-01-19 14:06:41 +00001488 AppendToBuffer(",%s", NameOfXMMRegister(regop));
1489 } else {
1490 UnimplementedInstruction();
1491 }
1492 } else if (*(data+1) == 0xA5) {
1493 data += 2;
1494 AppendToBuffer("rep_movs");
Steve Block6ded16b2010-05-10 14:33:55 +01001495 } else if (*(data+1) == 0xAB) {
1496 data += 2;
1497 AppendToBuffer("rep_stos");
Steve Blocka7e24c12009-10-30 11:49:00 +00001498 } else {
1499 UnimplementedInstruction();
1500 }
1501 break;
1502
1503 case 0xF7:
1504 data += F7Instruction(data);
1505 break;
1506
1507 default:
1508 UnimplementedInstruction();
1509 }
1510 }
1511
1512 if (tmp_buffer_pos_ < sizeof tmp_buffer_) {
1513 tmp_buffer_[tmp_buffer_pos_] = '\0';
1514 }
1515
1516 int instr_len = data - instr;
Leon Clarkee46be812010-01-19 14:06:41 +00001517 if (instr_len == 0) {
1518 printf("%02x", *data);
1519 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001520 ASSERT(instr_len > 0); // Ensure progress.
1521
1522 int outp = 0;
1523 // Instruction bytes.
1524 for (byte* bp = instr; bp < data; bp++) {
1525 outp += v8::internal::OS::SNPrintF(out_buffer + outp,
1526 "%02x",
1527 *bp);
1528 }
1529 for (int i = 6 - instr_len; i >= 0; i--) {
1530 outp += v8::internal::OS::SNPrintF(out_buffer + outp,
1531 " ");
1532 }
1533
1534 outp += v8::internal::OS::SNPrintF(out_buffer + outp,
1535 " %s",
1536 tmp_buffer_.start());
1537 return instr_len;
Kristian Monsen0d5e1162010-09-30 15:31:59 +01001538} // NOLINT (function is too long)
Steve Blocka7e24c12009-10-30 11:49:00 +00001539
1540
1541//------------------------------------------------------------------------------
1542
1543
1544static const char* cpu_regs[8] = {
1545 "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi"
1546};
1547
1548
1549static const char* byte_cpu_regs[8] = {
1550 "al", "cl", "dl", "bl", "ah", "ch", "dh", "bh"
1551};
1552
1553
1554static const char* xmm_regs[8] = {
1555 "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7"
1556};
1557
1558
1559const char* NameConverter::NameOfAddress(byte* addr) const {
Steve Block44f0eee2011-05-26 01:26:41 +01001560 v8::internal::OS::SNPrintF(tmp_buffer_, "%p", addr);
1561 return tmp_buffer_.start();
Steve Blocka7e24c12009-10-30 11:49:00 +00001562}
1563
1564
1565const char* NameConverter::NameOfConstant(byte* addr) const {
1566 return NameOfAddress(addr);
1567}
1568
1569
1570const char* NameConverter::NameOfCPURegister(int reg) const {
1571 if (0 <= reg && reg < 8) return cpu_regs[reg];
1572 return "noreg";
1573}
1574
1575
1576const char* NameConverter::NameOfByteCPURegister(int reg) const {
1577 if (0 <= reg && reg < 8) return byte_cpu_regs[reg];
1578 return "noreg";
1579}
1580
1581
1582const char* NameConverter::NameOfXMMRegister(int reg) const {
1583 if (0 <= reg && reg < 8) return xmm_regs[reg];
1584 return "noxmmreg";
1585}
1586
1587
1588const char* NameConverter::NameInCode(byte* addr) const {
1589 // IA32 does not embed debug strings at the moment.
1590 UNREACHABLE();
1591 return "";
1592}
1593
1594
1595//------------------------------------------------------------------------------
1596
1597Disassembler::Disassembler(const NameConverter& converter)
1598 : converter_(converter) {}
1599
1600
1601Disassembler::~Disassembler() {}
1602
1603
1604int Disassembler::InstructionDecode(v8::internal::Vector<char> buffer,
1605 byte* instruction) {
1606 DisassemblerIA32 d(converter_, false /*do not crash if unimplemented*/);
1607 return d.InstructionDecode(buffer, instruction);
1608}
1609
1610
1611// The IA-32 assembler does not currently use constant pools.
1612int Disassembler::ConstantPoolSizeAt(byte* instruction) { return -1; }
1613
1614
1615/*static*/ void Disassembler::Disassemble(FILE* f, byte* begin, byte* end) {
1616 NameConverter converter;
1617 Disassembler d(converter);
1618 for (byte* pc = begin; pc < end;) {
1619 v8::internal::EmbeddedVector<char, 128> buffer;
1620 buffer[0] = '\0';
1621 byte* prev_pc = pc;
1622 pc += d.InstructionDecode(buffer, pc);
1623 fprintf(f, "%p", prev_pc);
1624 fprintf(f, " ");
1625
1626 for (byte* bp = prev_pc; bp < pc; bp++) {
1627 fprintf(f, "%02x", *bp);
1628 }
1629 for (int i = 6 - (pc - prev_pc); i >= 0; i--) {
1630 fprintf(f, " ");
1631 }
1632 fprintf(f, " %s\n", buffer.start());
1633 }
1634}
1635
1636
1637} // namespace disasm
Leon Clarkef7060e22010-06-03 12:02:55 +01001638
1639#endif // V8_TARGET_ARCH_IA32