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