blob: bf2200ca11deee100df45990e29612d5ba83e988 [file] [log] [blame]
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001// Copyright 2011 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <assert.h>
6#include <stdarg.h>
7#include <stdio.h>
8
Ben Murdochb8a8cc12014-11-26 15:28:44 +00009#if V8_TARGET_ARCH_X87
10
11#include "src/disasm.h"
12
13namespace disasm {
14
15enum OperandOrder {
16 UNSET_OP_ORDER = 0,
17 REG_OPER_OP_ORDER,
18 OPER_REG_OP_ORDER
19};
20
21
22//------------------------------------------------------------------
23// Tables
24//------------------------------------------------------------------
25struct ByteMnemonic {
26 int b; // -1 terminates, otherwise must be in range (0..255)
27 const char* mnem;
28 OperandOrder op_order_;
29};
30
31
32static const ByteMnemonic two_operands_instr[] = {
33 {0x01, "add", OPER_REG_OP_ORDER},
34 {0x03, "add", REG_OPER_OP_ORDER},
35 {0x09, "or", OPER_REG_OP_ORDER},
36 {0x0B, "or", REG_OPER_OP_ORDER},
37 {0x1B, "sbb", REG_OPER_OP_ORDER},
38 {0x21, "and", OPER_REG_OP_ORDER},
39 {0x23, "and", REG_OPER_OP_ORDER},
40 {0x29, "sub", OPER_REG_OP_ORDER},
41 {0x2A, "subb", REG_OPER_OP_ORDER},
42 {0x2B, "sub", REG_OPER_OP_ORDER},
43 {0x31, "xor", OPER_REG_OP_ORDER},
44 {0x33, "xor", REG_OPER_OP_ORDER},
45 {0x38, "cmpb", OPER_REG_OP_ORDER},
46 {0x3A, "cmpb", REG_OPER_OP_ORDER},
47 {0x3B, "cmp", REG_OPER_OP_ORDER},
48 {0x84, "test_b", REG_OPER_OP_ORDER},
49 {0x85, "test", REG_OPER_OP_ORDER},
50 {0x87, "xchg", REG_OPER_OP_ORDER},
51 {0x8A, "mov_b", REG_OPER_OP_ORDER},
52 {0x8B, "mov", REG_OPER_OP_ORDER},
53 {0x8D, "lea", REG_OPER_OP_ORDER},
54 {-1, "", UNSET_OP_ORDER}
55};
56
57
58static const ByteMnemonic zero_operands_instr[] = {
59 {0xC3, "ret", UNSET_OP_ORDER},
60 {0xC9, "leave", UNSET_OP_ORDER},
61 {0x90, "nop", UNSET_OP_ORDER},
62 {0xF4, "hlt", UNSET_OP_ORDER},
63 {0xCC, "int3", UNSET_OP_ORDER},
64 {0x60, "pushad", UNSET_OP_ORDER},
65 {0x61, "popad", UNSET_OP_ORDER},
66 {0x9C, "pushfd", UNSET_OP_ORDER},
67 {0x9D, "popfd", UNSET_OP_ORDER},
68 {0x9E, "sahf", UNSET_OP_ORDER},
69 {0x99, "cdq", UNSET_OP_ORDER},
70 {0x9B, "fwait", UNSET_OP_ORDER},
71 {0xFC, "cld", UNSET_OP_ORDER},
72 {0xAB, "stos", UNSET_OP_ORDER},
73 {-1, "", UNSET_OP_ORDER}
74};
75
76
77static const ByteMnemonic call_jump_instr[] = {
78 {0xE8, "call", UNSET_OP_ORDER},
79 {0xE9, "jmp", UNSET_OP_ORDER},
80 {-1, "", UNSET_OP_ORDER}
81};
82
83
84static const ByteMnemonic short_immediate_instr[] = {
85 {0x05, "add", UNSET_OP_ORDER},
86 {0x0D, "or", UNSET_OP_ORDER},
87 {0x15, "adc", UNSET_OP_ORDER},
88 {0x25, "and", UNSET_OP_ORDER},
89 {0x2D, "sub", UNSET_OP_ORDER},
90 {0x35, "xor", UNSET_OP_ORDER},
91 {0x3D, "cmp", UNSET_OP_ORDER},
92 {-1, "", UNSET_OP_ORDER}
93};
94
95
96// Generally we don't want to generate these because they are subject to partial
97// register stalls. They are included for completeness and because the cmp
98// variant is used by the RecordWrite stub. Because it does not update the
99// register it is not subject to partial register stalls.
100static ByteMnemonic byte_immediate_instr[] = {
101 {0x0c, "or", UNSET_OP_ORDER},
102 {0x24, "and", UNSET_OP_ORDER},
103 {0x34, "xor", UNSET_OP_ORDER},
104 {0x3c, "cmp", UNSET_OP_ORDER},
105 {-1, "", UNSET_OP_ORDER}
106};
107
108
109static const char* const jump_conditional_mnem[] = {
110 /*0*/ "jo", "jno", "jc", "jnc",
111 /*4*/ "jz", "jnz", "jna", "ja",
112 /*8*/ "js", "jns", "jpe", "jpo",
113 /*12*/ "jl", "jnl", "jng", "jg"
114};
115
116
117static const char* const set_conditional_mnem[] = {
118 /*0*/ "seto", "setno", "setc", "setnc",
119 /*4*/ "setz", "setnz", "setna", "seta",
120 /*8*/ "sets", "setns", "setpe", "setpo",
121 /*12*/ "setl", "setnl", "setng", "setg"
122};
123
124
125static const char* const conditional_move_mnem[] = {
126 /*0*/ "cmovo", "cmovno", "cmovc", "cmovnc",
127 /*4*/ "cmovz", "cmovnz", "cmovna", "cmova",
128 /*8*/ "cmovs", "cmovns", "cmovpe", "cmovpo",
129 /*12*/ "cmovl", "cmovnl", "cmovng", "cmovg"
130};
131
132
133enum InstructionType {
134 NO_INSTR,
135 ZERO_OPERANDS_INSTR,
136 TWO_OPERANDS_INSTR,
137 JUMP_CONDITIONAL_SHORT_INSTR,
138 REGISTER_INSTR,
139 MOVE_REG_INSTR,
140 CALL_JUMP_INSTR,
141 SHORT_IMMEDIATE_INSTR,
142 BYTE_IMMEDIATE_INSTR
143};
144
145
146struct InstructionDesc {
147 const char* mnem;
148 InstructionType type;
149 OperandOrder op_order_;
150};
151
152
153class InstructionTable {
154 public:
155 InstructionTable();
156 const InstructionDesc& Get(byte x) const { return instructions_[x]; }
157 static InstructionTable* get_instance() {
158 static InstructionTable table;
159 return &table;
160 }
161
162 private:
163 InstructionDesc instructions_[256];
164 void Clear();
165 void Init();
166 void CopyTable(const ByteMnemonic bm[], InstructionType type);
167 void SetTableRange(InstructionType type,
168 byte start,
169 byte end,
170 const char* mnem);
171 void AddJumpConditionalShort();
172};
173
174
175InstructionTable::InstructionTable() {
176 Clear();
177 Init();
178}
179
180
181void InstructionTable::Clear() {
182 for (int i = 0; i < 256; i++) {
183 instructions_[i].mnem = "";
184 instructions_[i].type = NO_INSTR;
185 instructions_[i].op_order_ = UNSET_OP_ORDER;
186 }
187}
188
189
190void InstructionTable::Init() {
191 CopyTable(two_operands_instr, TWO_OPERANDS_INSTR);
192 CopyTable(zero_operands_instr, ZERO_OPERANDS_INSTR);
193 CopyTable(call_jump_instr, CALL_JUMP_INSTR);
194 CopyTable(short_immediate_instr, SHORT_IMMEDIATE_INSTR);
195 CopyTable(byte_immediate_instr, BYTE_IMMEDIATE_INSTR);
196 AddJumpConditionalShort();
197 SetTableRange(REGISTER_INSTR, 0x40, 0x47, "inc");
198 SetTableRange(REGISTER_INSTR, 0x48, 0x4F, "dec");
199 SetTableRange(REGISTER_INSTR, 0x50, 0x57, "push");
200 SetTableRange(REGISTER_INSTR, 0x58, 0x5F, "pop");
201 SetTableRange(REGISTER_INSTR, 0x91, 0x97, "xchg eax,"); // 0x90 is nop.
202 SetTableRange(MOVE_REG_INSTR, 0xB8, 0xBF, "mov");
203}
204
205
206void InstructionTable::CopyTable(const ByteMnemonic bm[],
207 InstructionType type) {
208 for (int i = 0; bm[i].b >= 0; i++) {
209 InstructionDesc* id = &instructions_[bm[i].b];
210 id->mnem = bm[i].mnem;
211 id->op_order_ = bm[i].op_order_;
212 DCHECK_EQ(NO_INSTR, id->type); // Information not already entered.
213 id->type = type;
214 }
215}
216
217
218void InstructionTable::SetTableRange(InstructionType type,
219 byte start,
220 byte end,
221 const char* mnem) {
222 for (byte b = start; b <= end; b++) {
223 InstructionDesc* id = &instructions_[b];
224 DCHECK_EQ(NO_INSTR, id->type); // Information not already entered.
225 id->mnem = mnem;
226 id->type = type;
227 }
228}
229
230
231void InstructionTable::AddJumpConditionalShort() {
232 for (byte b = 0x70; b <= 0x7F; b++) {
233 InstructionDesc* id = &instructions_[b];
234 DCHECK_EQ(NO_INSTR, id->type); // Information not already entered.
235 id->mnem = jump_conditional_mnem[b & 0x0F];
236 id->type = JUMP_CONDITIONAL_SHORT_INSTR;
237 }
238}
239
240
241// The X87 disassembler implementation.
242class DisassemblerX87 {
243 public:
244 DisassemblerX87(const NameConverter& converter,
245 bool abort_on_unimplemented = true)
246 : converter_(converter),
247 instruction_table_(InstructionTable::get_instance()),
248 tmp_buffer_pos_(0),
249 abort_on_unimplemented_(abort_on_unimplemented) {
250 tmp_buffer_[0] = '\0';
251 }
252
253 virtual ~DisassemblerX87() {}
254
255 // Writes one disassembled instruction into 'buffer' (0-terminated).
256 // Returns the length of the disassembled machine instruction in bytes.
257 int InstructionDecode(v8::internal::Vector<char> buffer, byte* instruction);
258
259 private:
260 const NameConverter& converter_;
261 InstructionTable* instruction_table_;
262 v8::internal::EmbeddedVector<char, 128> tmp_buffer_;
263 unsigned int tmp_buffer_pos_;
264 bool abort_on_unimplemented_;
265
266 enum {
267 eax = 0,
268 ecx = 1,
269 edx = 2,
270 ebx = 3,
271 esp = 4,
272 ebp = 5,
273 esi = 6,
274 edi = 7
275 };
276
277
278 enum ShiftOpcodeExtension {
279 kROL = 0,
280 kROR = 1,
281 kRCL = 2,
282 kRCR = 3,
283 kSHL = 4,
284 KSHR = 5,
285 kSAR = 7
286 };
287
288
289 const char* NameOfCPURegister(int reg) const {
290 return converter_.NameOfCPURegister(reg);
291 }
292
293
294 const char* NameOfByteCPURegister(int reg) const {
295 return converter_.NameOfByteCPURegister(reg);
296 }
297
298
299 const char* NameOfXMMRegister(int reg) const {
300 return converter_.NameOfXMMRegister(reg);
301 }
302
303
304 const char* NameOfAddress(byte* addr) const {
305 return converter_.NameOfAddress(addr);
306 }
307
308
309 // Disassembler helper functions.
310 static void get_modrm(byte data, int* mod, int* regop, int* rm) {
311 *mod = (data >> 6) & 3;
312 *regop = (data & 0x38) >> 3;
313 *rm = data & 7;
314 }
315
316
317 static void get_sib(byte data, int* scale, int* index, int* base) {
318 *scale = (data >> 6) & 3;
319 *index = (data >> 3) & 7;
320 *base = data & 7;
321 }
322
323 typedef const char* (DisassemblerX87::*RegisterNameMapping)(int reg) const;
324
325 int PrintRightOperandHelper(byte* modrmp, RegisterNameMapping register_name);
326 int PrintRightOperand(byte* modrmp);
327 int PrintRightByteOperand(byte* modrmp);
328 int PrintRightXMMOperand(byte* modrmp);
329 int PrintOperands(const char* mnem, OperandOrder op_order, byte* data);
330 int PrintImmediateOp(byte* data);
331 int F7Instruction(byte* data);
332 int D1D3C1Instruction(byte* data);
333 int JumpShort(byte* data);
334 int JumpConditional(byte* data, const char* comment);
335 int JumpConditionalShort(byte* data, const char* comment);
336 int SetCC(byte* data);
337 int CMov(byte* data);
338 int FPUInstruction(byte* data);
339 int MemoryFPUInstruction(int escape_opcode, int regop, byte* modrm_start);
340 int RegisterFPUInstruction(int escape_opcode, byte modrm_byte);
341 void AppendToBuffer(const char* format, ...);
342
343
344 void UnimplementedInstruction() {
345 if (abort_on_unimplemented_) {
346 UNIMPLEMENTED();
347 } else {
348 AppendToBuffer("'Unimplemented Instruction'");
349 }
350 }
351};
352
353
354void DisassemblerX87::AppendToBuffer(const char* format, ...) {
355 v8::internal::Vector<char> buf = tmp_buffer_ + tmp_buffer_pos_;
356 va_list args;
357 va_start(args, format);
358 int result = v8::internal::VSNPrintF(buf, format, args);
359 va_end(args);
360 tmp_buffer_pos_ += result;
361}
362
363int DisassemblerX87::PrintRightOperandHelper(
364 byte* modrmp,
365 RegisterNameMapping direct_register_name) {
366 int mod, regop, rm;
367 get_modrm(*modrmp, &mod, &regop, &rm);
368 RegisterNameMapping register_name = (mod == 3) ? direct_register_name :
369 &DisassemblerX87::NameOfCPURegister;
370 switch (mod) {
371 case 0:
372 if (rm == ebp) {
373 int32_t disp = *reinterpret_cast<int32_t*>(modrmp+1);
374 AppendToBuffer("[0x%x]", disp);
375 return 5;
376 } else if (rm == esp) {
377 byte sib = *(modrmp + 1);
378 int scale, index, base;
379 get_sib(sib, &scale, &index, &base);
380 if (index == esp && base == esp && scale == 0 /*times_1*/) {
381 AppendToBuffer("[%s]", (this->*register_name)(rm));
382 return 2;
383 } else if (base == ebp) {
384 int32_t disp = *reinterpret_cast<int32_t*>(modrmp + 2);
385 AppendToBuffer("[%s*%d%s0x%x]",
386 (this->*register_name)(index),
387 1 << scale,
388 disp < 0 ? "-" : "+",
389 disp < 0 ? -disp : disp);
390 return 6;
391 } else if (index != esp && base != ebp) {
392 // [base+index*scale]
393 AppendToBuffer("[%s+%s*%d]",
394 (this->*register_name)(base),
395 (this->*register_name)(index),
396 1 << scale);
397 return 2;
398 } else {
399 UnimplementedInstruction();
400 return 1;
401 }
402 } else {
403 AppendToBuffer("[%s]", (this->*register_name)(rm));
404 return 1;
405 }
406 break;
407 case 1: // fall through
408 case 2:
409 if (rm == esp) {
410 byte sib = *(modrmp + 1);
411 int scale, index, base;
412 get_sib(sib, &scale, &index, &base);
413 int disp = mod == 2 ? *reinterpret_cast<int32_t*>(modrmp + 2)
414 : *reinterpret_cast<int8_t*>(modrmp + 2);
415 if (index == base && index == rm /*esp*/ && scale == 0 /*times_1*/) {
416 AppendToBuffer("[%s%s0x%x]",
417 (this->*register_name)(rm),
418 disp < 0 ? "-" : "+",
419 disp < 0 ? -disp : disp);
420 } else {
421 AppendToBuffer("[%s+%s*%d%s0x%x]",
422 (this->*register_name)(base),
423 (this->*register_name)(index),
424 1 << scale,
425 disp < 0 ? "-" : "+",
426 disp < 0 ? -disp : disp);
427 }
428 return mod == 2 ? 6 : 3;
429 } else {
430 // No sib.
431 int disp = mod == 2 ? *reinterpret_cast<int32_t*>(modrmp + 1)
432 : *reinterpret_cast<int8_t*>(modrmp + 1);
433 AppendToBuffer("[%s%s0x%x]",
434 (this->*register_name)(rm),
435 disp < 0 ? "-" : "+",
436 disp < 0 ? -disp : disp);
437 return mod == 2 ? 5 : 2;
438 }
439 break;
440 case 3:
441 AppendToBuffer("%s", (this->*register_name)(rm));
442 return 1;
443 default:
444 UnimplementedInstruction();
445 return 1;
446 }
447 UNREACHABLE();
448}
449
450
451int DisassemblerX87::PrintRightOperand(byte* modrmp) {
452 return PrintRightOperandHelper(modrmp, &DisassemblerX87::NameOfCPURegister);
453}
454
455
456int DisassemblerX87::PrintRightByteOperand(byte* modrmp) {
457 return PrintRightOperandHelper(modrmp,
458 &DisassemblerX87::NameOfByteCPURegister);
459}
460
461
462int DisassemblerX87::PrintRightXMMOperand(byte* modrmp) {
463 return PrintRightOperandHelper(modrmp,
464 &DisassemblerX87::NameOfXMMRegister);
465}
466
467
468// Returns number of bytes used including the current *data.
469// Writes instruction's mnemonic, left and right operands to 'tmp_buffer_'.
470int DisassemblerX87::PrintOperands(const char* mnem,
471 OperandOrder op_order,
472 byte* data) {
473 byte modrm = *data;
474 int mod, regop, rm;
475 get_modrm(modrm, &mod, &regop, &rm);
476 int advance = 0;
477 switch (op_order) {
478 case REG_OPER_OP_ORDER: {
479 AppendToBuffer("%s %s,", mnem, NameOfCPURegister(regop));
480 advance = PrintRightOperand(data);
481 break;
482 }
483 case OPER_REG_OP_ORDER: {
484 AppendToBuffer("%s ", mnem);
485 advance = PrintRightOperand(data);
486 AppendToBuffer(",%s", NameOfCPURegister(regop));
487 break;
488 }
489 default:
490 UNREACHABLE();
491 break;
492 }
493 return advance;
494}
495
496
497// Returns number of bytes used by machine instruction, including *data byte.
498// Writes immediate instructions to 'tmp_buffer_'.
499int DisassemblerX87::PrintImmediateOp(byte* data) {
500 bool sign_extension_bit = (*data & 0x02) != 0;
501 byte modrm = *(data+1);
502 int mod, regop, rm;
503 get_modrm(modrm, &mod, &regop, &rm);
504 const char* mnem = "Imm???";
505 switch (regop) {
506 case 0: mnem = "add"; break;
507 case 1: mnem = "or"; break;
508 case 2: mnem = "adc"; break;
509 case 4: mnem = "and"; break;
510 case 5: mnem = "sub"; break;
511 case 6: mnem = "xor"; break;
512 case 7: mnem = "cmp"; break;
513 default: UnimplementedInstruction();
514 }
515 AppendToBuffer("%s ", mnem);
516 int count = PrintRightOperand(data+1);
517 if (sign_extension_bit) {
518 AppendToBuffer(",0x%x", *(data + 1 + count));
519 return 1 + count + 1 /*int8*/;
520 } else {
521 AppendToBuffer(",0x%x", *reinterpret_cast<int32_t*>(data + 1 + count));
522 return 1 + count + 4 /*int32_t*/;
523 }
524}
525
526
527// Returns number of bytes used, including *data.
528int DisassemblerX87::F7Instruction(byte* data) {
529 DCHECK_EQ(0xF7, *data);
530 byte modrm = *++data;
531 int mod, regop, rm;
532 get_modrm(modrm, &mod, &regop, &rm);
533 const char* mnem = NULL;
534 switch (regop) {
535 case 0:
536 mnem = "test";
537 break;
538 case 2:
539 mnem = "not";
540 break;
541 case 3:
542 mnem = "neg";
543 break;
544 case 4:
545 mnem = "mul";
546 break;
547 case 5:
548 mnem = "imul";
549 break;
550 case 6:
551 mnem = "div";
552 break;
553 case 7:
554 mnem = "idiv";
555 break;
556 default:
557 UnimplementedInstruction();
558 }
559 AppendToBuffer("%s ", mnem);
560 int count = PrintRightOperand(data);
561 if (regop == 0) {
562 AppendToBuffer(",0x%x", *reinterpret_cast<int32_t*>(data + count));
563 count += 4;
564 }
565 return 1 + count;
566}
567
568
569int DisassemblerX87::D1D3C1Instruction(byte* data) {
570 byte op = *data;
571 DCHECK(op == 0xD1 || op == 0xD3 || op == 0xC1);
572 byte modrm = *++data;
573 int mod, regop, rm;
574 get_modrm(modrm, &mod, &regop, &rm);
575 int imm8 = -1;
576 const char* mnem = NULL;
577 switch (regop) {
578 case kROL:
579 mnem = "rol";
580 break;
581 case kROR:
582 mnem = "ror";
583 break;
584 case kRCL:
585 mnem = "rcl";
586 break;
587 case kRCR:
588 mnem = "rcr";
589 break;
590 case kSHL:
591 mnem = "shl";
592 break;
593 case KSHR:
594 mnem = "shr";
595 break;
596 case kSAR:
597 mnem = "sar";
598 break;
599 default:
600 UnimplementedInstruction();
601 }
602 AppendToBuffer("%s ", mnem);
603 int count = PrintRightOperand(data);
604 if (op == 0xD1) {
605 imm8 = 1;
606 } else if (op == 0xC1) {
607 imm8 = *(data + 1);
608 count++;
609 } else if (op == 0xD3) {
610 // Shift/rotate by cl.
611 }
612 if (imm8 >= 0) {
613 AppendToBuffer(",%d", imm8);
614 } else {
615 AppendToBuffer(",cl");
616 }
617 return 1 + count;
618}
619
620
621// Returns number of bytes used, including *data.
622int DisassemblerX87::JumpShort(byte* data) {
623 DCHECK_EQ(0xEB, *data);
624 byte b = *(data+1);
625 byte* dest = data + static_cast<int8_t>(b) + 2;
626 AppendToBuffer("jmp %s", NameOfAddress(dest));
627 return 2;
628}
629
630
631// Returns number of bytes used, including *data.
632int DisassemblerX87::JumpConditional(byte* data, const char* comment) {
633 DCHECK_EQ(0x0F, *data);
634 byte cond = *(data+1) & 0x0F;
635 byte* dest = data + *reinterpret_cast<int32_t*>(data+2) + 6;
636 const char* mnem = jump_conditional_mnem[cond];
637 AppendToBuffer("%s %s", mnem, NameOfAddress(dest));
638 if (comment != NULL) {
639 AppendToBuffer(", %s", comment);
640 }
641 return 6; // includes 0x0F
642}
643
644
645// Returns number of bytes used, including *data.
646int DisassemblerX87::JumpConditionalShort(byte* data, const char* comment) {
647 byte cond = *data & 0x0F;
648 byte b = *(data+1);
649 byte* dest = data + static_cast<int8_t>(b) + 2;
650 const char* mnem = jump_conditional_mnem[cond];
651 AppendToBuffer("%s %s", mnem, NameOfAddress(dest));
652 if (comment != NULL) {
653 AppendToBuffer(", %s", comment);
654 }
655 return 2;
656}
657
658
659// Returns number of bytes used, including *data.
660int DisassemblerX87::SetCC(byte* data) {
661 DCHECK_EQ(0x0F, *data);
662 byte cond = *(data+1) & 0x0F;
663 const char* mnem = set_conditional_mnem[cond];
664 AppendToBuffer("%s ", mnem);
665 PrintRightByteOperand(data+2);
666 return 3; // Includes 0x0F.
667}
668
669
670// Returns number of bytes used, including *data.
671int DisassemblerX87::CMov(byte* data) {
672 DCHECK_EQ(0x0F, *data);
673 byte cond = *(data + 1) & 0x0F;
674 const char* mnem = conditional_move_mnem[cond];
675 int op_size = PrintOperands(mnem, REG_OPER_OP_ORDER, data + 2);
676 return 2 + op_size; // includes 0x0F
677}
678
679
680// Returns number of bytes used, including *data.
681int DisassemblerX87::FPUInstruction(byte* data) {
682 byte escape_opcode = *data;
683 DCHECK_EQ(0xD8, escape_opcode & 0xF8);
684 byte modrm_byte = *(data+1);
685
686 if (modrm_byte >= 0xC0) {
687 return RegisterFPUInstruction(escape_opcode, modrm_byte);
688 } else {
689 return MemoryFPUInstruction(escape_opcode, modrm_byte, data+1);
690 }
691}
692
693int DisassemblerX87::MemoryFPUInstruction(int escape_opcode,
694 int modrm_byte,
695 byte* modrm_start) {
696 const char* mnem = "?";
697 int regop = (modrm_byte >> 3) & 0x7; // reg/op field of modrm byte.
698 switch (escape_opcode) {
699 case 0xD9: switch (regop) {
700 case 0: mnem = "fld_s"; break;
701 case 2: mnem = "fst_s"; break;
702 case 3: mnem = "fstp_s"; break;
703 case 5:
704 mnem = "fldcw";
705 break;
706 case 7:
707 mnem = "fnstcw";
708 break;
709 default: UnimplementedInstruction();
710 }
711 break;
712
713 case 0xDB: switch (regop) {
714 case 0: mnem = "fild_s"; break;
715 case 1: mnem = "fisttp_s"; break;
716 case 2: mnem = "fist_s"; break;
717 case 3: mnem = "fistp_s"; break;
718 default: UnimplementedInstruction();
719 }
720 break;
721
722 case 0xDC:
723 switch (regop) {
724 case 0:
725 mnem = "fadd_d";
726 break;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000727 case 1:
728 mnem = "fmul_d";
729 break;
730 case 4:
731 mnem = "fsub_d";
732 break;
733 case 5:
734 mnem = "fsubr_d";
735 break;
736 case 6:
737 mnem = "fdiv_d";
738 break;
739 case 7:
740 mnem = "fdivr_d";
741 break;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000742 default:
743 UnimplementedInstruction();
744 }
745 break;
746
747 case 0xDD: switch (regop) {
748 case 0: mnem = "fld_d"; break;
749 case 1: mnem = "fisttp_d"; break;
750 case 2: mnem = "fst_d"; break;
751 case 3: mnem = "fstp_d"; break;
752 case 4:
753 mnem = "frstor";
754 break;
755 case 6:
756 mnem = "fnsave";
757 break;
758 default: UnimplementedInstruction();
759 }
760 break;
761
762 case 0xDF: switch (regop) {
763 case 5: mnem = "fild_d"; break;
764 case 7: mnem = "fistp_d"; break;
765 default: UnimplementedInstruction();
766 }
767 break;
768
769 default: UnimplementedInstruction();
770 }
771 AppendToBuffer("%s ", mnem);
772 int count = PrintRightOperand(modrm_start);
773 return count + 1;
774}
775
776int DisassemblerX87::RegisterFPUInstruction(int escape_opcode,
777 byte modrm_byte) {
778 bool has_register = false; // Is the FPU register encoded in modrm_byte?
779 const char* mnem = "?";
780
781 switch (escape_opcode) {
782 case 0xD8:
783 has_register = true;
784 switch (modrm_byte & 0xF8) {
785 case 0xC0: mnem = "fadd_i"; break;
786 case 0xE0: mnem = "fsub_i"; break;
787 case 0xC8: mnem = "fmul_i"; break;
788 case 0xF0: mnem = "fdiv_i"; break;
789 default: UnimplementedInstruction();
790 }
791 break;
792
793 case 0xD9:
794 switch (modrm_byte & 0xF8) {
795 case 0xC0:
796 mnem = "fld";
797 has_register = true;
798 break;
799 case 0xC8:
800 mnem = "fxch";
801 has_register = true;
802 break;
803 default:
804 switch (modrm_byte) {
805 case 0xE0: mnem = "fchs"; break;
806 case 0xE1: mnem = "fabs"; break;
807 case 0xE4: mnem = "ftst"; break;
808 case 0xE8: mnem = "fld1"; break;
809 case 0xEB: mnem = "fldpi"; break;
810 case 0xED: mnem = "fldln2"; break;
811 case 0xEE: mnem = "fldz"; break;
812 case 0xF0: mnem = "f2xm1"; break;
813 case 0xF1: mnem = "fyl2x"; break;
814 case 0xF4: mnem = "fxtract"; break;
815 case 0xF5: mnem = "fprem1"; break;
816 case 0xF7: mnem = "fincstp"; break;
817 case 0xF8: mnem = "fprem"; break;
818 case 0xFC: mnem = "frndint"; break;
819 case 0xFD: mnem = "fscale"; break;
820 case 0xFE: mnem = "fsin"; break;
821 case 0xFF: mnem = "fcos"; break;
822 default: UnimplementedInstruction();
823 }
824 }
825 break;
826
827 case 0xDA:
828 if (modrm_byte == 0xE9) {
829 mnem = "fucompp";
830 } else {
831 UnimplementedInstruction();
832 }
833 break;
834
835 case 0xDB:
836 if ((modrm_byte & 0xF8) == 0xE8) {
837 mnem = "fucomi";
838 has_register = true;
839 } else if (modrm_byte == 0xE2) {
840 mnem = "fclex";
841 } else if (modrm_byte == 0xE3) {
842 mnem = "fninit";
843 } else {
844 UnimplementedInstruction();
845 }
846 break;
847
848 case 0xDC:
849 has_register = true;
850 switch (modrm_byte & 0xF8) {
851 case 0xC0: mnem = "fadd"; break;
852 case 0xE8: mnem = "fsub"; break;
853 case 0xC8: mnem = "fmul"; break;
854 case 0xF8: mnem = "fdiv"; break;
855 default: UnimplementedInstruction();
856 }
857 break;
858
859 case 0xDD:
860 has_register = true;
861 switch (modrm_byte & 0xF8) {
862 case 0xC0: mnem = "ffree"; break;
863 case 0xD0: mnem = "fst"; break;
864 case 0xD8: mnem = "fstp"; break;
865 default: UnimplementedInstruction();
866 }
867 break;
868
869 case 0xDE:
870 if (modrm_byte == 0xD9) {
871 mnem = "fcompp";
872 } else {
873 has_register = true;
874 switch (modrm_byte & 0xF8) {
875 case 0xC0: mnem = "faddp"; break;
876 case 0xE8: mnem = "fsubp"; break;
877 case 0xC8: mnem = "fmulp"; break;
878 case 0xF8: mnem = "fdivp"; break;
879 default: UnimplementedInstruction();
880 }
881 }
882 break;
883
884 case 0xDF:
885 if (modrm_byte == 0xE0) {
886 mnem = "fnstsw_ax";
887 } else if ((modrm_byte & 0xF8) == 0xE8) {
888 mnem = "fucomip";
889 has_register = true;
890 }
891 break;
892
893 default: UnimplementedInstruction();
894 }
895
896 if (has_register) {
897 AppendToBuffer("%s st%d", mnem, modrm_byte & 0x7);
898 } else {
899 AppendToBuffer("%s", mnem);
900 }
901 return 2;
902}
903
904
905// Mnemonics for instructions 0xF0 byte.
906// Returns NULL if the instruction is not handled here.
907static const char* F0Mnem(byte f0byte) {
908 switch (f0byte) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000909 case 0x0B:
910 return "ud2";
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000911 case 0x18: return "prefetch";
912 case 0xA2: return "cpuid";
913 case 0xBE: return "movsx_b";
914 case 0xBF: return "movsx_w";
915 case 0xB6: return "movzx_b";
916 case 0xB7: return "movzx_w";
917 case 0xAF: return "imul";
918 case 0xA5: return "shld";
919 case 0xAD: return "shrd";
920 case 0xAC: return "shrd"; // 3-operand version.
921 case 0xAB: return "bts";
922 case 0xBD: return "bsr";
923 default: return NULL;
924 }
925}
926
927
928// Disassembled instruction '*instr' and writes it into 'out_buffer'.
929int DisassemblerX87::InstructionDecode(v8::internal::Vector<char> out_buffer,
930 byte* instr) {
931 tmp_buffer_pos_ = 0; // starting to write as position 0
932 byte* data = instr;
933 // Check for hints.
934 const char* branch_hint = NULL;
935 // We use these two prefixes only with branch prediction
936 if (*data == 0x3E /*ds*/) {
937 branch_hint = "predicted taken";
938 data++;
939 } else if (*data == 0x2E /*cs*/) {
940 branch_hint = "predicted not taken";
941 data++;
942 }
943 bool processed = true; // Will be set to false if the current instruction
944 // is not in 'instructions' table.
945 const InstructionDesc& idesc = instruction_table_->Get(*data);
946 switch (idesc.type) {
947 case ZERO_OPERANDS_INSTR:
948 AppendToBuffer(idesc.mnem);
949 data++;
950 break;
951
952 case TWO_OPERANDS_INSTR:
953 data++;
954 data += PrintOperands(idesc.mnem, idesc.op_order_, data);
955 break;
956
957 case JUMP_CONDITIONAL_SHORT_INSTR:
958 data += JumpConditionalShort(data, branch_hint);
959 break;
960
961 case REGISTER_INSTR:
962 AppendToBuffer("%s %s", idesc.mnem, NameOfCPURegister(*data & 0x07));
963 data++;
964 break;
965
966 case MOVE_REG_INSTR: {
967 byte* addr = reinterpret_cast<byte*>(*reinterpret_cast<int32_t*>(data+1));
968 AppendToBuffer("mov %s,%s",
969 NameOfCPURegister(*data & 0x07),
970 NameOfAddress(addr));
971 data += 5;
972 break;
973 }
974
975 case CALL_JUMP_INSTR: {
976 byte* addr = data + *reinterpret_cast<int32_t*>(data+1) + 5;
977 AppendToBuffer("%s %s", idesc.mnem, NameOfAddress(addr));
978 data += 5;
979 break;
980 }
981
982 case SHORT_IMMEDIATE_INSTR: {
983 byte* addr = reinterpret_cast<byte*>(*reinterpret_cast<int32_t*>(data+1));
984 AppendToBuffer("%s eax,%s", idesc.mnem, NameOfAddress(addr));
985 data += 5;
986 break;
987 }
988
989 case BYTE_IMMEDIATE_INSTR: {
990 AppendToBuffer("%s al,0x%x", idesc.mnem, data[1]);
991 data += 2;
992 break;
993 }
994
995 case NO_INSTR:
996 processed = false;
997 break;
998
999 default:
1000 UNIMPLEMENTED(); // This type is not implemented.
1001 }
1002 //----------------------------
1003 if (!processed) {
1004 switch (*data) {
1005 case 0xC2:
1006 AppendToBuffer("ret 0x%x", *reinterpret_cast<uint16_t*>(data+1));
1007 data += 3;
1008 break;
1009
1010 case 0x6B: {
1011 data++;
1012 data += PrintOperands("imul", REG_OPER_OP_ORDER, data);
1013 AppendToBuffer(",%d", *data);
1014 data++;
1015 } break;
1016
1017 case 0x69: {
1018 data++;
1019 data += PrintOperands("imul", REG_OPER_OP_ORDER, data);
1020 AppendToBuffer(",%d", *reinterpret_cast<int32_t*>(data));
1021 data += 4;
1022 }
1023 break;
1024
1025 case 0xF6:
1026 { data++;
1027 int mod, regop, rm;
1028 get_modrm(*data, &mod, &regop, &rm);
1029 if (regop == eax) {
1030 AppendToBuffer("test_b ");
1031 data += PrintRightByteOperand(data);
1032 int32_t imm = *data;
1033 AppendToBuffer(",0x%x", imm);
1034 data++;
1035 } else {
1036 UnimplementedInstruction();
1037 }
1038 }
1039 break;
1040
1041 case 0x81: // fall through
1042 case 0x83: // 0x81 with sign extension bit set
1043 data += PrintImmediateOp(data);
1044 break;
1045
1046 case 0x0F:
1047 { byte f0byte = data[1];
1048 const char* f0mnem = F0Mnem(f0byte);
1049 if (f0byte == 0x18) {
1050 data += 2;
1051 int mod, regop, rm;
1052 get_modrm(*data, &mod, &regop, &rm);
1053 const char* suffix[] = {"nta", "1", "2", "3"};
1054 AppendToBuffer("%s%s ", f0mnem, suffix[regop & 0x03]);
1055 data += PrintRightOperand(data);
1056 } else if (f0byte == 0x1F && data[2] == 0) {
1057 AppendToBuffer("nop"); // 3 byte nop.
1058 data += 3;
1059 } else if (f0byte == 0x1F && data[2] == 0x40 && data[3] == 0) {
1060 AppendToBuffer("nop"); // 4 byte nop.
1061 data += 4;
1062 } else if (f0byte == 0x1F && data[2] == 0x44 && data[3] == 0 &&
1063 data[4] == 0) {
1064 AppendToBuffer("nop"); // 5 byte nop.
1065 data += 5;
1066 } else if (f0byte == 0x1F && data[2] == 0x80 && data[3] == 0 &&
1067 data[4] == 0 && data[5] == 0 && data[6] == 0) {
1068 AppendToBuffer("nop"); // 7 byte nop.
1069 data += 7;
1070 } else if (f0byte == 0x1F && data[2] == 0x84 && data[3] == 0 &&
1071 data[4] == 0 && data[5] == 0 && data[6] == 0 &&
1072 data[7] == 0) {
1073 AppendToBuffer("nop"); // 8 byte nop.
1074 data += 8;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001075 } else if (f0byte == 0x0B || f0byte == 0xA2 || f0byte == 0x31) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001076 AppendToBuffer("%s", f0mnem);
1077 data += 2;
1078 } else if (f0byte == 0x28) {
1079 data += 2;
1080 int mod, regop, rm;
1081 get_modrm(*data, &mod, &regop, &rm);
1082 AppendToBuffer("movaps %s,%s",
1083 NameOfXMMRegister(regop),
1084 NameOfXMMRegister(rm));
1085 data++;
1086 } else if (f0byte >= 0x53 && f0byte <= 0x5F) {
1087 const char* const pseudo_op[] = {
1088 "rcpps",
1089 "andps",
1090 "andnps",
1091 "orps",
1092 "xorps",
1093 "addps",
1094 "mulps",
1095 "cvtps2pd",
1096 "cvtdq2ps",
1097 "subps",
1098 "minps",
1099 "divps",
1100 "maxps",
1101 };
1102
1103 data += 2;
1104 int mod, regop, rm;
1105 get_modrm(*data, &mod, &regop, &rm);
1106 AppendToBuffer("%s %s,",
1107 pseudo_op[f0byte - 0x53],
1108 NameOfXMMRegister(regop));
1109 data += PrintRightXMMOperand(data);
1110 } else if (f0byte == 0x50) {
1111 data += 2;
1112 int mod, regop, rm;
1113 get_modrm(*data, &mod, &regop, &rm);
1114 AppendToBuffer("movmskps %s,%s",
1115 NameOfCPURegister(regop),
1116 NameOfXMMRegister(rm));
1117 data++;
1118 } else if (f0byte== 0xC6) {
1119 // shufps xmm, xmm/m128, imm8
1120 data += 2;
1121 int mod, regop, rm;
1122 get_modrm(*data, &mod, &regop, &rm);
1123 int8_t imm8 = static_cast<int8_t>(data[1]);
1124 AppendToBuffer("shufps %s,%s,%d",
1125 NameOfXMMRegister(rm),
1126 NameOfXMMRegister(regop),
1127 static_cast<int>(imm8));
1128 data += 2;
1129 } else if ((f0byte & 0xF0) == 0x80) {
1130 data += JumpConditional(data, branch_hint);
1131 } else if (f0byte == 0xBE || f0byte == 0xBF || f0byte == 0xB6 ||
1132 f0byte == 0xB7 || f0byte == 0xAF) {
1133 data += 2;
1134 data += PrintOperands(f0mnem, REG_OPER_OP_ORDER, data);
1135 } else if ((f0byte & 0xF0) == 0x90) {
1136 data += SetCC(data);
1137 } else if ((f0byte & 0xF0) == 0x40) {
1138 data += CMov(data);
1139 } else if (f0byte == 0xAB || f0byte == 0xA5 || f0byte == 0xAD) {
1140 // shrd, shld, bts
1141 data += 2;
1142 AppendToBuffer("%s ", f0mnem);
1143 int mod, regop, rm;
1144 get_modrm(*data, &mod, &regop, &rm);
1145 data += PrintRightOperand(data);
1146 if (f0byte == 0xAB) {
1147 AppendToBuffer(",%s", NameOfCPURegister(regop));
1148 } else {
1149 AppendToBuffer(",%s,cl", NameOfCPURegister(regop));
1150 }
1151 } else if (f0byte == 0xBD) {
1152 data += 2;
1153 int mod, regop, rm;
1154 get_modrm(*data, &mod, &regop, &rm);
1155 AppendToBuffer("%s %s,", f0mnem, NameOfCPURegister(regop));
1156 data += PrintRightOperand(data);
1157 } else {
1158 UnimplementedInstruction();
1159 }
1160 }
1161 break;
1162
1163 case 0x8F:
1164 { data++;
1165 int mod, regop, rm;
1166 get_modrm(*data, &mod, &regop, &rm);
1167 if (regop == eax) {
1168 AppendToBuffer("pop ");
1169 data += PrintRightOperand(data);
1170 }
1171 }
1172 break;
1173
1174 case 0xFF:
1175 { data++;
1176 int mod, regop, rm;
1177 get_modrm(*data, &mod, &regop, &rm);
1178 const char* mnem = NULL;
1179 switch (regop) {
1180 case esi: mnem = "push"; break;
1181 case eax: mnem = "inc"; break;
1182 case ecx: mnem = "dec"; break;
1183 case edx: mnem = "call"; break;
1184 case esp: mnem = "jmp"; break;
1185 default: mnem = "???";
1186 }
1187 AppendToBuffer("%s ", mnem);
1188 data += PrintRightOperand(data);
1189 }
1190 break;
1191
1192 case 0xC7: // imm32, fall through
1193 case 0xC6: // imm8
1194 { bool is_byte = *data == 0xC6;
1195 data++;
1196 if (is_byte) {
1197 AppendToBuffer("%s ", "mov_b");
1198 data += PrintRightByteOperand(data);
1199 int32_t imm = *data;
1200 AppendToBuffer(",0x%x", imm);
1201 data++;
1202 } else {
1203 AppendToBuffer("%s ", "mov");
1204 data += PrintRightOperand(data);
1205 int32_t imm = *reinterpret_cast<int32_t*>(data);
1206 AppendToBuffer(",0x%x", imm);
1207 data += 4;
1208 }
1209 }
1210 break;
1211
1212 case 0x80:
1213 { data++;
1214 int mod, regop, rm;
1215 get_modrm(*data, &mod, &regop, &rm);
1216 const char* mnem = NULL;
1217 switch (regop) {
1218 case 5: mnem = "subb"; break;
1219 case 7: mnem = "cmpb"; break;
1220 default: UnimplementedInstruction();
1221 }
1222 AppendToBuffer("%s ", mnem);
1223 data += PrintRightByteOperand(data);
1224 int32_t imm = *data;
1225 AppendToBuffer(",0x%x", imm);
1226 data++;
1227 }
1228 break;
1229
1230 case 0x88: // 8bit, fall through
1231 case 0x89: // 32bit
1232 { bool is_byte = *data == 0x88;
1233 int mod, regop, rm;
1234 data++;
1235 get_modrm(*data, &mod, &regop, &rm);
1236 if (is_byte) {
1237 AppendToBuffer("%s ", "mov_b");
1238 data += PrintRightByteOperand(data);
1239 AppendToBuffer(",%s", NameOfByteCPURegister(regop));
1240 } else {
1241 AppendToBuffer("%s ", "mov");
1242 data += PrintRightOperand(data);
1243 AppendToBuffer(",%s", NameOfCPURegister(regop));
1244 }
1245 }
1246 break;
1247
1248 case 0x66: // prefix
1249 while (*data == 0x66) data++;
1250 if (*data == 0xf && data[1] == 0x1f) {
1251 AppendToBuffer("nop"); // 0x66 prefix
1252 } else if (*data == 0x90) {
1253 AppendToBuffer("nop"); // 0x66 prefix
1254 } else if (*data == 0x8B) {
1255 data++;
1256 data += PrintOperands("mov_w", REG_OPER_OP_ORDER, data);
1257 } else if (*data == 0x89) {
1258 data++;
1259 int mod, regop, rm;
1260 get_modrm(*data, &mod, &regop, &rm);
1261 AppendToBuffer("mov_w ");
1262 data += PrintRightOperand(data);
1263 AppendToBuffer(",%s", NameOfCPURegister(regop));
1264 } else if (*data == 0xC7) {
1265 data++;
1266 AppendToBuffer("%s ", "mov_w");
1267 data += PrintRightOperand(data);
1268 int imm = *reinterpret_cast<int16_t*>(data);
1269 AppendToBuffer(",0x%x", imm);
1270 data += 2;
1271 } else if (*data == 0x0F) {
1272 data++;
1273 if (*data == 0x38) {
1274 data++;
1275 if (*data == 0x17) {
1276 data++;
1277 int mod, regop, rm;
1278 get_modrm(*data, &mod, &regop, &rm);
1279 AppendToBuffer("ptest %s,%s",
1280 NameOfXMMRegister(regop),
1281 NameOfXMMRegister(rm));
1282 data++;
1283 } else if (*data == 0x2A) {
1284 // movntdqa
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001285 UnimplementedInstruction();
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001286 } else {
1287 UnimplementedInstruction();
1288 }
1289 } else if (*data == 0x3A) {
1290 data++;
1291 if (*data == 0x0B) {
1292 data++;
1293 int mod, regop, rm;
1294 get_modrm(*data, &mod, &regop, &rm);
1295 int8_t imm8 = static_cast<int8_t>(data[1]);
1296 AppendToBuffer("roundsd %s,%s,%d",
1297 NameOfXMMRegister(regop),
1298 NameOfXMMRegister(rm),
1299 static_cast<int>(imm8));
1300 data += 2;
1301 } else if (*data == 0x16) {
1302 data++;
1303 int mod, regop, rm;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001304 get_modrm(*data, &mod, &rm, &regop);
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001305 int8_t imm8 = static_cast<int8_t>(data[1]);
1306 AppendToBuffer("pextrd %s,%s,%d",
1307 NameOfCPURegister(regop),
1308 NameOfXMMRegister(rm),
1309 static_cast<int>(imm8));
1310 data += 2;
1311 } else if (*data == 0x17) {
1312 data++;
1313 int mod, regop, rm;
1314 get_modrm(*data, &mod, &regop, &rm);
1315 int8_t imm8 = static_cast<int8_t>(data[1]);
1316 AppendToBuffer("extractps %s,%s,%d",
1317 NameOfCPURegister(rm),
1318 NameOfXMMRegister(regop),
1319 static_cast<int>(imm8));
1320 data += 2;
1321 } else if (*data == 0x22) {
1322 data++;
1323 int mod, regop, rm;
1324 get_modrm(*data, &mod, &regop, &rm);
1325 int8_t imm8 = static_cast<int8_t>(data[1]);
1326 AppendToBuffer("pinsrd %s,%s,%d",
1327 NameOfXMMRegister(regop),
1328 NameOfCPURegister(rm),
1329 static_cast<int>(imm8));
1330 data += 2;
1331 } else {
1332 UnimplementedInstruction();
1333 }
1334 } else if (*data == 0x2E || *data == 0x2F) {
1335 const char* mnem = (*data == 0x2E) ? "ucomisd" : "comisd";
1336 data++;
1337 int mod, regop, rm;
1338 get_modrm(*data, &mod, &regop, &rm);
1339 if (mod == 0x3) {
1340 AppendToBuffer("%s %s,%s", mnem,
1341 NameOfXMMRegister(regop),
1342 NameOfXMMRegister(rm));
1343 data++;
1344 } else {
1345 AppendToBuffer("%s %s,", mnem, NameOfXMMRegister(regop));
1346 data += PrintRightOperand(data);
1347 }
1348 } else if (*data == 0x50) {
1349 data++;
1350 int mod, regop, rm;
1351 get_modrm(*data, &mod, &regop, &rm);
1352 AppendToBuffer("movmskpd %s,%s",
1353 NameOfCPURegister(regop),
1354 NameOfXMMRegister(rm));
1355 data++;
1356 } else if (*data == 0x54) {
1357 data++;
1358 int mod, regop, rm;
1359 get_modrm(*data, &mod, &regop, &rm);
1360 AppendToBuffer("andpd %s,%s",
1361 NameOfXMMRegister(regop),
1362 NameOfXMMRegister(rm));
1363 data++;
1364 } else if (*data == 0x56) {
1365 data++;
1366 int mod, regop, rm;
1367 get_modrm(*data, &mod, &regop, &rm);
1368 AppendToBuffer("orpd %s,%s",
1369 NameOfXMMRegister(regop),
1370 NameOfXMMRegister(rm));
1371 data++;
1372 } else if (*data == 0x57) {
1373 data++;
1374 int mod, regop, rm;
1375 get_modrm(*data, &mod, &regop, &rm);
1376 AppendToBuffer("xorpd %s,%s",
1377 NameOfXMMRegister(regop),
1378 NameOfXMMRegister(rm));
1379 data++;
1380 } else if (*data == 0x6E) {
1381 data++;
1382 int mod, regop, rm;
1383 get_modrm(*data, &mod, &regop, &rm);
1384 AppendToBuffer("movd %s,", NameOfXMMRegister(regop));
1385 data += PrintRightOperand(data);
1386 } else if (*data == 0x6F) {
1387 data++;
1388 int mod, regop, rm;
1389 get_modrm(*data, &mod, &regop, &rm);
1390 AppendToBuffer("movdqa %s,", NameOfXMMRegister(regop));
1391 data += PrintRightXMMOperand(data);
1392 } else if (*data == 0x70) {
1393 data++;
1394 int mod, regop, rm;
1395 get_modrm(*data, &mod, &regop, &rm);
1396 int8_t imm8 = static_cast<int8_t>(data[1]);
1397 AppendToBuffer("pshufd %s,%s,%d",
1398 NameOfXMMRegister(regop),
1399 NameOfXMMRegister(rm),
1400 static_cast<int>(imm8));
1401 data += 2;
1402 } else if (*data == 0x76) {
1403 data++;
1404 int mod, regop, rm;
1405 get_modrm(*data, &mod, &regop, &rm);
1406 AppendToBuffer("pcmpeqd %s,%s",
1407 NameOfXMMRegister(regop),
1408 NameOfXMMRegister(rm));
1409 data++;
1410 } else if (*data == 0x90) {
1411 data++;
1412 AppendToBuffer("nop"); // 2 byte nop.
1413 } else if (*data == 0xF3) {
1414 data++;
1415 int mod, regop, rm;
1416 get_modrm(*data, &mod, &regop, &rm);
1417 AppendToBuffer("psllq %s,%s",
1418 NameOfXMMRegister(regop),
1419 NameOfXMMRegister(rm));
1420 data++;
1421 } else if (*data == 0x73) {
1422 data++;
1423 int mod, regop, rm;
1424 get_modrm(*data, &mod, &regop, &rm);
1425 int8_t imm8 = static_cast<int8_t>(data[1]);
1426 DCHECK(regop == esi || regop == edx);
1427 AppendToBuffer("%s %s,%d",
1428 (regop == esi) ? "psllq" : "psrlq",
1429 NameOfXMMRegister(rm),
1430 static_cast<int>(imm8));
1431 data += 2;
1432 } else if (*data == 0xD3) {
1433 data++;
1434 int mod, regop, rm;
1435 get_modrm(*data, &mod, &regop, &rm);
1436 AppendToBuffer("psrlq %s,%s",
1437 NameOfXMMRegister(regop),
1438 NameOfXMMRegister(rm));
1439 data++;
1440 } else if (*data == 0x7F) {
1441 AppendToBuffer("movdqa ");
1442 data++;
1443 int mod, regop, rm;
1444 get_modrm(*data, &mod, &regop, &rm);
1445 data += PrintRightXMMOperand(data);
1446 AppendToBuffer(",%s", NameOfXMMRegister(regop));
1447 } else if (*data == 0x7E) {
1448 data++;
1449 int mod, regop, rm;
1450 get_modrm(*data, &mod, &regop, &rm);
1451 AppendToBuffer("movd ");
1452 data += PrintRightOperand(data);
1453 AppendToBuffer(",%s", NameOfXMMRegister(regop));
1454 } else if (*data == 0xDB) {
1455 data++;
1456 int mod, regop, rm;
1457 get_modrm(*data, &mod, &regop, &rm);
1458 AppendToBuffer("pand %s,%s",
1459 NameOfXMMRegister(regop),
1460 NameOfXMMRegister(rm));
1461 data++;
1462 } else if (*data == 0xE7) {
1463 data++;
1464 int mod, regop, rm;
1465 get_modrm(*data, &mod, &regop, &rm);
1466 if (mod == 3) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001467 // movntdq
1468 UnimplementedInstruction();
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001469 } else {
1470 UnimplementedInstruction();
1471 }
1472 } else if (*data == 0xEF) {
1473 data++;
1474 int mod, regop, rm;
1475 get_modrm(*data, &mod, &regop, &rm);
1476 AppendToBuffer("pxor %s,%s",
1477 NameOfXMMRegister(regop),
1478 NameOfXMMRegister(rm));
1479 data++;
1480 } else if (*data == 0xEB) {
1481 data++;
1482 int mod, regop, rm;
1483 get_modrm(*data, &mod, &regop, &rm);
1484 AppendToBuffer("por %s,%s",
1485 NameOfXMMRegister(regop),
1486 NameOfXMMRegister(rm));
1487 data++;
1488 } else {
1489 UnimplementedInstruction();
1490 }
1491 } else {
1492 UnimplementedInstruction();
1493 }
1494 break;
1495
1496 case 0xFE:
1497 { data++;
1498 int mod, regop, rm;
1499 get_modrm(*data, &mod, &regop, &rm);
1500 if (regop == ecx) {
1501 AppendToBuffer("dec_b ");
1502 data += PrintRightOperand(data);
1503 } else {
1504 UnimplementedInstruction();
1505 }
1506 }
1507 break;
1508
1509 case 0x68:
1510 AppendToBuffer("push 0x%x", *reinterpret_cast<int32_t*>(data+1));
1511 data += 5;
1512 break;
1513
1514 case 0x6A:
1515 AppendToBuffer("push 0x%x", *reinterpret_cast<int8_t*>(data + 1));
1516 data += 2;
1517 break;
1518
1519 case 0xA8:
1520 AppendToBuffer("test al,0x%x", *reinterpret_cast<uint8_t*>(data+1));
1521 data += 2;
1522 break;
1523
1524 case 0xA9:
1525 AppendToBuffer("test eax,0x%x", *reinterpret_cast<int32_t*>(data+1));
1526 data += 5;
1527 break;
1528
1529 case 0xD1: // fall through
1530 case 0xD3: // fall through
1531 case 0xC1:
1532 data += D1D3C1Instruction(data);
1533 break;
1534
1535 case 0xD8: // fall through
1536 case 0xD9: // fall through
1537 case 0xDA: // fall through
1538 case 0xDB: // fall through
1539 case 0xDC: // fall through
1540 case 0xDD: // fall through
1541 case 0xDE: // fall through
1542 case 0xDF:
1543 data += FPUInstruction(data);
1544 break;
1545
1546 case 0xEB:
1547 data += JumpShort(data);
1548 break;
1549
1550 case 0xF2:
1551 if (*(data+1) == 0x0F) {
1552 byte b2 = *(data+2);
1553 if (b2 == 0x11) {
1554 AppendToBuffer("movsd ");
1555 data += 3;
1556 int mod, regop, rm;
1557 get_modrm(*data, &mod, &regop, &rm);
1558 data += PrintRightXMMOperand(data);
1559 AppendToBuffer(",%s", NameOfXMMRegister(regop));
1560 } else if (b2 == 0x10) {
1561 data += 3;
1562 int mod, regop, rm;
1563 get_modrm(*data, &mod, &regop, &rm);
1564 AppendToBuffer("movsd %s,", NameOfXMMRegister(regop));
1565 data += PrintRightXMMOperand(data);
1566 } else if (b2 == 0x5A) {
1567 data += 3;
1568 int mod, regop, rm;
1569 get_modrm(*data, &mod, &regop, &rm);
1570 AppendToBuffer("cvtsd2ss %s,", NameOfXMMRegister(regop));
1571 data += PrintRightXMMOperand(data);
1572 } else {
1573 const char* mnem = "?";
1574 switch (b2) {
1575 case 0x2A: mnem = "cvtsi2sd"; break;
1576 case 0x2C: mnem = "cvttsd2si"; break;
1577 case 0x2D: mnem = "cvtsd2si"; break;
1578 case 0x51: mnem = "sqrtsd"; break;
1579 case 0x58: mnem = "addsd"; break;
1580 case 0x59: mnem = "mulsd"; break;
1581 case 0x5C: mnem = "subsd"; break;
1582 case 0x5E: mnem = "divsd"; break;
1583 }
1584 data += 3;
1585 int mod, regop, rm;
1586 get_modrm(*data, &mod, &regop, &rm);
1587 if (b2 == 0x2A) {
1588 AppendToBuffer("%s %s,", mnem, NameOfXMMRegister(regop));
1589 data += PrintRightOperand(data);
1590 } else if (b2 == 0x2C || b2 == 0x2D) {
1591 AppendToBuffer("%s %s,", mnem, NameOfCPURegister(regop));
1592 data += PrintRightXMMOperand(data);
1593 } else if (b2 == 0xC2) {
1594 // Intel manual 2A, Table 3-18.
1595 const char* const pseudo_op[] = {
1596 "cmpeqsd",
1597 "cmpltsd",
1598 "cmplesd",
1599 "cmpunordsd",
1600 "cmpneqsd",
1601 "cmpnltsd",
1602 "cmpnlesd",
1603 "cmpordsd"
1604 };
1605 AppendToBuffer("%s %s,%s",
1606 pseudo_op[data[1]],
1607 NameOfXMMRegister(regop),
1608 NameOfXMMRegister(rm));
1609 data += 2;
1610 } else {
1611 AppendToBuffer("%s %s,", mnem, NameOfXMMRegister(regop));
1612 data += PrintRightXMMOperand(data);
1613 }
1614 }
1615 } else {
1616 UnimplementedInstruction();
1617 }
1618 break;
1619
1620 case 0xF3:
1621 if (*(data+1) == 0x0F) {
1622 byte b2 = *(data+2);
1623 if (b2 == 0x11) {
1624 AppendToBuffer("movss ");
1625 data += 3;
1626 int mod, regop, rm;
1627 get_modrm(*data, &mod, &regop, &rm);
1628 data += PrintRightXMMOperand(data);
1629 AppendToBuffer(",%s", NameOfXMMRegister(regop));
1630 } else if (b2 == 0x10) {
1631 data += 3;
1632 int mod, regop, rm;
1633 get_modrm(*data, &mod, &regop, &rm);
1634 AppendToBuffer("movss %s,", NameOfXMMRegister(regop));
1635 data += PrintRightXMMOperand(data);
1636 } else if (b2 == 0x2C) {
1637 data += 3;
1638 int mod, regop, rm;
1639 get_modrm(*data, &mod, &regop, &rm);
1640 AppendToBuffer("cvttss2si %s,", NameOfCPURegister(regop));
1641 data += PrintRightXMMOperand(data);
1642 } else if (b2 == 0x5A) {
1643 data += 3;
1644 int mod, regop, rm;
1645 get_modrm(*data, &mod, &regop, &rm);
1646 AppendToBuffer("cvtss2sd %s,", NameOfXMMRegister(regop));
1647 data += PrintRightXMMOperand(data);
1648 } else if (b2 == 0x6F) {
1649 data += 3;
1650 int mod, regop, rm;
1651 get_modrm(*data, &mod, &regop, &rm);
1652 AppendToBuffer("movdqu %s,", NameOfXMMRegister(regop));
1653 data += PrintRightXMMOperand(data);
1654 } else if (b2 == 0x7F) {
1655 AppendToBuffer("movdqu ");
1656 data += 3;
1657 int mod, regop, rm;
1658 get_modrm(*data, &mod, &regop, &rm);
1659 data += PrintRightXMMOperand(data);
1660 AppendToBuffer(",%s", NameOfXMMRegister(regop));
1661 } else {
1662 UnimplementedInstruction();
1663 }
1664 } else if (*(data+1) == 0xA5) {
1665 data += 2;
1666 AppendToBuffer("rep_movs");
1667 } else if (*(data+1) == 0xAB) {
1668 data += 2;
1669 AppendToBuffer("rep_stos");
1670 } else {
1671 UnimplementedInstruction();
1672 }
1673 break;
1674
1675 case 0xF7:
1676 data += F7Instruction(data);
1677 break;
1678
1679 default:
1680 UnimplementedInstruction();
1681 }
1682 }
1683
1684 if (tmp_buffer_pos_ < sizeof tmp_buffer_) {
1685 tmp_buffer_[tmp_buffer_pos_] = '\0';
1686 }
1687
1688 int instr_len = data - instr;
1689 if (instr_len == 0) {
1690 printf("%02x", *data);
1691 }
1692 DCHECK(instr_len > 0); // Ensure progress.
1693
1694 int outp = 0;
1695 // Instruction bytes.
1696 for (byte* bp = instr; bp < data; bp++) {
1697 outp += v8::internal::SNPrintF(out_buffer + outp, "%02x", *bp);
1698 }
1699 for (int i = 6 - instr_len; i >= 0; i--) {
1700 outp += v8::internal::SNPrintF(out_buffer + outp, " ");
1701 }
1702
1703 outp += v8::internal::SNPrintF(out_buffer + outp, " %s", tmp_buffer_.start());
1704 return instr_len;
1705} // NOLINT (function is too long)
1706
1707
1708//------------------------------------------------------------------------------
1709
1710
Emily Bernierd0a1eb72015-03-24 16:35:39 -04001711static const char* const cpu_regs[8] = {
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001712 "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi"
1713};
1714
1715
Emily Bernierd0a1eb72015-03-24 16:35:39 -04001716static const char* const byte_cpu_regs[8] = {
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001717 "al", "cl", "dl", "bl", "ah", "ch", "dh", "bh"
1718};
1719
1720
Emily Bernierd0a1eb72015-03-24 16:35:39 -04001721static const char* const xmm_regs[8] = {
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001722 "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7"
1723};
1724
1725
1726const char* NameConverter::NameOfAddress(byte* addr) const {
1727 v8::internal::SNPrintF(tmp_buffer_, "%p", addr);
1728 return tmp_buffer_.start();
1729}
1730
1731
1732const char* NameConverter::NameOfConstant(byte* addr) const {
1733 return NameOfAddress(addr);
1734}
1735
1736
1737const char* NameConverter::NameOfCPURegister(int reg) const {
1738 if (0 <= reg && reg < 8) return cpu_regs[reg];
1739 return "noreg";
1740}
1741
1742
1743const char* NameConverter::NameOfByteCPURegister(int reg) const {
1744 if (0 <= reg && reg < 8) return byte_cpu_regs[reg];
1745 return "noreg";
1746}
1747
1748
1749const char* NameConverter::NameOfXMMRegister(int reg) const {
1750 if (0 <= reg && reg < 8) return xmm_regs[reg];
1751 return "noxmmreg";
1752}
1753
1754
1755const char* NameConverter::NameInCode(byte* addr) const {
1756 // X87 does not embed debug strings at the moment.
1757 UNREACHABLE();
1758 return "";
1759}
1760
1761
1762//------------------------------------------------------------------------------
1763
1764Disassembler::Disassembler(const NameConverter& converter)
1765 : converter_(converter) {}
1766
1767
1768Disassembler::~Disassembler() {}
1769
1770
1771int Disassembler::InstructionDecode(v8::internal::Vector<char> buffer,
1772 byte* instruction) {
1773 DisassemblerX87 d(converter_, false /*do not crash if unimplemented*/);
1774 return d.InstructionDecode(buffer, instruction);
1775}
1776
1777
1778// The IA-32 assembler does not currently use constant pools.
1779int Disassembler::ConstantPoolSizeAt(byte* instruction) { return -1; }
1780
1781
1782/*static*/ void Disassembler::Disassemble(FILE* f, byte* begin, byte* end) {
1783 NameConverter converter;
1784 Disassembler d(converter);
1785 for (byte* pc = begin; pc < end;) {
1786 v8::internal::EmbeddedVector<char, 128> buffer;
1787 buffer[0] = '\0';
1788 byte* prev_pc = pc;
1789 pc += d.InstructionDecode(buffer, pc);
1790 fprintf(f, "%p", prev_pc);
1791 fprintf(f, " ");
1792
1793 for (byte* bp = prev_pc; bp < pc; bp++) {
1794 fprintf(f, "%02x", *bp);
1795 }
1796 for (int i = 6 - (pc - prev_pc); i >= 0; i--) {
1797 fprintf(f, " ");
1798 }
1799 fprintf(f, " %s\n", buffer.start());
1800 }
1801}
1802
1803
1804} // namespace disasm
1805
1806#endif // V8_TARGET_ARCH_X87