blob: 52c2b3848ae6e3c882d07197108b48c4d9495d66 [file] [log] [blame]
Steve Blocka7e24c12009-10-30 11:49:00 +00001// Copyright 2007-2008 the V8 project authors. All rights reserved.
2// 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
57static ByteMnemonic two_operands_instr[] = {
58 {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
82static ByteMnemonic zero_operands_instr[] = {
83 {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
101static ByteMnemonic call_jump_instr[] = {
102 {0xE8, "call", UNSET_OP_ORDER},
103 {0xE9, "jmp", UNSET_OP_ORDER},
104 {-1, "", UNSET_OP_ORDER}
105};
106
107
108static ByteMnemonic short_immediate_instr[] = {
109 {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
120static const char* jump_conditional_mnem[] = {
121 /*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
128static const char* set_conditional_mnem[] = {
129 /*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
Steve Block3ce2e202009-11-05 08:53:23 +0000136static const char* conditional_move_mnem[] = {
137 /*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();
172 void CopyTable(ByteMnemonic bm[], InstructionType type);
173 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
211void InstructionTable::CopyTable(ByteMnemonic bm[], InstructionType type) {
212 for (int i = 0; bm[i].b >= 0; i++) {
213 InstructionDesc* id = &instructions_[bm[i].b];
214 id->mnem = bm[i].mnem;
215 id->op_order_ = bm[i].op_order_;
Steve Blockd0582a62009-12-15 09:54:21 +0000216 ASSERT_EQ(NO_INSTR, id->type); // Information not already entered.
Steve Blocka7e24c12009-10-30 11:49:00 +0000217 id->type = type;
218 }
219}
220
221
222void InstructionTable::SetTableRange(InstructionType type,
223 byte start,
224 byte end,
225 const char* mnem) {
226 for (byte b = start; b <= end; b++) {
227 InstructionDesc* id = &instructions_[b];
Steve Blockd0582a62009-12-15 09:54:21 +0000228 ASSERT_EQ(NO_INSTR, id->type); // Information not already entered.
Steve Blocka7e24c12009-10-30 11:49:00 +0000229 id->mnem = mnem;
230 id->type = type;
231 }
232}
233
234
235void InstructionTable::AddJumpConditionalShort() {
236 for (byte b = 0x70; b <= 0x7F; b++) {
237 InstructionDesc* id = &instructions_[b];
Steve Blockd0582a62009-12-15 09:54:21 +0000238 ASSERT_EQ(NO_INSTR, id->type); // Information not already entered.
Steve Blocka7e24c12009-10-30 11:49:00 +0000239 id->mnem = jump_conditional_mnem[b & 0x0F];
240 id->type = JUMP_CONDITIONAL_SHORT_INSTR;
241 }
242}
243
244
245static InstructionTable instruction_table;
246
247
248// The IA32 disassembler implementation.
249class DisassemblerIA32 {
250 public:
251 DisassemblerIA32(const NameConverter& converter,
252 bool abort_on_unimplemented = true)
253 : converter_(converter),
254 tmp_buffer_pos_(0),
255 abort_on_unimplemented_(abort_on_unimplemented) {
256 tmp_buffer_[0] = '\0';
257 }
258
259 virtual ~DisassemblerIA32() {}
260
261 // Writes one disassembled instruction into 'buffer' (0-terminated).
262 // Returns the length of the disassembled machine instruction in bytes.
263 int InstructionDecode(v8::internal::Vector<char> buffer, byte* instruction);
264
265 private:
266 const NameConverter& converter_;
267 v8::internal::EmbeddedVector<char, 128> tmp_buffer_;
268 unsigned int tmp_buffer_pos_;
269 bool abort_on_unimplemented_;
270
271
272 enum {
273 eax = 0,
274 ecx = 1,
275 edx = 2,
276 ebx = 3,
277 esp = 4,
278 ebp = 5,
279 esi = 6,
280 edi = 7
281 };
282
283
Steve Blockd0582a62009-12-15 09:54:21 +0000284 enum ShiftOpcodeExtension {
285 kROL = 0,
286 kROR = 1,
287 kRCL = 2,
288 kRCR = 3,
289 kSHL = 4,
290 KSHR = 5,
291 kSAR = 7
292 };
293
294
Steve Blocka7e24c12009-10-30 11:49:00 +0000295 const char* NameOfCPURegister(int reg) const {
296 return converter_.NameOfCPURegister(reg);
297 }
298
299
300 const char* NameOfByteCPURegister(int reg) const {
301 return converter_.NameOfByteCPURegister(reg);
302 }
303
304
305 const char* NameOfXMMRegister(int reg) const {
306 return converter_.NameOfXMMRegister(reg);
307 }
308
309
310 const char* NameOfAddress(byte* addr) const {
311 return converter_.NameOfAddress(addr);
312 }
313
314
315 // Disassembler helper functions.
316 static void get_modrm(byte data, int* mod, int* regop, int* rm) {
317 *mod = (data >> 6) & 3;
318 *regop = (data & 0x38) >> 3;
319 *rm = data & 7;
320 }
321
322
323 static void get_sib(byte data, int* scale, int* index, int* base) {
324 *scale = (data >> 6) & 3;
325 *index = (data >> 3) & 7;
326 *base = data & 7;
327 }
328
329 typedef const char* (DisassemblerIA32::*RegisterNameMapping)(int reg) const;
330
331 int PrintRightOperandHelper(byte* modrmp, RegisterNameMapping register_name);
332 int PrintRightOperand(byte* modrmp);
333 int PrintRightByteOperand(byte* modrmp);
334 int PrintOperands(const char* mnem, OperandOrder op_order, byte* data);
335 int PrintImmediateOp(byte* data);
336 int F7Instruction(byte* data);
337 int D1D3C1Instruction(byte* data);
338 int JumpShort(byte* data);
339 int JumpConditional(byte* data, const char* comment);
340 int JumpConditionalShort(byte* data, const char* comment);
341 int SetCC(byte* data);
Steve Block3ce2e202009-11-05 08:53:23 +0000342 int CMov(byte* data);
Steve Blocka7e24c12009-10-30 11:49:00 +0000343 int FPUInstruction(byte* data);
Steve Blockd0582a62009-12-15 09:54:21 +0000344 int MemoryFPUInstruction(int escape_opcode, int regop, byte* modrm_start);
345 int RegisterFPUInstruction(int escape_opcode, byte modrm_byte);
Steve Blocka7e24c12009-10-30 11:49:00 +0000346 void AppendToBuffer(const char* format, ...);
347
348
349 void UnimplementedInstruction() {
350 if (abort_on_unimplemented_) {
351 UNIMPLEMENTED();
352 } else {
353 AppendToBuffer("'Unimplemented Instruction'");
354 }
355 }
356};
357
358
359void DisassemblerIA32::AppendToBuffer(const char* format, ...) {
360 v8::internal::Vector<char> buf = tmp_buffer_ + tmp_buffer_pos_;
361 va_list args;
362 va_start(args, format);
363 int result = v8::internal::OS::VSNPrintF(buf, format, args);
364 va_end(args);
365 tmp_buffer_pos_ += result;
366}
367
368int DisassemblerIA32::PrintRightOperandHelper(
369 byte* modrmp,
370 RegisterNameMapping register_name) {
371 int mod, regop, rm;
372 get_modrm(*modrmp, &mod, &regop, &rm);
373 switch (mod) {
374 case 0:
375 if (rm == ebp) {
376 int32_t disp = *reinterpret_cast<int32_t*>(modrmp+1);
377 AppendToBuffer("[0x%x]", disp);
378 return 5;
379 } else if (rm == esp) {
380 byte sib = *(modrmp + 1);
381 int scale, index, base;
382 get_sib(sib, &scale, &index, &base);
383 if (index == esp && base == esp && scale == 0 /*times_1*/) {
384 AppendToBuffer("[%s]", (this->*register_name)(rm));
385 return 2;
386 } else if (base == ebp) {
387 int32_t disp = *reinterpret_cast<int32_t*>(modrmp + 2);
388 AppendToBuffer("[%s*%d+0x%x]",
389 (this->*register_name)(index),
390 1 << scale,
391 disp);
392 return 6;
393 } else if (index != esp && base != ebp) {
394 // [base+index*scale]
395 AppendToBuffer("[%s+%s*%d]",
396 (this->*register_name)(base),
397 (this->*register_name)(index),
398 1 << scale);
399 return 2;
400 } else {
401 UnimplementedInstruction();
402 return 1;
403 }
404 } else {
405 AppendToBuffer("[%s]", (this->*register_name)(rm));
406 return 1;
407 }
408 break;
409 case 1: // fall through
410 case 2:
411 if (rm == esp) {
412 byte sib = *(modrmp + 1);
413 int scale, index, base;
414 get_sib(sib, &scale, &index, &base);
415 int disp =
416 mod == 2 ? *reinterpret_cast<int32_t*>(modrmp + 2) : *(modrmp + 2);
417 if (index == base && index == rm /*esp*/ && scale == 0 /*times_1*/) {
418 AppendToBuffer("[%s+0x%x]", (this->*register_name)(rm), disp);
419 } else {
420 AppendToBuffer("[%s+%s*%d+0x%x]",
421 (this->*register_name)(base),
422 (this->*register_name)(index),
423 1 << scale,
424 disp);
425 }
426 return mod == 2 ? 6 : 3;
427 } else {
428 // No sib.
429 int disp =
430 mod == 2 ? *reinterpret_cast<int32_t*>(modrmp + 1) : *(modrmp + 1);
431 AppendToBuffer("[%s+0x%x]", (this->*register_name)(rm), disp);
432 return mod == 2 ? 5 : 2;
433 }
434 break;
435 case 3:
436 AppendToBuffer("%s", (this->*register_name)(rm));
437 return 1;
438 default:
439 UnimplementedInstruction();
440 return 1;
441 }
442 UNREACHABLE();
443}
444
445
446int DisassemblerIA32::PrintRightOperand(byte* modrmp) {
447 return PrintRightOperandHelper(modrmp, &DisassemblerIA32::NameOfCPURegister);
448}
449
450
451int DisassemblerIA32::PrintRightByteOperand(byte* modrmp) {
452 return PrintRightOperandHelper(modrmp,
453 &DisassemblerIA32::NameOfByteCPURegister);
454}
455
456
457// Returns number of bytes used including the current *data.
458// Writes instruction's mnemonic, left and right operands to 'tmp_buffer_'.
459int DisassemblerIA32::PrintOperands(const char* mnem,
460 OperandOrder op_order,
461 byte* data) {
462 byte modrm = *data;
463 int mod, regop, rm;
464 get_modrm(modrm, &mod, &regop, &rm);
465 int advance = 0;
466 switch (op_order) {
467 case REG_OPER_OP_ORDER: {
468 AppendToBuffer("%s %s,", mnem, NameOfCPURegister(regop));
469 advance = PrintRightOperand(data);
470 break;
471 }
472 case OPER_REG_OP_ORDER: {
473 AppendToBuffer("%s ", mnem);
474 advance = PrintRightOperand(data);
475 AppendToBuffer(",%s", NameOfCPURegister(regop));
476 break;
477 }
478 default:
479 UNREACHABLE();
480 break;
481 }
482 return advance;
483}
484
485
486// Returns number of bytes used by machine instruction, including *data byte.
487// Writes immediate instructions to 'tmp_buffer_'.
488int DisassemblerIA32::PrintImmediateOp(byte* data) {
489 bool sign_extension_bit = (*data & 0x02) != 0;
490 byte modrm = *(data+1);
491 int mod, regop, rm;
492 get_modrm(modrm, &mod, &regop, &rm);
493 const char* mnem = "Imm???";
494 switch (regop) {
495 case 0: mnem = "add"; break;
496 case 1: mnem = "or"; break;
497 case 2: mnem = "adc"; break;
498 case 4: mnem = "and"; break;
499 case 5: mnem = "sub"; break;
500 case 6: mnem = "xor"; break;
501 case 7: mnem = "cmp"; break;
502 default: UnimplementedInstruction();
503 }
504 AppendToBuffer("%s ", mnem);
505 int count = PrintRightOperand(data+1);
506 if (sign_extension_bit) {
507 AppendToBuffer(",0x%x", *(data + 1 + count));
508 return 1 + count + 1 /*int8*/;
509 } else {
510 AppendToBuffer(",0x%x", *reinterpret_cast<int32_t*>(data + 1 + count));
511 return 1 + count + 4 /*int32_t*/;
512 }
513}
514
515
516// Returns number of bytes used, including *data.
517int DisassemblerIA32::F7Instruction(byte* data) {
Steve Blockd0582a62009-12-15 09:54:21 +0000518 ASSERT_EQ(0xF7, *data);
Steve Blocka7e24c12009-10-30 11:49:00 +0000519 byte modrm = *(data+1);
520 int mod, regop, rm;
521 get_modrm(modrm, &mod, &regop, &rm);
522 if (mod == 3 && regop != 0) {
523 const char* mnem = NULL;
524 switch (regop) {
525 case 2: mnem = "not"; break;
526 case 3: mnem = "neg"; break;
527 case 4: mnem = "mul"; break;
528 case 7: mnem = "idiv"; break;
529 default: UnimplementedInstruction();
530 }
531 AppendToBuffer("%s %s", mnem, NameOfCPURegister(rm));
532 return 2;
533 } else if (mod == 3 && regop == eax) {
534 int32_t imm = *reinterpret_cast<int32_t*>(data+2);
535 AppendToBuffer("test %s,0x%x", NameOfCPURegister(rm), imm);
536 return 6;
537 } else if (regop == eax) {
538 AppendToBuffer("test ");
539 int count = PrintRightOperand(data+1);
540 int32_t imm = *reinterpret_cast<int32_t*>(data+1+count);
541 AppendToBuffer(",0x%x", imm);
542 return 1+count+4 /*int32_t*/;
543 } else {
544 UnimplementedInstruction();
545 return 2;
546 }
547}
548
549int DisassemblerIA32::D1D3C1Instruction(byte* data) {
550 byte op = *data;
Steve Blockd0582a62009-12-15 09:54:21 +0000551 ASSERT(op == 0xD1 || op == 0xD3 || op == 0xC1);
Steve Blocka7e24c12009-10-30 11:49:00 +0000552 byte modrm = *(data+1);
553 int mod, regop, rm;
554 get_modrm(modrm, &mod, &regop, &rm);
555 int imm8 = -1;
556 int num_bytes = 2;
557 if (mod == 3) {
558 const char* mnem = NULL;
Steve Blockd0582a62009-12-15 09:54:21 +0000559 switch (regop) {
560 case kROL: mnem = "rol"; break;
561 case kROR: mnem = "ror"; break;
562 case kRCL: mnem = "rcl"; break;
Iain Merrick75681382010-08-19 15:07:18 +0100563 case kRCR: mnem = "rcr"; break;
Steve Blockd0582a62009-12-15 09:54:21 +0000564 case kSHL: mnem = "shl"; break;
565 case KSHR: mnem = "shr"; break;
566 case kSAR: mnem = "sar"; break;
567 default: UnimplementedInstruction();
568 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000569 if (op == 0xD1) {
570 imm8 = 1;
Steve Blocka7e24c12009-10-30 11:49:00 +0000571 } else if (op == 0xC1) {
572 imm8 = *(data+2);
573 num_bytes = 3;
Steve Blocka7e24c12009-10-30 11:49:00 +0000574 } else if (op == 0xD3) {
Steve Blockd0582a62009-12-15 09:54:21 +0000575 // Shift/rotate by cl.
Steve Blocka7e24c12009-10-30 11:49:00 +0000576 }
Steve Blockd0582a62009-12-15 09:54:21 +0000577 ASSERT_NE(NULL, mnem);
Steve Blocka7e24c12009-10-30 11:49:00 +0000578 AppendToBuffer("%s %s,", mnem, NameOfCPURegister(rm));
579 if (imm8 > 0) {
580 AppendToBuffer("%d", imm8);
581 } else {
582 AppendToBuffer("cl");
583 }
584 } else {
585 UnimplementedInstruction();
586 }
587 return num_bytes;
588}
589
590
591// Returns number of bytes used, including *data.
592int DisassemblerIA32::JumpShort(byte* data) {
Steve Blockd0582a62009-12-15 09:54:21 +0000593 ASSERT_EQ(0xEB, *data);
Steve Blocka7e24c12009-10-30 11:49:00 +0000594 byte b = *(data+1);
595 byte* dest = data + static_cast<int8_t>(b) + 2;
596 AppendToBuffer("jmp %s", NameOfAddress(dest));
597 return 2;
598}
599
600
601// Returns number of bytes used, including *data.
602int DisassemblerIA32::JumpConditional(byte* data, const char* comment) {
Steve Blockd0582a62009-12-15 09:54:21 +0000603 ASSERT_EQ(0x0F, *data);
Steve Blocka7e24c12009-10-30 11:49:00 +0000604 byte cond = *(data+1) & 0x0F;
605 byte* dest = data + *reinterpret_cast<int32_t*>(data+2) + 6;
606 const char* mnem = jump_conditional_mnem[cond];
607 AppendToBuffer("%s %s", mnem, NameOfAddress(dest));
608 if (comment != NULL) {
609 AppendToBuffer(", %s", comment);
610 }
611 return 6; // includes 0x0F
612}
613
614
615// Returns number of bytes used, including *data.
616int DisassemblerIA32::JumpConditionalShort(byte* data, const char* comment) {
617 byte cond = *data & 0x0F;
618 byte b = *(data+1);
619 byte* dest = data + static_cast<int8_t>(b) + 2;
620 const char* mnem = jump_conditional_mnem[cond];
621 AppendToBuffer("%s %s", mnem, NameOfAddress(dest));
622 if (comment != NULL) {
623 AppendToBuffer(", %s", comment);
624 }
625 return 2;
626}
627
628
629// Returns number of bytes used, including *data.
630int DisassemblerIA32::SetCC(byte* data) {
Steve Blockd0582a62009-12-15 09:54:21 +0000631 ASSERT_EQ(0x0F, *data);
Steve Blocka7e24c12009-10-30 11:49:00 +0000632 byte cond = *(data+1) & 0x0F;
633 const char* mnem = set_conditional_mnem[cond];
634 AppendToBuffer("%s ", mnem);
635 PrintRightByteOperand(data+2);
Steve Blockd0582a62009-12-15 09:54:21 +0000636 return 3; // Includes 0x0F.
Steve Blocka7e24c12009-10-30 11:49:00 +0000637}
638
639
640// Returns number of bytes used, including *data.
Steve Block3ce2e202009-11-05 08:53:23 +0000641int DisassemblerIA32::CMov(byte* data) {
Steve Blockd0582a62009-12-15 09:54:21 +0000642 ASSERT_EQ(0x0F, *data);
Steve Block3ce2e202009-11-05 08:53:23 +0000643 byte cond = *(data + 1) & 0x0F;
644 const char* mnem = conditional_move_mnem[cond];
645 int op_size = PrintOperands(mnem, REG_OPER_OP_ORDER, data + 2);
646 return 2 + op_size; // includes 0x0F
647}
648
649
650// Returns number of bytes used, including *data.
Steve Blocka7e24c12009-10-30 11:49:00 +0000651int DisassemblerIA32::FPUInstruction(byte* data) {
Steve Blockd0582a62009-12-15 09:54:21 +0000652 byte escape_opcode = *data;
653 ASSERT_EQ(0xD8, escape_opcode & 0xF8);
654 byte modrm_byte = *(data+1);
655
656 if (modrm_byte >= 0xC0) {
657 return RegisterFPUInstruction(escape_opcode, modrm_byte);
658 } else {
659 return MemoryFPUInstruction(escape_opcode, modrm_byte, data+1);
Steve Blocka7e24c12009-10-30 11:49:00 +0000660 }
Steve Blockd0582a62009-12-15 09:54:21 +0000661}
662
663int DisassemblerIA32::MemoryFPUInstruction(int escape_opcode,
664 int modrm_byte,
665 byte* modrm_start) {
666 const char* mnem = "?";
667 int regop = (modrm_byte >> 3) & 0x7; // reg/op field of modrm byte.
668 switch (escape_opcode) {
669 case 0xD9: switch (regop) {
670 case 0: mnem = "fld_s"; break;
671 case 3: mnem = "fstp_s"; break;
672 case 7: mnem = "fstcw"; break;
673 default: UnimplementedInstruction();
674 }
675 break;
676
677 case 0xDB: switch (regop) {
678 case 0: mnem = "fild_s"; break;
679 case 1: mnem = "fisttp_s"; break;
680 case 2: mnem = "fist_s"; break;
681 case 3: mnem = "fistp_s"; break;
682 default: UnimplementedInstruction();
683 }
684 break;
685
686 case 0xDD: switch (regop) {
687 case 0: mnem = "fld_d"; break;
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100688 case 1: mnem = "fisttp_d"; break;
689 case 2: mnem = "fst_d"; break;
Steve Blockd0582a62009-12-15 09:54:21 +0000690 case 3: mnem = "fstp_d"; break;
691 default: UnimplementedInstruction();
692 }
693 break;
694
695 case 0xDF: switch (regop) {
696 case 5: mnem = "fild_d"; break;
697 case 7: mnem = "fistp_d"; break;
698 default: UnimplementedInstruction();
699 }
700 break;
701
702 default: UnimplementedInstruction();
703 }
704 AppendToBuffer("%s ", mnem);
705 int count = PrintRightOperand(modrm_start);
706 return count + 1;
707}
708
709int DisassemblerIA32::RegisterFPUInstruction(int escape_opcode,
710 byte modrm_byte) {
711 bool has_register = false; // Is the FPU register encoded in modrm_byte?
712 const char* mnem = "?";
713
714 switch (escape_opcode) {
715 case 0xD8:
716 UnimplementedInstruction();
717 break;
718
719 case 0xD9:
720 switch (modrm_byte & 0xF8) {
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100721 case 0xC0:
722 mnem = "fld";
723 has_register = true;
724 break;
Steve Blockd0582a62009-12-15 09:54:21 +0000725 case 0xC8:
726 mnem = "fxch";
727 has_register = true;
728 break;
729 default:
730 switch (modrm_byte) {
731 case 0xE0: mnem = "fchs"; break;
732 case 0xE1: mnem = "fabs"; break;
733 case 0xE4: mnem = "ftst"; break;
734 case 0xE8: mnem = "fld1"; break;
Andrei Popescu402d9372010-02-26 13:31:12 +0000735 case 0xEB: mnem = "fldpi"; break;
Steve Blockd0582a62009-12-15 09:54:21 +0000736 case 0xEE: mnem = "fldz"; break;
737 case 0xF5: mnem = "fprem1"; break;
738 case 0xF7: mnem = "fincstp"; break;
739 case 0xF8: mnem = "fprem"; break;
740 case 0xFE: mnem = "fsin"; break;
741 case 0xFF: mnem = "fcos"; break;
742 default: UnimplementedInstruction();
743 }
744 }
745 break;
746
747 case 0xDA:
748 if (modrm_byte == 0xE9) {
749 mnem = "fucompp";
750 } else {
751 UnimplementedInstruction();
752 }
753 break;
754
755 case 0xDB:
756 if ((modrm_byte & 0xF8) == 0xE8) {
757 mnem = "fucomi";
758 has_register = true;
759 } else if (modrm_byte == 0xE2) {
760 mnem = "fclex";
761 } else {
762 UnimplementedInstruction();
763 }
764 break;
765
766 case 0xDC:
767 has_register = true;
768 switch (modrm_byte & 0xF8) {
769 case 0xC0: mnem = "fadd"; break;
770 case 0xE8: mnem = "fsub"; break;
771 case 0xC8: mnem = "fmul"; break;
772 case 0xF8: mnem = "fdiv"; break;
773 default: UnimplementedInstruction();
774 }
775 break;
776
777 case 0xDD:
778 has_register = true;
779 switch (modrm_byte & 0xF8) {
780 case 0xC0: mnem = "ffree"; break;
781 case 0xD8: mnem = "fstp"; break;
782 default: UnimplementedInstruction();
783 }
784 break;
785
786 case 0xDE:
787 if (modrm_byte == 0xD9) {
788 mnem = "fcompp";
789 } else {
790 has_register = true;
791 switch (modrm_byte & 0xF8) {
792 case 0xC0: mnem = "faddp"; break;
793 case 0xE8: mnem = "fsubp"; break;
794 case 0xC8: mnem = "fmulp"; break;
795 case 0xF8: mnem = "fdivp"; break;
796 default: UnimplementedInstruction();
797 }
798 }
799 break;
800
801 case 0xDF:
802 if (modrm_byte == 0xE0) {
803 mnem = "fnstsw_ax";
804 } else if ((modrm_byte & 0xF8) == 0xE8) {
805 mnem = "fucomip";
806 has_register = true;
807 }
808 break;
809
810 default: UnimplementedInstruction();
811 }
812
813 if (has_register) {
814 AppendToBuffer("%s st%d", mnem, modrm_byte & 0x7);
815 } else {
816 AppendToBuffer("%s", mnem);
817 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000818 return 2;
819}
820
821
822// Mnemonics for instructions 0xF0 byte.
823// Returns NULL if the instruction is not handled here.
824static const char* F0Mnem(byte f0byte) {
825 switch (f0byte) {
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100826 case 0x18: return "prefetch";
Steve Blocka7e24c12009-10-30 11:49:00 +0000827 case 0xA2: return "cpuid";
828 case 0x31: return "rdtsc";
829 case 0xBE: return "movsx_b";
830 case 0xBF: return "movsx_w";
831 case 0xB6: return "movzx_b";
832 case 0xB7: return "movzx_w";
833 case 0xAF: return "imul";
834 case 0xA5: return "shld";
835 case 0xAD: return "shrd";
836 case 0xAB: return "bts";
837 default: return NULL;
838 }
839}
840
841
842// Disassembled instruction '*instr' and writes it into 'out_buffer'.
843int DisassemblerIA32::InstructionDecode(v8::internal::Vector<char> out_buffer,
844 byte* instr) {
845 tmp_buffer_pos_ = 0; // starting to write as position 0
846 byte* data = instr;
847 // Check for hints.
848 const char* branch_hint = NULL;
849 // We use these two prefixes only with branch prediction
850 if (*data == 0x3E /*ds*/) {
851 branch_hint = "predicted taken";
852 data++;
853 } else if (*data == 0x2E /*cs*/) {
854 branch_hint = "predicted not taken";
855 data++;
856 }
857 bool processed = true; // Will be set to false if the current instruction
858 // is not in 'instructions' table.
859 const InstructionDesc& idesc = instruction_table.Get(*data);
860 switch (idesc.type) {
861 case ZERO_OPERANDS_INSTR:
862 AppendToBuffer(idesc.mnem);
863 data++;
864 break;
865
866 case TWO_OPERANDS_INSTR:
867 data++;
868 data += PrintOperands(idesc.mnem, idesc.op_order_, data);
869 break;
870
871 case JUMP_CONDITIONAL_SHORT_INSTR:
872 data += JumpConditionalShort(data, branch_hint);
873 break;
874
875 case REGISTER_INSTR:
876 AppendToBuffer("%s %s", idesc.mnem, NameOfCPURegister(*data & 0x07));
877 data++;
878 break;
879
880 case MOVE_REG_INSTR: {
881 byte* addr = reinterpret_cast<byte*>(*reinterpret_cast<int32_t*>(data+1));
882 AppendToBuffer("mov %s,%s",
883 NameOfCPURegister(*data & 0x07),
884 NameOfAddress(addr));
885 data += 5;
886 break;
887 }
888
889 case CALL_JUMP_INSTR: {
890 byte* addr = data + *reinterpret_cast<int32_t*>(data+1) + 5;
891 AppendToBuffer("%s %s", idesc.mnem, NameOfAddress(addr));
892 data += 5;
893 break;
894 }
895
896 case SHORT_IMMEDIATE_INSTR: {
897 byte* addr = reinterpret_cast<byte*>(*reinterpret_cast<int32_t*>(data+1));
898 AppendToBuffer("%s eax, %s", idesc.mnem, NameOfAddress(addr));
899 data += 5;
900 break;
901 }
902
903 case NO_INSTR:
904 processed = false;
905 break;
906
907 default:
908 UNIMPLEMENTED(); // This type is not implemented.
909 }
910 //----------------------------
911 if (!processed) {
912 switch (*data) {
913 case 0xC2:
914 AppendToBuffer("ret 0x%x", *reinterpret_cast<uint16_t*>(data+1));
915 data += 3;
916 break;
917
918 case 0x69: // fall through
919 case 0x6B:
920 { int mod, regop, rm;
921 get_modrm(*(data+1), &mod, &regop, &rm);
922 int32_t imm =
923 *data == 0x6B ? *(data+2) : *reinterpret_cast<int32_t*>(data+2);
924 AppendToBuffer("imul %s,%s,0x%x",
925 NameOfCPURegister(regop),
926 NameOfCPURegister(rm),
927 imm);
928 data += 2 + (*data == 0x6B ? 1 : 4);
929 }
930 break;
931
932 case 0xF6:
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100933 { data++;
934 int mod, regop, rm;
935 get_modrm(*data, &mod, &regop, &rm);
936 if (regop == eax) {
937 AppendToBuffer("test_b ");
938 data += PrintRightOperand(data);
939 int32_t imm = *data;
940 AppendToBuffer(",0x%x", imm);
941 data++;
Steve Blocka7e24c12009-10-30 11:49:00 +0000942 } else {
943 UnimplementedInstruction();
944 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000945 }
946 break;
947
948 case 0x81: // fall through
949 case 0x83: // 0x81 with sign extension bit set
950 data += PrintImmediateOp(data);
951 break;
952
953 case 0x0F:
954 { byte f0byte = *(data+1);
955 const char* f0mnem = F0Mnem(f0byte);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100956 if (f0byte == 0x18) {
957 int mod, regop, rm;
958 get_modrm(*data, &mod, &regop, &rm);
959 const char* suffix[] = {"nta", "1", "2", "3"};
960 AppendToBuffer("%s%s ", f0mnem, suffix[regop & 0x03]);
961 data += PrintRightOperand(data);
962 } else if (f0byte == 0xA2 || f0byte == 0x31) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000963 AppendToBuffer("%s", f0mnem);
964 data += 2;
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100965 } else if (f0byte == 0x28) {
966 data += 2;
967 int mod, regop, rm;
968 get_modrm(*data, &mod, &regop, &rm);
969 AppendToBuffer("movaps %s,%s",
970 NameOfXMMRegister(regop),
971 NameOfXMMRegister(rm));
972 data++;
Steve Blocka7e24c12009-10-30 11:49:00 +0000973 } else if ((f0byte & 0xF0) == 0x80) {
974 data += JumpConditional(data, branch_hint);
975 } else if (f0byte == 0xBE || f0byte == 0xBF || f0byte == 0xB6 ||
976 f0byte == 0xB7 || f0byte == 0xAF) {
977 data += 2;
978 data += PrintOperands(f0mnem, REG_OPER_OP_ORDER, data);
979 } else if ((f0byte & 0xF0) == 0x90) {
980 data += SetCC(data);
Steve Block3ce2e202009-11-05 08:53:23 +0000981 } else if ((f0byte & 0xF0) == 0x40) {
982 data += CMov(data);
Steve Blocka7e24c12009-10-30 11:49:00 +0000983 } else {
984 data += 2;
985 if (f0byte == 0xAB || f0byte == 0xA5 || f0byte == 0xAD) {
986 // shrd, shld, bts
987 AppendToBuffer("%s ", f0mnem);
988 int mod, regop, rm;
989 get_modrm(*data, &mod, &regop, &rm);
990 data += PrintRightOperand(data);
991 if (f0byte == 0xAB) {
992 AppendToBuffer(",%s", NameOfCPURegister(regop));
993 } else {
994 AppendToBuffer(",%s,cl", NameOfCPURegister(regop));
995 }
996 } else {
997 UnimplementedInstruction();
998 }
999 }
1000 }
1001 break;
1002
1003 case 0x8F:
1004 { data++;
1005 int mod, regop, rm;
1006 get_modrm(*data, &mod, &regop, &rm);
1007 if (regop == eax) {
1008 AppendToBuffer("pop ");
1009 data += PrintRightOperand(data);
1010 }
1011 }
1012 break;
1013
1014 case 0xFF:
1015 { data++;
1016 int mod, regop, rm;
1017 get_modrm(*data, &mod, &regop, &rm);
1018 const char* mnem = NULL;
1019 switch (regop) {
1020 case esi: mnem = "push"; break;
1021 case eax: mnem = "inc"; break;
1022 case ecx: mnem = "dec"; break;
1023 case edx: mnem = "call"; break;
1024 case esp: mnem = "jmp"; break;
1025 default: mnem = "???";
1026 }
1027 AppendToBuffer("%s ", mnem);
1028 data += PrintRightOperand(data);
1029 }
1030 break;
1031
1032 case 0xC7: // imm32, fall through
1033 case 0xC6: // imm8
1034 { bool is_byte = *data == 0xC6;
1035 data++;
1036 AppendToBuffer("%s ", is_byte ? "mov_b" : "mov");
1037 data += PrintRightOperand(data);
1038 int32_t imm = is_byte ? *data : *reinterpret_cast<int32_t*>(data);
1039 AppendToBuffer(",0x%x", imm);
1040 data += is_byte ? 1 : 4;
1041 }
1042 break;
1043
1044 case 0x80:
1045 { data++;
Leon Clarkee46be812010-01-19 14:06:41 +00001046 int mod, regop, rm;
1047 get_modrm(*data, &mod, &regop, &rm);
1048 const char* mnem = NULL;
Leon Clarkee46be812010-01-19 14:06:41 +00001049 switch (regop) {
1050 case 5: mnem = "subb"; break;
1051 case 7: mnem = "cmpb"; break;
1052 default: UnimplementedInstruction();
1053 }
1054 AppendToBuffer("%s ", mnem);
Steve Blocka7e24c12009-10-30 11:49:00 +00001055 data += PrintRightOperand(data);
1056 int32_t imm = *data;
1057 AppendToBuffer(",0x%x", imm);
1058 data++;
1059 }
1060 break;
1061
1062 case 0x88: // 8bit, fall through
1063 case 0x89: // 32bit
1064 { bool is_byte = *data == 0x88;
1065 int mod, regop, rm;
1066 data++;
1067 get_modrm(*data, &mod, &regop, &rm);
1068 AppendToBuffer("%s ", is_byte ? "mov_b" : "mov");
1069 data += PrintRightOperand(data);
1070 AppendToBuffer(",%s", NameOfCPURegister(regop));
1071 }
1072 break;
1073
1074 case 0x66: // prefix
1075 data++;
1076 if (*data == 0x8B) {
1077 data++;
1078 data += PrintOperands("mov_w", REG_OPER_OP_ORDER, data);
1079 } else if (*data == 0x89) {
1080 data++;
1081 int mod, regop, rm;
1082 get_modrm(*data, &mod, &regop, &rm);
1083 AppendToBuffer("mov_w ");
1084 data += PrintRightOperand(data);
1085 AppendToBuffer(",%s", NameOfCPURegister(regop));
Steve Block3ce2e202009-11-05 08:53:23 +00001086 } else if (*data == 0x0F) {
1087 data++;
Steve Block6ded16b2010-05-10 14:33:55 +01001088 if (*data == 0x38) {
1089 data++;
1090 if (*data == 0x17) {
1091 data++;
1092 int mod, regop, rm;
1093 get_modrm(*data, &mod, &regop, &rm);
1094 AppendToBuffer("ptest %s,%s",
1095 NameOfXMMRegister(regop),
1096 NameOfXMMRegister(rm));
1097 data++;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001098 } else if (*data == 0x2A) {
1099 // movntdqa
1100 data++;
1101 int mod, regop, rm;
1102 get_modrm(*data, &mod, &regop, &rm);
1103 AppendToBuffer("movntdqa %s,", NameOfXMMRegister(regop));
1104 data += PrintRightOperand(data);
Steve Block6ded16b2010-05-10 14:33:55 +01001105 } else {
1106 UnimplementedInstruction();
1107 }
1108 } else if (*data == 0x2E || *data == 0x2F) {
1109 const char* mnem = (*data == 0x2E) ? "ucomisd" : "comisd";
Steve Block3ce2e202009-11-05 08:53:23 +00001110 data++;
1111 int mod, regop, rm;
1112 get_modrm(*data, &mod, &regop, &rm);
Steve Block6ded16b2010-05-10 14:33:55 +01001113 if (mod == 0x3) {
1114 AppendToBuffer("%s %s,%s", mnem,
1115 NameOfXMMRegister(regop),
1116 NameOfXMMRegister(rm));
1117 data++;
1118 } else {
1119 AppendToBuffer("%s %s,", mnem, NameOfXMMRegister(regop));
1120 data += PrintRightOperand(data);
1121 }
1122 } else if (*data == 0x50) {
1123 data++;
1124 int mod, regop, rm;
1125 get_modrm(*data, &mod, &regop, &rm);
1126 AppendToBuffer("movmskpd %s,%s",
1127 NameOfCPURegister(regop),
Steve Block3ce2e202009-11-05 08:53:23 +00001128 NameOfXMMRegister(rm));
1129 data++;
Leon Clarkee46be812010-01-19 14:06:41 +00001130 } else if (*data == 0x57) {
1131 data++;
1132 int mod, regop, rm;
1133 get_modrm(*data, &mod, &regop, &rm);
1134 AppendToBuffer("xorpd %s,%s",
1135 NameOfXMMRegister(regop),
1136 NameOfXMMRegister(rm));
1137 data++;
Steve Block6ded16b2010-05-10 14:33:55 +01001138 } else if (*data == 0x6E) {
1139 data++;
1140 int mod, regop, rm;
1141 get_modrm(*data, &mod, &regop, &rm);
1142 AppendToBuffer("movd %s,", NameOfXMMRegister(regop));
1143 data += PrintRightOperand(data);
Leon Clarkee46be812010-01-19 14:06:41 +00001144 } else if (*data == 0x6F) {
1145 data++;
1146 int mod, regop, rm;
1147 get_modrm(*data, &mod, &regop, &rm);
1148 AppendToBuffer("movdqa %s,", NameOfXMMRegister(regop));
1149 data += PrintRightOperand(data);
1150 } else if (*data == 0x7F) {
1151 AppendToBuffer("movdqa ");
1152 data++;
1153 int mod, regop, rm;
1154 get_modrm(*data, &mod, &regop, &rm);
1155 data += PrintRightOperand(data);
1156 AppendToBuffer(",%s", NameOfXMMRegister(regop));
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001157 } else if (*data == 0xE7) {
1158 AppendToBuffer("movntdq ");
1159 data++;
1160 int mod, regop, rm;
1161 get_modrm(*data, &mod, &regop, &rm);
1162 data += PrintRightOperand(data);
1163 AppendToBuffer(",%s", NameOfXMMRegister(regop));
Steve Block6ded16b2010-05-10 14:33:55 +01001164 } else if (*data == 0xEF) {
1165 data++;
1166 int mod, regop, rm;
1167 get_modrm(*data, &mod, &regop, &rm);
1168 AppendToBuffer("pxor %s,%s",
1169 NameOfXMMRegister(regop),
1170 NameOfXMMRegister(rm));
1171 data++;
Kristian Monsen0d5e1162010-09-30 15:31:59 +01001172 } else if (*data == 0x73) {
1173 data++;
1174 int mod, regop, rm;
1175 get_modrm(*data, &mod, &regop, &rm);
1176 int8_t imm8 = static_cast<int8_t>(data[1]);
1177 AppendToBuffer("psllq %s,%d",
1178 NameOfXMMRegister(rm),
1179 static_cast<int>(imm8));
1180 data += 2;
1181 } else if (*data == 0x54) {
1182 data++;
1183 int mod, regop, rm;
1184 get_modrm(*data, &mod, &regop, &rm);
1185 AppendToBuffer("andpd %s,%s",
1186 NameOfXMMRegister(regop),
1187 NameOfXMMRegister(rm));
1188 data++;
Steve Block3ce2e202009-11-05 08:53:23 +00001189 } else {
1190 UnimplementedInstruction();
1191 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001192 } else {
1193 UnimplementedInstruction();
1194 }
1195 break;
1196
1197 case 0xFE:
1198 { data++;
1199 int mod, regop, rm;
1200 get_modrm(*data, &mod, &regop, &rm);
Kristian Monsen0d5e1162010-09-30 15:31:59 +01001201 if (regop == ecx) {
1202 AppendToBuffer("dec_b ");
1203 data += PrintRightOperand(data);
Steve Blocka7e24c12009-10-30 11:49:00 +00001204 } else {
1205 UnimplementedInstruction();
1206 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001207 }
1208 break;
1209
1210 case 0x68:
1211 AppendToBuffer("push 0x%x", *reinterpret_cast<int32_t*>(data+1));
1212 data += 5;
1213 break;
1214
1215 case 0x6A:
1216 AppendToBuffer("push 0x%x", *reinterpret_cast<int8_t*>(data + 1));
1217 data += 2;
1218 break;
1219
1220 case 0xA8:
1221 AppendToBuffer("test al,0x%x", *reinterpret_cast<uint8_t*>(data+1));
1222 data += 2;
1223 break;
1224
Leon Clarkee46be812010-01-19 14:06:41 +00001225 case 0x2C:
1226 AppendToBuffer("subb eax,0x%x", *reinterpret_cast<uint8_t*>(data+1));
1227 data += 2;
1228 break;
1229
Steve Blocka7e24c12009-10-30 11:49:00 +00001230 case 0xA9:
1231 AppendToBuffer("test eax,0x%x", *reinterpret_cast<int32_t*>(data+1));
1232 data += 5;
1233 break;
1234
1235 case 0xD1: // fall through
1236 case 0xD3: // fall through
1237 case 0xC1:
1238 data += D1D3C1Instruction(data);
1239 break;
1240
1241 case 0xD9: // fall through
1242 case 0xDA: // fall through
1243 case 0xDB: // fall through
1244 case 0xDC: // fall through
1245 case 0xDD: // fall through
1246 case 0xDE: // fall through
1247 case 0xDF:
1248 data += FPUInstruction(data);
1249 break;
1250
1251 case 0xEB:
1252 data += JumpShort(data);
1253 break;
1254
1255 case 0xF2:
1256 if (*(data+1) == 0x0F) {
1257 byte b2 = *(data+2);
1258 if (b2 == 0x11) {
1259 AppendToBuffer("movsd ");
1260 data += 3;
1261 int mod, regop, rm;
1262 get_modrm(*data, &mod, &regop, &rm);
1263 data += PrintRightOperand(data);
1264 AppendToBuffer(",%s", NameOfXMMRegister(regop));
1265 } else if (b2 == 0x10) {
1266 data += 3;
1267 int mod, regop, rm;
1268 get_modrm(*data, &mod, &regop, &rm);
1269 AppendToBuffer("movsd %s,", NameOfXMMRegister(regop));
1270 data += PrintRightOperand(data);
1271 } else {
1272 const char* mnem = "?";
1273 switch (b2) {
1274 case 0x2A: mnem = "cvtsi2sd"; break;
Steve Block6ded16b2010-05-10 14:33:55 +01001275 case 0x2C: mnem = "cvttsd2si"; break;
1276 case 0x51: mnem = "sqrtsd"; break;
Steve Blocka7e24c12009-10-30 11:49:00 +00001277 case 0x58: mnem = "addsd"; break;
1278 case 0x59: mnem = "mulsd"; break;
1279 case 0x5C: mnem = "subsd"; break;
1280 case 0x5E: mnem = "divsd"; break;
1281 }
1282 data += 3;
1283 int mod, regop, rm;
1284 get_modrm(*data, &mod, &regop, &rm);
1285 if (b2 == 0x2A) {
Steve Block6ded16b2010-05-10 14:33:55 +01001286 if (mod != 0x3) {
1287 AppendToBuffer("%s %s,", mnem, NameOfXMMRegister(regop));
1288 data += PrintRightOperand(data);
1289 } else {
1290 AppendToBuffer("%s %s,%s",
1291 mnem,
1292 NameOfXMMRegister(regop),
1293 NameOfCPURegister(rm));
1294 data++;
1295 }
1296 } else if (b2 == 0x2C) {
1297 if (mod != 0x3) {
1298 AppendToBuffer("%s %s,", mnem, NameOfCPURegister(regop));
1299 data += PrintRightOperand(data);
1300 } else {
1301 AppendToBuffer("%s %s,%s",
1302 mnem,
1303 NameOfCPURegister(regop),
1304 NameOfXMMRegister(rm));
1305 data++;
1306 }
Kristian Monsen0d5e1162010-09-30 15:31:59 +01001307 } else if (b2 == 0xC2) {
1308 // Intel manual 2A, Table 3-18.
1309 const char* const pseudo_op[] = {
1310 "cmpeqsd",
1311 "cmpltsd",
1312 "cmplesd",
1313 "cmpunordsd",
1314 "cmpneqsd",
1315 "cmpnltsd",
1316 "cmpnlesd",
1317 "cmpordsd"
1318 };
1319 AppendToBuffer("%s %s,%s",
1320 pseudo_op[data[1]],
1321 NameOfXMMRegister(regop),
1322 NameOfXMMRegister(rm));
1323 data += 2;
Steve Blocka7e24c12009-10-30 11:49:00 +00001324 } else {
Steve Block6ded16b2010-05-10 14:33:55 +01001325 if (mod != 0x3) {
1326 AppendToBuffer("%s %s,", mnem, NameOfXMMRegister(regop));
1327 data += PrintRightOperand(data);
1328 } else {
1329 AppendToBuffer("%s %s,%s",
1330 mnem,
1331 NameOfXMMRegister(regop),
1332 NameOfXMMRegister(rm));
1333 data++;
1334 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001335 }
1336 }
1337 } else {
1338 UnimplementedInstruction();
1339 }
1340 break;
1341
1342 case 0xF3:
Leon Clarkee46be812010-01-19 14:06:41 +00001343 if (*(data+1) == 0x0F) {
1344 if (*(data+2) == 0x2C) {
1345 data += 3;
1346 data += PrintOperands("cvttss2si", REG_OPER_OP_ORDER, data);
Steve Block6ded16b2010-05-10 14:33:55 +01001347 } else if (*(data+2) == 0x5A) {
1348 data += 3;
1349 int mod, regop, rm;
1350 get_modrm(*data, &mod, &regop, &rm);
1351 AppendToBuffer("cvtss2sd %s,%s",
1352 NameOfXMMRegister(regop),
1353 NameOfXMMRegister(rm));
1354 data++;
Leon Clarkee46be812010-01-19 14:06:41 +00001355 } else if (*(data+2) == 0x6F) {
1356 data += 3;
1357 int mod, regop, rm;
1358 get_modrm(*data, &mod, &regop, &rm);
1359 AppendToBuffer("movdqu %s,", NameOfXMMRegister(regop));
1360 data += PrintRightOperand(data);
1361 } else if (*(data+2) == 0x7F) {
1362 AppendToBuffer("movdqu ");
1363 data += 3;
1364 int mod, regop, rm;
1365 get_modrm(*data, &mod, &regop, &rm);
1366 data += PrintRightOperand(data);
1367 AppendToBuffer(",%s", NameOfXMMRegister(regop));
1368 } else {
1369 UnimplementedInstruction();
1370 }
1371 } else if (*(data+1) == 0xA5) {
1372 data += 2;
1373 AppendToBuffer("rep_movs");
Steve Block6ded16b2010-05-10 14:33:55 +01001374 } else if (*(data+1) == 0xAB) {
1375 data += 2;
1376 AppendToBuffer("rep_stos");
Steve Blocka7e24c12009-10-30 11:49:00 +00001377 } else {
1378 UnimplementedInstruction();
1379 }
1380 break;
1381
1382 case 0xF7:
1383 data += F7Instruction(data);
1384 break;
1385
1386 default:
1387 UnimplementedInstruction();
1388 }
1389 }
1390
1391 if (tmp_buffer_pos_ < sizeof tmp_buffer_) {
1392 tmp_buffer_[tmp_buffer_pos_] = '\0';
1393 }
1394
1395 int instr_len = data - instr;
Leon Clarkee46be812010-01-19 14:06:41 +00001396 if (instr_len == 0) {
1397 printf("%02x", *data);
1398 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001399 ASSERT(instr_len > 0); // Ensure progress.
1400
1401 int outp = 0;
1402 // Instruction bytes.
1403 for (byte* bp = instr; bp < data; bp++) {
1404 outp += v8::internal::OS::SNPrintF(out_buffer + outp,
1405 "%02x",
1406 *bp);
1407 }
1408 for (int i = 6 - instr_len; i >= 0; i--) {
1409 outp += v8::internal::OS::SNPrintF(out_buffer + outp,
1410 " ");
1411 }
1412
1413 outp += v8::internal::OS::SNPrintF(out_buffer + outp,
1414 " %s",
1415 tmp_buffer_.start());
1416 return instr_len;
Kristian Monsen0d5e1162010-09-30 15:31:59 +01001417} // NOLINT (function is too long)
Steve Blocka7e24c12009-10-30 11:49:00 +00001418
1419
1420//------------------------------------------------------------------------------
1421
1422
1423static const char* cpu_regs[8] = {
1424 "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi"
1425};
1426
1427
1428static const char* byte_cpu_regs[8] = {
1429 "al", "cl", "dl", "bl", "ah", "ch", "dh", "bh"
1430};
1431
1432
1433static const char* xmm_regs[8] = {
1434 "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7"
1435};
1436
1437
1438const char* NameConverter::NameOfAddress(byte* addr) const {
1439 static v8::internal::EmbeddedVector<char, 32> tmp_buffer;
1440 v8::internal::OS::SNPrintF(tmp_buffer, "%p", addr);
1441 return tmp_buffer.start();
1442}
1443
1444
1445const char* NameConverter::NameOfConstant(byte* addr) const {
1446 return NameOfAddress(addr);
1447}
1448
1449
1450const char* NameConverter::NameOfCPURegister(int reg) const {
1451 if (0 <= reg && reg < 8) return cpu_regs[reg];
1452 return "noreg";
1453}
1454
1455
1456const char* NameConverter::NameOfByteCPURegister(int reg) const {
1457 if (0 <= reg && reg < 8) return byte_cpu_regs[reg];
1458 return "noreg";
1459}
1460
1461
1462const char* NameConverter::NameOfXMMRegister(int reg) const {
1463 if (0 <= reg && reg < 8) return xmm_regs[reg];
1464 return "noxmmreg";
1465}
1466
1467
1468const char* NameConverter::NameInCode(byte* addr) const {
1469 // IA32 does not embed debug strings at the moment.
1470 UNREACHABLE();
1471 return "";
1472}
1473
1474
1475//------------------------------------------------------------------------------
1476
1477Disassembler::Disassembler(const NameConverter& converter)
1478 : converter_(converter) {}
1479
1480
1481Disassembler::~Disassembler() {}
1482
1483
1484int Disassembler::InstructionDecode(v8::internal::Vector<char> buffer,
1485 byte* instruction) {
1486 DisassemblerIA32 d(converter_, false /*do not crash if unimplemented*/);
1487 return d.InstructionDecode(buffer, instruction);
1488}
1489
1490
1491// The IA-32 assembler does not currently use constant pools.
1492int Disassembler::ConstantPoolSizeAt(byte* instruction) { return -1; }
1493
1494
1495/*static*/ void Disassembler::Disassemble(FILE* f, byte* begin, byte* end) {
1496 NameConverter converter;
1497 Disassembler d(converter);
1498 for (byte* pc = begin; pc < end;) {
1499 v8::internal::EmbeddedVector<char, 128> buffer;
1500 buffer[0] = '\0';
1501 byte* prev_pc = pc;
1502 pc += d.InstructionDecode(buffer, pc);
1503 fprintf(f, "%p", prev_pc);
1504 fprintf(f, " ");
1505
1506 for (byte* bp = prev_pc; bp < pc; bp++) {
1507 fprintf(f, "%02x", *bp);
1508 }
1509 for (int i = 6 - (pc - prev_pc); i >= 0; i--) {
1510 fprintf(f, " ");
1511 }
1512 fprintf(f, " %s\n", buffer.start());
1513 }
1514}
1515
1516
1517} // namespace disasm
Leon Clarkef7060e22010-06-03 12:02:55 +01001518
1519#endif // V8_TARGET_ARCH_IA32