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