blob: dfbcbb76d5eb5917976faf7070515a7368690e35 [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;
Ben Murdochb0fe1622011-05-05 13:52:32 +0100736 case 0xED: mnem = "fldln2"; break;
Steve Blockd0582a62009-12-15 09:54:21 +0000737 case 0xEE: mnem = "fldz"; break;
Ben Murdochb0fe1622011-05-05 13:52:32 +0100738 case 0xF1: mnem = "fyl2x"; break;
Steve Blockd0582a62009-12-15 09:54:21 +0000739 case 0xF5: mnem = "fprem1"; break;
740 case 0xF7: mnem = "fincstp"; break;
741 case 0xF8: mnem = "fprem"; break;
742 case 0xFE: mnem = "fsin"; break;
743 case 0xFF: mnem = "fcos"; break;
744 default: UnimplementedInstruction();
745 }
746 }
747 break;
748
749 case 0xDA:
750 if (modrm_byte == 0xE9) {
751 mnem = "fucompp";
752 } else {
753 UnimplementedInstruction();
754 }
755 break;
756
757 case 0xDB:
758 if ((modrm_byte & 0xF8) == 0xE8) {
759 mnem = "fucomi";
760 has_register = true;
761 } else if (modrm_byte == 0xE2) {
762 mnem = "fclex";
763 } else {
764 UnimplementedInstruction();
765 }
766 break;
767
768 case 0xDC:
769 has_register = true;
770 switch (modrm_byte & 0xF8) {
771 case 0xC0: mnem = "fadd"; break;
772 case 0xE8: mnem = "fsub"; break;
773 case 0xC8: mnem = "fmul"; break;
774 case 0xF8: mnem = "fdiv"; break;
775 default: UnimplementedInstruction();
776 }
777 break;
778
779 case 0xDD:
780 has_register = true;
781 switch (modrm_byte & 0xF8) {
782 case 0xC0: mnem = "ffree"; break;
783 case 0xD8: mnem = "fstp"; break;
784 default: UnimplementedInstruction();
785 }
786 break;
787
788 case 0xDE:
789 if (modrm_byte == 0xD9) {
790 mnem = "fcompp";
791 } else {
792 has_register = true;
793 switch (modrm_byte & 0xF8) {
794 case 0xC0: mnem = "faddp"; break;
795 case 0xE8: mnem = "fsubp"; break;
796 case 0xC8: mnem = "fmulp"; break;
797 case 0xF8: mnem = "fdivp"; break;
798 default: UnimplementedInstruction();
799 }
800 }
801 break;
802
803 case 0xDF:
804 if (modrm_byte == 0xE0) {
805 mnem = "fnstsw_ax";
806 } else if ((modrm_byte & 0xF8) == 0xE8) {
807 mnem = "fucomip";
808 has_register = true;
809 }
810 break;
811
812 default: UnimplementedInstruction();
813 }
814
815 if (has_register) {
816 AppendToBuffer("%s st%d", mnem, modrm_byte & 0x7);
817 } else {
818 AppendToBuffer("%s", mnem);
819 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000820 return 2;
821}
822
823
824// Mnemonics for instructions 0xF0 byte.
825// Returns NULL if the instruction is not handled here.
826static const char* F0Mnem(byte f0byte) {
827 switch (f0byte) {
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100828 case 0x18: return "prefetch";
Steve Blocka7e24c12009-10-30 11:49:00 +0000829 case 0xA2: return "cpuid";
830 case 0x31: return "rdtsc";
831 case 0xBE: return "movsx_b";
832 case 0xBF: return "movsx_w";
833 case 0xB6: return "movzx_b";
834 case 0xB7: return "movzx_w";
835 case 0xAF: return "imul";
836 case 0xA5: return "shld";
837 case 0xAD: return "shrd";
838 case 0xAB: return "bts";
839 default: return NULL;
840 }
841}
842
843
844// Disassembled instruction '*instr' and writes it into 'out_buffer'.
845int DisassemblerIA32::InstructionDecode(v8::internal::Vector<char> out_buffer,
846 byte* instr) {
847 tmp_buffer_pos_ = 0; // starting to write as position 0
848 byte* data = instr;
849 // Check for hints.
850 const char* branch_hint = NULL;
851 // We use these two prefixes only with branch prediction
852 if (*data == 0x3E /*ds*/) {
853 branch_hint = "predicted taken";
854 data++;
855 } else if (*data == 0x2E /*cs*/) {
856 branch_hint = "predicted not taken";
857 data++;
858 }
859 bool processed = true; // Will be set to false if the current instruction
860 // is not in 'instructions' table.
861 const InstructionDesc& idesc = instruction_table.Get(*data);
862 switch (idesc.type) {
863 case ZERO_OPERANDS_INSTR:
864 AppendToBuffer(idesc.mnem);
865 data++;
866 break;
867
868 case TWO_OPERANDS_INSTR:
869 data++;
870 data += PrintOperands(idesc.mnem, idesc.op_order_, data);
871 break;
872
873 case JUMP_CONDITIONAL_SHORT_INSTR:
874 data += JumpConditionalShort(data, branch_hint);
875 break;
876
877 case REGISTER_INSTR:
878 AppendToBuffer("%s %s", idesc.mnem, NameOfCPURegister(*data & 0x07));
879 data++;
880 break;
881
882 case MOVE_REG_INSTR: {
883 byte* addr = reinterpret_cast<byte*>(*reinterpret_cast<int32_t*>(data+1));
884 AppendToBuffer("mov %s,%s",
885 NameOfCPURegister(*data & 0x07),
886 NameOfAddress(addr));
887 data += 5;
888 break;
889 }
890
891 case CALL_JUMP_INSTR: {
892 byte* addr = data + *reinterpret_cast<int32_t*>(data+1) + 5;
893 AppendToBuffer("%s %s", idesc.mnem, NameOfAddress(addr));
894 data += 5;
895 break;
896 }
897
898 case SHORT_IMMEDIATE_INSTR: {
899 byte* addr = reinterpret_cast<byte*>(*reinterpret_cast<int32_t*>(data+1));
900 AppendToBuffer("%s eax, %s", idesc.mnem, NameOfAddress(addr));
901 data += 5;
902 break;
903 }
904
905 case NO_INSTR:
906 processed = false;
907 break;
908
909 default:
910 UNIMPLEMENTED(); // This type is not implemented.
911 }
912 //----------------------------
913 if (!processed) {
914 switch (*data) {
915 case 0xC2:
916 AppendToBuffer("ret 0x%x", *reinterpret_cast<uint16_t*>(data+1));
917 data += 3;
918 break;
919
920 case 0x69: // fall through
921 case 0x6B:
922 { int mod, regop, rm;
923 get_modrm(*(data+1), &mod, &regop, &rm);
924 int32_t imm =
925 *data == 0x6B ? *(data+2) : *reinterpret_cast<int32_t*>(data+2);
926 AppendToBuffer("imul %s,%s,0x%x",
927 NameOfCPURegister(regop),
928 NameOfCPURegister(rm),
929 imm);
930 data += 2 + (*data == 0x6B ? 1 : 4);
931 }
932 break;
933
934 case 0xF6:
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100935 { data++;
936 int mod, regop, rm;
937 get_modrm(*data, &mod, &regop, &rm);
938 if (regop == eax) {
939 AppendToBuffer("test_b ");
940 data += PrintRightOperand(data);
941 int32_t imm = *data;
942 AppendToBuffer(",0x%x", imm);
943 data++;
Steve Blocka7e24c12009-10-30 11:49:00 +0000944 } else {
945 UnimplementedInstruction();
946 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000947 }
948 break;
949
950 case 0x81: // fall through
951 case 0x83: // 0x81 with sign extension bit set
952 data += PrintImmediateOp(data);
953 break;
954
955 case 0x0F:
956 { byte f0byte = *(data+1);
957 const char* f0mnem = F0Mnem(f0byte);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100958 if (f0byte == 0x18) {
959 int mod, regop, rm;
960 get_modrm(*data, &mod, &regop, &rm);
961 const char* suffix[] = {"nta", "1", "2", "3"};
962 AppendToBuffer("%s%s ", f0mnem, suffix[regop & 0x03]);
963 data += PrintRightOperand(data);
964 } else if (f0byte == 0xA2 || f0byte == 0x31) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000965 AppendToBuffer("%s", f0mnem);
966 data += 2;
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100967 } else if (f0byte == 0x28) {
968 data += 2;
969 int mod, regop, rm;
970 get_modrm(*data, &mod, &regop, &rm);
971 AppendToBuffer("movaps %s,%s",
972 NameOfXMMRegister(regop),
973 NameOfXMMRegister(rm));
974 data++;
Steve Blocka7e24c12009-10-30 11:49:00 +0000975 } else if ((f0byte & 0xF0) == 0x80) {
976 data += JumpConditional(data, branch_hint);
977 } else if (f0byte == 0xBE || f0byte == 0xBF || f0byte == 0xB6 ||
978 f0byte == 0xB7 || f0byte == 0xAF) {
979 data += 2;
980 data += PrintOperands(f0mnem, REG_OPER_OP_ORDER, data);
981 } else if ((f0byte & 0xF0) == 0x90) {
982 data += SetCC(data);
Steve Block3ce2e202009-11-05 08:53:23 +0000983 } else if ((f0byte & 0xF0) == 0x40) {
984 data += CMov(data);
Steve Blocka7e24c12009-10-30 11:49:00 +0000985 } else {
986 data += 2;
987 if (f0byte == 0xAB || f0byte == 0xA5 || f0byte == 0xAD) {
988 // shrd, shld, bts
989 AppendToBuffer("%s ", f0mnem);
990 int mod, regop, rm;
991 get_modrm(*data, &mod, &regop, &rm);
992 data += PrintRightOperand(data);
993 if (f0byte == 0xAB) {
994 AppendToBuffer(",%s", NameOfCPURegister(regop));
995 } else {
996 AppendToBuffer(",%s,cl", NameOfCPURegister(regop));
997 }
998 } else {
999 UnimplementedInstruction();
1000 }
1001 }
1002 }
1003 break;
1004
1005 case 0x8F:
1006 { data++;
1007 int mod, regop, rm;
1008 get_modrm(*data, &mod, &regop, &rm);
1009 if (regop == eax) {
1010 AppendToBuffer("pop ");
1011 data += PrintRightOperand(data);
1012 }
1013 }
1014 break;
1015
1016 case 0xFF:
1017 { data++;
1018 int mod, regop, rm;
1019 get_modrm(*data, &mod, &regop, &rm);
1020 const char* mnem = NULL;
1021 switch (regop) {
1022 case esi: mnem = "push"; break;
1023 case eax: mnem = "inc"; break;
1024 case ecx: mnem = "dec"; break;
1025 case edx: mnem = "call"; break;
1026 case esp: mnem = "jmp"; break;
1027 default: mnem = "???";
1028 }
1029 AppendToBuffer("%s ", mnem);
1030 data += PrintRightOperand(data);
1031 }
1032 break;
1033
1034 case 0xC7: // imm32, fall through
1035 case 0xC6: // imm8
1036 { bool is_byte = *data == 0xC6;
1037 data++;
1038 AppendToBuffer("%s ", is_byte ? "mov_b" : "mov");
1039 data += PrintRightOperand(data);
1040 int32_t imm = is_byte ? *data : *reinterpret_cast<int32_t*>(data);
1041 AppendToBuffer(",0x%x", imm);
1042 data += is_byte ? 1 : 4;
1043 }
1044 break;
1045
1046 case 0x80:
1047 { data++;
Leon Clarkee46be812010-01-19 14:06:41 +00001048 int mod, regop, rm;
1049 get_modrm(*data, &mod, &regop, &rm);
1050 const char* mnem = NULL;
Leon Clarkee46be812010-01-19 14:06:41 +00001051 switch (regop) {
1052 case 5: mnem = "subb"; break;
1053 case 7: mnem = "cmpb"; break;
1054 default: UnimplementedInstruction();
1055 }
1056 AppendToBuffer("%s ", mnem);
Steve Blocka7e24c12009-10-30 11:49:00 +00001057 data += PrintRightOperand(data);
1058 int32_t imm = *data;
1059 AppendToBuffer(",0x%x", imm);
1060 data++;
1061 }
1062 break;
1063
1064 case 0x88: // 8bit, fall through
1065 case 0x89: // 32bit
1066 { bool is_byte = *data == 0x88;
1067 int mod, regop, rm;
1068 data++;
1069 get_modrm(*data, &mod, &regop, &rm);
1070 AppendToBuffer("%s ", is_byte ? "mov_b" : "mov");
1071 data += PrintRightOperand(data);
1072 AppendToBuffer(",%s", NameOfCPURegister(regop));
1073 }
1074 break;
1075
1076 case 0x66: // prefix
1077 data++;
1078 if (*data == 0x8B) {
1079 data++;
1080 data += PrintOperands("mov_w", REG_OPER_OP_ORDER, data);
1081 } else if (*data == 0x89) {
1082 data++;
1083 int mod, regop, rm;
1084 get_modrm(*data, &mod, &regop, &rm);
1085 AppendToBuffer("mov_w ");
1086 data += PrintRightOperand(data);
1087 AppendToBuffer(",%s", NameOfCPURegister(regop));
Steve Block3ce2e202009-11-05 08:53:23 +00001088 } else if (*data == 0x0F) {
1089 data++;
Steve Block6ded16b2010-05-10 14:33:55 +01001090 if (*data == 0x38) {
1091 data++;
1092 if (*data == 0x17) {
1093 data++;
1094 int mod, regop, rm;
1095 get_modrm(*data, &mod, &regop, &rm);
1096 AppendToBuffer("ptest %s,%s",
1097 NameOfXMMRegister(regop),
1098 NameOfXMMRegister(rm));
1099 data++;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001100 } else if (*data == 0x2A) {
1101 // movntdqa
1102 data++;
1103 int mod, regop, rm;
1104 get_modrm(*data, &mod, &regop, &rm);
1105 AppendToBuffer("movntdqa %s,", NameOfXMMRegister(regop));
1106 data += PrintRightOperand(data);
Steve Block6ded16b2010-05-10 14:33:55 +01001107 } else {
1108 UnimplementedInstruction();
1109 }
Ben Murdochb0fe1622011-05-05 13:52:32 +01001110 } else if (*data == 0x3A) {
1111 data++;
1112 if (*data == 0x16) {
1113 data++;
1114 int mod, regop, rm;
1115 get_modrm(*data, &mod, &regop, &rm);
1116 int8_t imm8 = static_cast<int8_t>(data[1]);
1117 AppendToBuffer("pextrd %s,%s,%d",
1118 NameOfXMMRegister(regop),
1119 NameOfXMMRegister(rm),
1120 static_cast<int>(imm8));
1121 data += 2;
1122 } else {
1123 UnimplementedInstruction();
1124 }
Steve Block6ded16b2010-05-10 14:33:55 +01001125 } else if (*data == 0x2E || *data == 0x2F) {
1126 const char* mnem = (*data == 0x2E) ? "ucomisd" : "comisd";
Steve Block3ce2e202009-11-05 08:53:23 +00001127 data++;
1128 int mod, regop, rm;
1129 get_modrm(*data, &mod, &regop, &rm);
Steve Block6ded16b2010-05-10 14:33:55 +01001130 if (mod == 0x3) {
1131 AppendToBuffer("%s %s,%s", mnem,
1132 NameOfXMMRegister(regop),
1133 NameOfXMMRegister(rm));
1134 data++;
1135 } else {
1136 AppendToBuffer("%s %s,", mnem, NameOfXMMRegister(regop));
1137 data += PrintRightOperand(data);
1138 }
1139 } else if (*data == 0x50) {
1140 data++;
1141 int mod, regop, rm;
1142 get_modrm(*data, &mod, &regop, &rm);
1143 AppendToBuffer("movmskpd %s,%s",
1144 NameOfCPURegister(regop),
Steve Block3ce2e202009-11-05 08:53:23 +00001145 NameOfXMMRegister(rm));
1146 data++;
Ben Murdochb0fe1622011-05-05 13:52:32 +01001147 } else if (*data == 0x54) {
1148 data++;
1149 int mod, regop, rm;
1150 get_modrm(*data, &mod, &regop, &rm);
1151 AppendToBuffer("andpd %s,%s",
1152 NameOfXMMRegister(regop),
1153 NameOfXMMRegister(rm));
1154 data++;
Leon Clarkee46be812010-01-19 14:06:41 +00001155 } else if (*data == 0x57) {
1156 data++;
1157 int mod, regop, rm;
1158 get_modrm(*data, &mod, &regop, &rm);
1159 AppendToBuffer("xorpd %s,%s",
1160 NameOfXMMRegister(regop),
1161 NameOfXMMRegister(rm));
1162 data++;
Steve Block6ded16b2010-05-10 14:33:55 +01001163 } else if (*data == 0x6E) {
1164 data++;
1165 int mod, regop, rm;
1166 get_modrm(*data, &mod, &regop, &rm);
1167 AppendToBuffer("movd %s,", NameOfXMMRegister(regop));
1168 data += PrintRightOperand(data);
Leon Clarkee46be812010-01-19 14:06:41 +00001169 } else if (*data == 0x6F) {
1170 data++;
1171 int mod, regop, rm;
1172 get_modrm(*data, &mod, &regop, &rm);
1173 AppendToBuffer("movdqa %s,", NameOfXMMRegister(regop));
1174 data += PrintRightOperand(data);
Ben Murdochb0fe1622011-05-05 13:52:32 +01001175 } else if (*data == 0x70) {
1176 data++;
1177 int mod, regop, rm;
1178 get_modrm(*data, &mod, &regop, &rm);
1179 int8_t imm8 = static_cast<int8_t>(data[1]);
1180 AppendToBuffer("pshufd %s,%s,%d",
1181 NameOfXMMRegister(regop),
1182 NameOfXMMRegister(rm),
1183 static_cast<int>(imm8));
1184 data += 2;
1185 } else if (*data == 0x73) {
1186 data++;
1187 int mod, regop, rm;
1188 get_modrm(*data, &mod, &regop, &rm);
1189 int8_t imm8 = static_cast<int8_t>(data[1]);
1190 AppendToBuffer("psllq %s,%d",
1191 NameOfXMMRegister(rm),
1192 static_cast<int>(imm8));
1193 data += 2;
Leon Clarkee46be812010-01-19 14:06:41 +00001194 } else if (*data == 0x7F) {
1195 AppendToBuffer("movdqa ");
1196 data++;
1197 int mod, regop, rm;
1198 get_modrm(*data, &mod, &regop, &rm);
1199 data += PrintRightOperand(data);
1200 AppendToBuffer(",%s", NameOfXMMRegister(regop));
Ben Murdochb0fe1622011-05-05 13:52:32 +01001201 } else if (*data == 0x7E) {
1202 data++;
1203 int mod, regop, rm;
1204 get_modrm(*data, &mod, &regop, &rm);
1205 AppendToBuffer("movd ");
1206 data += PrintRightOperand(data);
1207 AppendToBuffer(",%s", NameOfXMMRegister(regop));
1208 } else if (*data == 0xDB) {
1209 data++;
1210 int mod, regop, rm;
1211 get_modrm(*data, &mod, &regop, &rm);
1212 AppendToBuffer("pand %s,%s",
1213 NameOfXMMRegister(regop),
1214 NameOfXMMRegister(rm));
1215 data++;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001216 } else if (*data == 0xE7) {
1217 AppendToBuffer("movntdq ");
1218 data++;
1219 int mod, regop, rm;
1220 get_modrm(*data, &mod, &regop, &rm);
1221 data += PrintRightOperand(data);
1222 AppendToBuffer(",%s", NameOfXMMRegister(regop));
Steve Block6ded16b2010-05-10 14:33:55 +01001223 } else if (*data == 0xEF) {
Ben Murdochb0fe1622011-05-05 13:52:32 +01001224 data++;
1225 int mod, regop, rm;
1226 get_modrm(*data, &mod, &regop, &rm);
1227 AppendToBuffer("pxor %s,%s",
1228 NameOfXMMRegister(regop),
1229 NameOfXMMRegister(rm));
1230 data++;
Steve Block3ce2e202009-11-05 08:53:23 +00001231 } else {
1232 UnimplementedInstruction();
1233 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001234 } else {
1235 UnimplementedInstruction();
1236 }
1237 break;
1238
1239 case 0xFE:
1240 { data++;
1241 int mod, regop, rm;
1242 get_modrm(*data, &mod, &regop, &rm);
Kristian Monsen0d5e1162010-09-30 15:31:59 +01001243 if (regop == ecx) {
1244 AppendToBuffer("dec_b ");
1245 data += PrintRightOperand(data);
Steve Blocka7e24c12009-10-30 11:49:00 +00001246 } else {
1247 UnimplementedInstruction();
1248 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001249 }
1250 break;
1251
1252 case 0x68:
1253 AppendToBuffer("push 0x%x", *reinterpret_cast<int32_t*>(data+1));
1254 data += 5;
1255 break;
1256
1257 case 0x6A:
1258 AppendToBuffer("push 0x%x", *reinterpret_cast<int8_t*>(data + 1));
1259 data += 2;
1260 break;
1261
1262 case 0xA8:
1263 AppendToBuffer("test al,0x%x", *reinterpret_cast<uint8_t*>(data+1));
1264 data += 2;
1265 break;
1266
Leon Clarkee46be812010-01-19 14:06:41 +00001267 case 0x2C:
1268 AppendToBuffer("subb eax,0x%x", *reinterpret_cast<uint8_t*>(data+1));
1269 data += 2;
1270 break;
1271
Steve Blocka7e24c12009-10-30 11:49:00 +00001272 case 0xA9:
1273 AppendToBuffer("test eax,0x%x", *reinterpret_cast<int32_t*>(data+1));
1274 data += 5;
1275 break;
1276
1277 case 0xD1: // fall through
1278 case 0xD3: // fall through
1279 case 0xC1:
1280 data += D1D3C1Instruction(data);
1281 break;
1282
1283 case 0xD9: // fall through
1284 case 0xDA: // fall through
1285 case 0xDB: // fall through
1286 case 0xDC: // fall through
1287 case 0xDD: // fall through
1288 case 0xDE: // fall through
1289 case 0xDF:
1290 data += FPUInstruction(data);
1291 break;
1292
1293 case 0xEB:
1294 data += JumpShort(data);
1295 break;
1296
1297 case 0xF2:
1298 if (*(data+1) == 0x0F) {
1299 byte b2 = *(data+2);
1300 if (b2 == 0x11) {
1301 AppendToBuffer("movsd ");
1302 data += 3;
1303 int mod, regop, rm;
1304 get_modrm(*data, &mod, &regop, &rm);
1305 data += PrintRightOperand(data);
1306 AppendToBuffer(",%s", NameOfXMMRegister(regop));
1307 } else if (b2 == 0x10) {
1308 data += 3;
1309 int mod, regop, rm;
1310 get_modrm(*data, &mod, &regop, &rm);
1311 AppendToBuffer("movsd %s,", NameOfXMMRegister(regop));
1312 data += PrintRightOperand(data);
1313 } else {
1314 const char* mnem = "?";
1315 switch (b2) {
1316 case 0x2A: mnem = "cvtsi2sd"; break;
Steve Block6ded16b2010-05-10 14:33:55 +01001317 case 0x2C: mnem = "cvttsd2si"; break;
1318 case 0x51: mnem = "sqrtsd"; break;
Steve Blocka7e24c12009-10-30 11:49:00 +00001319 case 0x58: mnem = "addsd"; break;
1320 case 0x59: mnem = "mulsd"; break;
1321 case 0x5C: mnem = "subsd"; break;
1322 case 0x5E: mnem = "divsd"; break;
1323 }
1324 data += 3;
1325 int mod, regop, rm;
1326 get_modrm(*data, &mod, &regop, &rm);
1327 if (b2 == 0x2A) {
Steve Block6ded16b2010-05-10 14:33:55 +01001328 if (mod != 0x3) {
1329 AppendToBuffer("%s %s,", mnem, NameOfXMMRegister(regop));
1330 data += PrintRightOperand(data);
1331 } else {
1332 AppendToBuffer("%s %s,%s",
1333 mnem,
1334 NameOfXMMRegister(regop),
1335 NameOfCPURegister(rm));
1336 data++;
1337 }
1338 } else if (b2 == 0x2C) {
1339 if (mod != 0x3) {
1340 AppendToBuffer("%s %s,", mnem, NameOfCPURegister(regop));
1341 data += PrintRightOperand(data);
1342 } else {
1343 AppendToBuffer("%s %s,%s",
1344 mnem,
1345 NameOfCPURegister(regop),
1346 NameOfXMMRegister(rm));
1347 data++;
1348 }
Kristian Monsen0d5e1162010-09-30 15:31:59 +01001349 } else if (b2 == 0xC2) {
1350 // Intel manual 2A, Table 3-18.
1351 const char* const pseudo_op[] = {
1352 "cmpeqsd",
1353 "cmpltsd",
1354 "cmplesd",
1355 "cmpunordsd",
1356 "cmpneqsd",
1357 "cmpnltsd",
1358 "cmpnlesd",
1359 "cmpordsd"
1360 };
1361 AppendToBuffer("%s %s,%s",
1362 pseudo_op[data[1]],
1363 NameOfXMMRegister(regop),
1364 NameOfXMMRegister(rm));
1365 data += 2;
Steve Blocka7e24c12009-10-30 11:49:00 +00001366 } else {
Steve Block6ded16b2010-05-10 14:33:55 +01001367 if (mod != 0x3) {
1368 AppendToBuffer("%s %s,", mnem, NameOfXMMRegister(regop));
1369 data += PrintRightOperand(data);
1370 } else {
1371 AppendToBuffer("%s %s,%s",
1372 mnem,
1373 NameOfXMMRegister(regop),
1374 NameOfXMMRegister(rm));
1375 data++;
1376 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001377 }
1378 }
1379 } else {
1380 UnimplementedInstruction();
1381 }
1382 break;
1383
1384 case 0xF3:
Leon Clarkee46be812010-01-19 14:06:41 +00001385 if (*(data+1) == 0x0F) {
1386 if (*(data+2) == 0x2C) {
1387 data += 3;
1388 data += PrintOperands("cvttss2si", REG_OPER_OP_ORDER, data);
Steve Block6ded16b2010-05-10 14:33:55 +01001389 } else if (*(data+2) == 0x5A) {
1390 data += 3;
1391 int mod, regop, rm;
1392 get_modrm(*data, &mod, &regop, &rm);
1393 AppendToBuffer("cvtss2sd %s,%s",
1394 NameOfXMMRegister(regop),
1395 NameOfXMMRegister(rm));
1396 data++;
Leon Clarkee46be812010-01-19 14:06:41 +00001397 } else if (*(data+2) == 0x6F) {
1398 data += 3;
1399 int mod, regop, rm;
1400 get_modrm(*data, &mod, &regop, &rm);
1401 AppendToBuffer("movdqu %s,", NameOfXMMRegister(regop));
1402 data += PrintRightOperand(data);
1403 } else if (*(data+2) == 0x7F) {
1404 AppendToBuffer("movdqu ");
1405 data += 3;
1406 int mod, regop, rm;
1407 get_modrm(*data, &mod, &regop, &rm);
1408 data += PrintRightOperand(data);
1409 AppendToBuffer(",%s", NameOfXMMRegister(regop));
1410 } else {
1411 UnimplementedInstruction();
1412 }
1413 } else if (*(data+1) == 0xA5) {
1414 data += 2;
1415 AppendToBuffer("rep_movs");
Steve Block6ded16b2010-05-10 14:33:55 +01001416 } else if (*(data+1) == 0xAB) {
1417 data += 2;
1418 AppendToBuffer("rep_stos");
Steve Blocka7e24c12009-10-30 11:49:00 +00001419 } else {
1420 UnimplementedInstruction();
1421 }
1422 break;
1423
1424 case 0xF7:
1425 data += F7Instruction(data);
1426 break;
1427
1428 default:
1429 UnimplementedInstruction();
1430 }
1431 }
1432
1433 if (tmp_buffer_pos_ < sizeof tmp_buffer_) {
1434 tmp_buffer_[tmp_buffer_pos_] = '\0';
1435 }
1436
1437 int instr_len = data - instr;
Leon Clarkee46be812010-01-19 14:06:41 +00001438 if (instr_len == 0) {
1439 printf("%02x", *data);
1440 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001441 ASSERT(instr_len > 0); // Ensure progress.
1442
1443 int outp = 0;
1444 // Instruction bytes.
1445 for (byte* bp = instr; bp < data; bp++) {
1446 outp += v8::internal::OS::SNPrintF(out_buffer + outp,
1447 "%02x",
1448 *bp);
1449 }
1450 for (int i = 6 - instr_len; i >= 0; i--) {
1451 outp += v8::internal::OS::SNPrintF(out_buffer + outp,
1452 " ");
1453 }
1454
1455 outp += v8::internal::OS::SNPrintF(out_buffer + outp,
1456 " %s",
1457 tmp_buffer_.start());
1458 return instr_len;
Kristian Monsen0d5e1162010-09-30 15:31:59 +01001459} // NOLINT (function is too long)
Steve Blocka7e24c12009-10-30 11:49:00 +00001460
1461
1462//------------------------------------------------------------------------------
1463
1464
1465static const char* cpu_regs[8] = {
1466 "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi"
1467};
1468
1469
1470static const char* byte_cpu_regs[8] = {
1471 "al", "cl", "dl", "bl", "ah", "ch", "dh", "bh"
1472};
1473
1474
1475static const char* xmm_regs[8] = {
1476 "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7"
1477};
1478
1479
1480const char* NameConverter::NameOfAddress(byte* addr) const {
1481 static v8::internal::EmbeddedVector<char, 32> tmp_buffer;
1482 v8::internal::OS::SNPrintF(tmp_buffer, "%p", addr);
1483 return tmp_buffer.start();
1484}
1485
1486
1487const char* NameConverter::NameOfConstant(byte* addr) const {
1488 return NameOfAddress(addr);
1489}
1490
1491
1492const char* NameConverter::NameOfCPURegister(int reg) const {
1493 if (0 <= reg && reg < 8) return cpu_regs[reg];
1494 return "noreg";
1495}
1496
1497
1498const char* NameConverter::NameOfByteCPURegister(int reg) const {
1499 if (0 <= reg && reg < 8) return byte_cpu_regs[reg];
1500 return "noreg";
1501}
1502
1503
1504const char* NameConverter::NameOfXMMRegister(int reg) const {
1505 if (0 <= reg && reg < 8) return xmm_regs[reg];
1506 return "noxmmreg";
1507}
1508
1509
1510const char* NameConverter::NameInCode(byte* addr) const {
1511 // IA32 does not embed debug strings at the moment.
1512 UNREACHABLE();
1513 return "";
1514}
1515
1516
1517//------------------------------------------------------------------------------
1518
1519Disassembler::Disassembler(const NameConverter& converter)
1520 : converter_(converter) {}
1521
1522
1523Disassembler::~Disassembler() {}
1524
1525
1526int Disassembler::InstructionDecode(v8::internal::Vector<char> buffer,
1527 byte* instruction) {
1528 DisassemblerIA32 d(converter_, false /*do not crash if unimplemented*/);
1529 return d.InstructionDecode(buffer, instruction);
1530}
1531
1532
1533// The IA-32 assembler does not currently use constant pools.
1534int Disassembler::ConstantPoolSizeAt(byte* instruction) { return -1; }
1535
1536
1537/*static*/ void Disassembler::Disassemble(FILE* f, byte* begin, byte* end) {
1538 NameConverter converter;
1539 Disassembler d(converter);
1540 for (byte* pc = begin; pc < end;) {
1541 v8::internal::EmbeddedVector<char, 128> buffer;
1542 buffer[0] = '\0';
1543 byte* prev_pc = pc;
1544 pc += d.InstructionDecode(buffer, pc);
1545 fprintf(f, "%p", prev_pc);
1546 fprintf(f, " ");
1547
1548 for (byte* bp = prev_pc; bp < pc; bp++) {
1549 fprintf(f, "%02x", *bp);
1550 }
1551 for (int i = 6 - (pc - prev_pc); i >= 0; i--) {
1552 fprintf(f, " ");
1553 }
1554 fprintf(f, " %s\n", buffer.start());
1555 }
1556}
1557
1558
1559} // namespace disasm
Leon Clarkef7060e22010-06-03 12:02:55 +01001560
1561#endif // V8_TARGET_ARCH_IA32