• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2011 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are
4 // met:
5 //
6 //     * Redistributions of source code must retain the above copyright
7 //       notice, this list of conditions and the following disclaimer.
8 //     * Redistributions in binary form must reproduce the above
9 //       copyright notice, this list of conditions and the following
10 //       disclaimer in the documentation and/or other materials provided
11 //       with the distribution.
12 //     * Neither the name of Google Inc. nor the names of its
13 //       contributors may be used to endorse or promote products derived
14 //       from this software without specific prior written permission.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 
28 #include <assert.h>
29 #include <stdio.h>
30 #include <stdarg.h>
31 
32 #include "v8.h"
33 
34 #if defined(V8_TARGET_ARCH_X64)
35 
36 #include "disasm.h"
37 #include "lazy-instance.h"
38 
39 namespace disasm {
40 
41 enum OperandType {
42   UNSET_OP_ORDER = 0,
43   // Operand size decides between 16, 32 and 64 bit operands.
44   REG_OPER_OP_ORDER = 1,  // Register destination, operand source.
45   OPER_REG_OP_ORDER = 2,  // Operand destination, register source.
46   // Fixed 8-bit operands.
47   BYTE_SIZE_OPERAND_FLAG = 4,
48   BYTE_REG_OPER_OP_ORDER = REG_OPER_OP_ORDER | BYTE_SIZE_OPERAND_FLAG,
49   BYTE_OPER_REG_OP_ORDER = OPER_REG_OP_ORDER | BYTE_SIZE_OPERAND_FLAG
50 };
51 
52 //------------------------------------------------------------------
53 // Tables
54 //------------------------------------------------------------------
55 struct ByteMnemonic {
56   int b;  // -1 terminates, otherwise must be in range (0..255)
57   OperandType op_order_;
58   const char* mnem;
59 };
60 
61 
62 static const ByteMnemonic two_operands_instr[] = {
63   { 0x00, BYTE_OPER_REG_OP_ORDER, "add" },
64   { 0x01, OPER_REG_OP_ORDER,      "add" },
65   { 0x02, BYTE_REG_OPER_OP_ORDER, "add" },
66   { 0x03, REG_OPER_OP_ORDER,      "add" },
67   { 0x08, BYTE_OPER_REG_OP_ORDER, "or" },
68   { 0x09, OPER_REG_OP_ORDER,      "or" },
69   { 0x0A, BYTE_REG_OPER_OP_ORDER, "or" },
70   { 0x0B, REG_OPER_OP_ORDER,      "or" },
71   { 0x10, BYTE_OPER_REG_OP_ORDER, "adc" },
72   { 0x11, OPER_REG_OP_ORDER,      "adc" },
73   { 0x12, BYTE_REG_OPER_OP_ORDER, "adc" },
74   { 0x13, REG_OPER_OP_ORDER,      "adc" },
75   { 0x18, BYTE_OPER_REG_OP_ORDER, "sbb" },
76   { 0x19, OPER_REG_OP_ORDER,      "sbb" },
77   { 0x1A, BYTE_REG_OPER_OP_ORDER, "sbb" },
78   { 0x1B, REG_OPER_OP_ORDER,      "sbb" },
79   { 0x20, BYTE_OPER_REG_OP_ORDER, "and" },
80   { 0x21, OPER_REG_OP_ORDER,      "and" },
81   { 0x22, BYTE_REG_OPER_OP_ORDER, "and" },
82   { 0x23, REG_OPER_OP_ORDER,      "and" },
83   { 0x28, BYTE_OPER_REG_OP_ORDER, "sub" },
84   { 0x29, OPER_REG_OP_ORDER,      "sub" },
85   { 0x2A, BYTE_REG_OPER_OP_ORDER, "sub" },
86   { 0x2B, REG_OPER_OP_ORDER,      "sub" },
87   { 0x30, BYTE_OPER_REG_OP_ORDER, "xor" },
88   { 0x31, OPER_REG_OP_ORDER,      "xor" },
89   { 0x32, BYTE_REG_OPER_OP_ORDER, "xor" },
90   { 0x33, REG_OPER_OP_ORDER,      "xor" },
91   { 0x38, BYTE_OPER_REG_OP_ORDER, "cmp" },
92   { 0x39, OPER_REG_OP_ORDER,      "cmp" },
93   { 0x3A, BYTE_REG_OPER_OP_ORDER, "cmp" },
94   { 0x3B, REG_OPER_OP_ORDER,      "cmp" },
95   { 0x63, REG_OPER_OP_ORDER,      "movsxlq" },
96   { 0x84, BYTE_REG_OPER_OP_ORDER, "test" },
97   { 0x85, REG_OPER_OP_ORDER,      "test" },
98   { 0x86, BYTE_REG_OPER_OP_ORDER, "xchg" },
99   { 0x87, REG_OPER_OP_ORDER,      "xchg" },
100   { 0x88, BYTE_OPER_REG_OP_ORDER, "mov" },
101   { 0x89, OPER_REG_OP_ORDER,      "mov" },
102   { 0x8A, BYTE_REG_OPER_OP_ORDER, "mov" },
103   { 0x8B, REG_OPER_OP_ORDER,      "mov" },
104   { 0x8D, REG_OPER_OP_ORDER,      "lea" },
105   { -1, UNSET_OP_ORDER, "" }
106 };
107 
108 
109 static const ByteMnemonic zero_operands_instr[] = {
110   { 0xC3, UNSET_OP_ORDER, "ret" },
111   { 0xC9, UNSET_OP_ORDER, "leave" },
112   { 0xF4, UNSET_OP_ORDER, "hlt" },
113   { 0xFC, UNSET_OP_ORDER, "cld" },
114   { 0xCC, UNSET_OP_ORDER, "int3" },
115   { 0x60, UNSET_OP_ORDER, "pushad" },
116   { 0x61, UNSET_OP_ORDER, "popad" },
117   { 0x9C, UNSET_OP_ORDER, "pushfd" },
118   { 0x9D, UNSET_OP_ORDER, "popfd" },
119   { 0x9E, UNSET_OP_ORDER, "sahf" },
120   { 0x99, UNSET_OP_ORDER, "cdq" },
121   { 0x9B, UNSET_OP_ORDER, "fwait" },
122   { 0xA4, UNSET_OP_ORDER, "movs" },
123   { 0xA5, UNSET_OP_ORDER, "movs" },
124   { 0xA6, UNSET_OP_ORDER, "cmps" },
125   { 0xA7, UNSET_OP_ORDER, "cmps" },
126   { -1, UNSET_OP_ORDER, "" }
127 };
128 
129 
130 static const ByteMnemonic call_jump_instr[] = {
131   { 0xE8, UNSET_OP_ORDER, "call" },
132   { 0xE9, UNSET_OP_ORDER, "jmp" },
133   { -1, UNSET_OP_ORDER, "" }
134 };
135 
136 
137 static const ByteMnemonic short_immediate_instr[] = {
138   { 0x05, UNSET_OP_ORDER, "add" },
139   { 0x0D, UNSET_OP_ORDER, "or" },
140   { 0x15, UNSET_OP_ORDER, "adc" },
141   { 0x1D, UNSET_OP_ORDER, "sbb" },
142   { 0x25, UNSET_OP_ORDER, "and" },
143   { 0x2D, UNSET_OP_ORDER, "sub" },
144   { 0x35, UNSET_OP_ORDER, "xor" },
145   { 0x3D, UNSET_OP_ORDER, "cmp" },
146   { -1, UNSET_OP_ORDER, "" }
147 };
148 
149 
150 static const char* const conditional_code_suffix[] = {
151   "o", "no", "c", "nc", "z", "nz", "na", "a",
152   "s", "ns", "pe", "po", "l", "ge", "le", "g"
153 };
154 
155 
156 enum InstructionType {
157   NO_INSTR,
158   ZERO_OPERANDS_INSTR,
159   TWO_OPERANDS_INSTR,
160   JUMP_CONDITIONAL_SHORT_INSTR,
161   REGISTER_INSTR,
162   PUSHPOP_INSTR,  // Has implicit 64-bit operand size.
163   MOVE_REG_INSTR,
164   CALL_JUMP_INSTR,
165   SHORT_IMMEDIATE_INSTR
166 };
167 
168 
169 enum Prefixes {
170   ESCAPE_PREFIX = 0x0F,
171   OPERAND_SIZE_OVERRIDE_PREFIX = 0x66,
172   ADDRESS_SIZE_OVERRIDE_PREFIX = 0x67,
173   REPNE_PREFIX = 0xF2,
174   REP_PREFIX = 0xF3,
175   REPEQ_PREFIX = REP_PREFIX
176 };
177 
178 
179 struct InstructionDesc {
180   const char* mnem;
181   InstructionType type;
182   OperandType op_order_;
183   bool byte_size_operation;  // Fixed 8-bit operation.
184 };
185 
186 
187 class InstructionTable {
188  public:
189   InstructionTable();
Get(byte x) const190   const InstructionDesc& Get(byte x) const {
191     return instructions_[x];
192   }
193 
194  private:
195   InstructionDesc instructions_[256];
196   void Clear();
197   void Init();
198   void CopyTable(const ByteMnemonic bm[], InstructionType type);
199   void SetTableRange(InstructionType type, byte start, byte end, bool byte_size,
200                      const char* mnem);
201   void AddJumpConditionalShort();
202 };
203 
204 
InstructionTable()205 InstructionTable::InstructionTable() {
206   Clear();
207   Init();
208 }
209 
210 
Clear()211 void InstructionTable::Clear() {
212   for (int i = 0; i < 256; i++) {
213     instructions_[i].mnem = "(bad)";
214     instructions_[i].type = NO_INSTR;
215     instructions_[i].op_order_ = UNSET_OP_ORDER;
216     instructions_[i].byte_size_operation = false;
217   }
218 }
219 
220 
Init()221 void InstructionTable::Init() {
222   CopyTable(two_operands_instr, TWO_OPERANDS_INSTR);
223   CopyTable(zero_operands_instr, ZERO_OPERANDS_INSTR);
224   CopyTable(call_jump_instr, CALL_JUMP_INSTR);
225   CopyTable(short_immediate_instr, SHORT_IMMEDIATE_INSTR);
226   AddJumpConditionalShort();
227   SetTableRange(PUSHPOP_INSTR, 0x50, 0x57, false, "push");
228   SetTableRange(PUSHPOP_INSTR, 0x58, 0x5F, false, "pop");
229   SetTableRange(MOVE_REG_INSTR, 0xB8, 0xBF, false, "mov");
230 }
231 
232 
CopyTable(const ByteMnemonic bm[],InstructionType type)233 void InstructionTable::CopyTable(const ByteMnemonic bm[],
234                                  InstructionType type) {
235   for (int i = 0; bm[i].b >= 0; i++) {
236     InstructionDesc* id = &instructions_[bm[i].b];
237     id->mnem = bm[i].mnem;
238     OperandType op_order = bm[i].op_order_;
239     id->op_order_ =
240         static_cast<OperandType>(op_order & ~BYTE_SIZE_OPERAND_FLAG);
241     ASSERT_EQ(NO_INSTR, id->type);  // Information not already entered
242     id->type = type;
243     id->byte_size_operation = ((op_order & BYTE_SIZE_OPERAND_FLAG) != 0);
244   }
245 }
246 
247 
SetTableRange(InstructionType type,byte start,byte end,bool byte_size,const char * mnem)248 void InstructionTable::SetTableRange(InstructionType type,
249                                      byte start,
250                                      byte end,
251                                      bool byte_size,
252                                      const char* mnem) {
253   for (byte b = start; b <= end; b++) {
254     InstructionDesc* id = &instructions_[b];
255     ASSERT_EQ(NO_INSTR, id->type);  // Information not already entered
256     id->mnem = mnem;
257     id->type = type;
258     id->byte_size_operation = byte_size;
259   }
260 }
261 
262 
AddJumpConditionalShort()263 void InstructionTable::AddJumpConditionalShort() {
264   for (byte b = 0x70; b <= 0x7F; b++) {
265     InstructionDesc* id = &instructions_[b];
266     ASSERT_EQ(NO_INSTR, id->type);  // Information not already entered
267     id->mnem = NULL;  // Computed depending on condition code.
268     id->type = JUMP_CONDITIONAL_SHORT_INSTR;
269   }
270 }
271 
272 
273 static v8::internal::LazyInstance<InstructionTable>::type instruction_table =
274     LAZY_INSTANCE_INITIALIZER;
275 
276 
277 static InstructionDesc cmov_instructions[16] = {
278   {"cmovo", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
279   {"cmovno", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
280   {"cmovc", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
281   {"cmovnc", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
282   {"cmovz", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
283   {"cmovnz", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
284   {"cmovna", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
285   {"cmova", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
286   {"cmovs", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
287   {"cmovns", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
288   {"cmovpe", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
289   {"cmovpo", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
290   {"cmovl", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
291   {"cmovge", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
292   {"cmovle", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
293   {"cmovg", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false}
294 };
295 
296 //------------------------------------------------------------------------------
297 // DisassemblerX64 implementation.
298 
299 enum UnimplementedOpcodeAction {
300   CONTINUE_ON_UNIMPLEMENTED_OPCODE,
301   ABORT_ON_UNIMPLEMENTED_OPCODE
302 };
303 
304 // A new DisassemblerX64 object is created to disassemble each instruction.
305 // The object can only disassemble a single instruction.
306 class DisassemblerX64 {
307  public:
DisassemblerX64(const NameConverter & converter,UnimplementedOpcodeAction unimplemented_action=ABORT_ON_UNIMPLEMENTED_OPCODE)308   DisassemblerX64(const NameConverter& converter,
309                   UnimplementedOpcodeAction unimplemented_action =
310                       ABORT_ON_UNIMPLEMENTED_OPCODE)
311       : converter_(converter),
312         tmp_buffer_pos_(0),
313         abort_on_unimplemented_(
314             unimplemented_action == ABORT_ON_UNIMPLEMENTED_OPCODE),
315         rex_(0),
316         operand_size_(0),
317         group_1_prefix_(0),
318         byte_size_operand_(false) {
319     tmp_buffer_[0] = '\0';
320   }
321 
~DisassemblerX64()322   virtual ~DisassemblerX64() {
323   }
324 
325   // Writes one disassembled instruction into 'buffer' (0-terminated).
326   // Returns the length of the disassembled machine instruction in bytes.
327   int InstructionDecode(v8::internal::Vector<char> buffer, byte* instruction);
328 
329  private:
330   enum OperandSize {
331     BYTE_SIZE = 0,
332     WORD_SIZE = 1,
333     DOUBLEWORD_SIZE = 2,
334     QUADWORD_SIZE = 3
335   };
336 
337   const NameConverter& converter_;
338   v8::internal::EmbeddedVector<char, 128> tmp_buffer_;
339   unsigned int tmp_buffer_pos_;
340   bool abort_on_unimplemented_;
341   // Prefixes parsed
342   byte rex_;
343   byte operand_size_;  // 0x66 or (if no group 3 prefix is present) 0x0.
344   byte group_1_prefix_;  // 0xF2, 0xF3, or (if no group 1 prefix is present) 0.
345   // Byte size operand override.
346   bool byte_size_operand_;
347 
setRex(byte rex)348   void setRex(byte rex) {
349     ASSERT_EQ(0x40, rex & 0xF0);
350     rex_ = rex;
351   }
352 
rex()353   bool rex() { return rex_ != 0; }
354 
rex_b()355   bool rex_b() { return (rex_ & 0x01) != 0; }
356 
357   // Actual number of base register given the low bits and the rex.b state.
base_reg(int low_bits)358   int base_reg(int low_bits) { return low_bits | ((rex_ & 0x01) << 3); }
359 
rex_x()360   bool rex_x() { return (rex_ & 0x02) != 0; }
361 
rex_r()362   bool rex_r() { return (rex_ & 0x04) != 0; }
363 
rex_w()364   bool rex_w() { return (rex_ & 0x08) != 0; }
365 
operand_size()366   OperandSize operand_size() {
367     if (byte_size_operand_) return BYTE_SIZE;
368     if (rex_w()) return QUADWORD_SIZE;
369     if (operand_size_ != 0) return WORD_SIZE;
370     return DOUBLEWORD_SIZE;
371   }
372 
operand_size_code()373   char operand_size_code() {
374     return "bwlq"[operand_size()];
375   }
376 
NameOfCPURegister(int reg) const377   const char* NameOfCPURegister(int reg) const {
378     return converter_.NameOfCPURegister(reg);
379   }
380 
NameOfByteCPURegister(int reg) const381   const char* NameOfByteCPURegister(int reg) const {
382     return converter_.NameOfByteCPURegister(reg);
383   }
384 
NameOfXMMRegister(int reg) const385   const char* NameOfXMMRegister(int reg) const {
386     return converter_.NameOfXMMRegister(reg);
387   }
388 
NameOfAddress(byte * addr) const389   const char* NameOfAddress(byte* addr) const {
390     return converter_.NameOfAddress(addr);
391   }
392 
393   // Disassembler helper functions.
get_modrm(byte data,int * mod,int * regop,int * rm)394   void get_modrm(byte data,
395                  int* mod,
396                  int* regop,
397                  int* rm) {
398     *mod = (data >> 6) & 3;
399     *regop = ((data & 0x38) >> 3) | (rex_r() ? 8 : 0);
400     *rm = (data & 7) | (rex_b() ? 8 : 0);
401   }
402 
get_sib(byte data,int * scale,int * index,int * base)403   void get_sib(byte data,
404                int* scale,
405                int* index,
406                int* base) {
407     *scale = (data >> 6) & 3;
408     *index = ((data >> 3) & 7) | (rex_x() ? 8 : 0);
409     *base = (data & 7) | (rex_b() ? 8 : 0);
410   }
411 
412   typedef const char* (DisassemblerX64::*RegisterNameMapping)(int reg) const;
413 
414   int PrintRightOperandHelper(byte* modrmp,
415                               RegisterNameMapping register_name);
416   int PrintRightOperand(byte* modrmp);
417   int PrintRightByteOperand(byte* modrmp);
418   int PrintRightXMMOperand(byte* modrmp);
419   int PrintOperands(const char* mnem,
420                     OperandType op_order,
421                     byte* data);
422   int PrintImmediate(byte* data, OperandSize size);
423   int PrintImmediateOp(byte* data);
424   const char* TwoByteMnemonic(byte opcode);
425   int TwoByteOpcodeInstruction(byte* data);
426   int F6F7Instruction(byte* data);
427   int ShiftInstruction(byte* data);
428   int JumpShort(byte* data);
429   int JumpConditional(byte* data);
430   int JumpConditionalShort(byte* data);
431   int SetCC(byte* data);
432   int FPUInstruction(byte* data);
433   int MemoryFPUInstruction(int escape_opcode, int regop, byte* modrm_start);
434   int RegisterFPUInstruction(int escape_opcode, byte modrm_byte);
435   void AppendToBuffer(const char* format, ...);
436 
UnimplementedInstruction()437   void UnimplementedInstruction() {
438     if (abort_on_unimplemented_) {
439       CHECK(false);
440     } else {
441       AppendToBuffer("'Unimplemented Instruction'");
442     }
443   }
444 };
445 
446 
AppendToBuffer(const char * format,...)447 void DisassemblerX64::AppendToBuffer(const char* format, ...) {
448   v8::internal::Vector<char> buf = tmp_buffer_ + tmp_buffer_pos_;
449   va_list args;
450   va_start(args, format);
451   int result = v8::internal::OS::VSNPrintF(buf, format, args);
452   va_end(args);
453   tmp_buffer_pos_ += result;
454 }
455 
456 
PrintRightOperandHelper(byte * modrmp,RegisterNameMapping direct_register_name)457 int DisassemblerX64::PrintRightOperandHelper(
458     byte* modrmp,
459     RegisterNameMapping direct_register_name) {
460   int mod, regop, rm;
461   get_modrm(*modrmp, &mod, &regop, &rm);
462   RegisterNameMapping register_name = (mod == 3) ? direct_register_name :
463       &DisassemblerX64::NameOfCPURegister;
464   switch (mod) {
465     case 0:
466       if ((rm & 7) == 5) {
467         int32_t disp = *reinterpret_cast<int32_t*>(modrmp + 1);
468         AppendToBuffer("[0x%x]", disp);
469         return 5;
470       } else if ((rm & 7) == 4) {
471         // Codes for SIB byte.
472         byte sib = *(modrmp + 1);
473         int scale, index, base;
474         get_sib(sib, &scale, &index, &base);
475         if (index == 4 && (base & 7) == 4 && scale == 0 /*times_1*/) {
476           // index == rsp means no index. Only use sib byte with no index for
477           // rsp and r12 base.
478           AppendToBuffer("[%s]", NameOfCPURegister(base));
479           return 2;
480         } else if (base == 5) {
481           // base == rbp means no base register (when mod == 0).
482           int32_t disp = *reinterpret_cast<int32_t*>(modrmp + 2);
483           AppendToBuffer("[%s*%d+0x%x]",
484                          NameOfCPURegister(index),
485                          1 << scale, disp);
486           return 6;
487         } else if (index != 4 && base != 5) {
488           // [base+index*scale]
489           AppendToBuffer("[%s+%s*%d]",
490                          NameOfCPURegister(base),
491                          NameOfCPURegister(index),
492                          1 << scale);
493           return 2;
494         } else {
495           UnimplementedInstruction();
496           return 1;
497         }
498       } else {
499         AppendToBuffer("[%s]", NameOfCPURegister(rm));
500         return 1;
501       }
502       break;
503     case 1:  // fall through
504     case 2:
505       if ((rm & 7) == 4) {
506         byte sib = *(modrmp + 1);
507         int scale, index, base;
508         get_sib(sib, &scale, &index, &base);
509         int disp = (mod == 2) ? *reinterpret_cast<int32_t*>(modrmp + 2)
510                               : *reinterpret_cast<char*>(modrmp + 2);
511         if (index == 4 && (base & 7) == 4 && scale == 0 /*times_1*/) {
512           if (-disp > 0) {
513             AppendToBuffer("[%s-0x%x]", NameOfCPURegister(base), -disp);
514           } else {
515             AppendToBuffer("[%s+0x%x]", NameOfCPURegister(base), disp);
516           }
517         } else {
518           if (-disp > 0) {
519             AppendToBuffer("[%s+%s*%d-0x%x]",
520                            NameOfCPURegister(base),
521                            NameOfCPURegister(index),
522                            1 << scale,
523                            -disp);
524           } else {
525             AppendToBuffer("[%s+%s*%d+0x%x]",
526                            NameOfCPURegister(base),
527                            NameOfCPURegister(index),
528                            1 << scale,
529                            disp);
530           }
531         }
532         return mod == 2 ? 6 : 3;
533       } else {
534         // No sib.
535         int disp = (mod == 2) ? *reinterpret_cast<int32_t*>(modrmp + 1)
536                               : *reinterpret_cast<char*>(modrmp + 1);
537         if (-disp > 0) {
538         AppendToBuffer("[%s-0x%x]", NameOfCPURegister(rm), -disp);
539         } else {
540         AppendToBuffer("[%s+0x%x]", NameOfCPURegister(rm), disp);
541         }
542         return (mod == 2) ? 5 : 2;
543       }
544       break;
545     case 3:
546       AppendToBuffer("%s", (this->*register_name)(rm));
547       return 1;
548     default:
549       UnimplementedInstruction();
550       return 1;
551   }
552   UNREACHABLE();
553 }
554 
555 
PrintImmediate(byte * data,OperandSize size)556 int DisassemblerX64::PrintImmediate(byte* data, OperandSize size) {
557   int64_t value;
558   int count;
559   switch (size) {
560     case BYTE_SIZE:
561       value = *data;
562       count = 1;
563       break;
564     case WORD_SIZE:
565       value = *reinterpret_cast<int16_t*>(data);
566       count = 2;
567       break;
568     case DOUBLEWORD_SIZE:
569       value = *reinterpret_cast<uint32_t*>(data);
570       count = 4;
571       break;
572     case QUADWORD_SIZE:
573       value = *reinterpret_cast<int32_t*>(data);
574       count = 4;
575       break;
576     default:
577       UNREACHABLE();
578       value = 0;  // Initialize variables on all paths to satisfy the compiler.
579       count = 0;
580   }
581   AppendToBuffer("%" V8_PTR_PREFIX "x", value);
582   return count;
583 }
584 
585 
PrintRightOperand(byte * modrmp)586 int DisassemblerX64::PrintRightOperand(byte* modrmp) {
587   return PrintRightOperandHelper(modrmp,
588                                  &DisassemblerX64::NameOfCPURegister);
589 }
590 
591 
PrintRightByteOperand(byte * modrmp)592 int DisassemblerX64::PrintRightByteOperand(byte* modrmp) {
593   return PrintRightOperandHelper(modrmp,
594                                  &DisassemblerX64::NameOfByteCPURegister);
595 }
596 
597 
PrintRightXMMOperand(byte * modrmp)598 int DisassemblerX64::PrintRightXMMOperand(byte* modrmp) {
599   return PrintRightOperandHelper(modrmp,
600                                  &DisassemblerX64::NameOfXMMRegister);
601 }
602 
603 
604 // Returns number of bytes used including the current *data.
605 // Writes instruction's mnemonic, left and right operands to 'tmp_buffer_'.
PrintOperands(const char * mnem,OperandType op_order,byte * data)606 int DisassemblerX64::PrintOperands(const char* mnem,
607                                    OperandType op_order,
608                                    byte* data) {
609   byte modrm = *data;
610   int mod, regop, rm;
611   get_modrm(modrm, &mod, &regop, &rm);
612   int advance = 0;
613   const char* register_name =
614       byte_size_operand_ ? NameOfByteCPURegister(regop)
615                          : NameOfCPURegister(regop);
616   switch (op_order) {
617     case REG_OPER_OP_ORDER: {
618       AppendToBuffer("%s%c %s,",
619                      mnem,
620                      operand_size_code(),
621                      register_name);
622       advance = byte_size_operand_ ? PrintRightByteOperand(data)
623                                    : PrintRightOperand(data);
624       break;
625     }
626     case OPER_REG_OP_ORDER: {
627       AppendToBuffer("%s%c ", mnem, operand_size_code());
628       advance = byte_size_operand_ ? PrintRightByteOperand(data)
629                                    : PrintRightOperand(data);
630       AppendToBuffer(",%s", register_name);
631       break;
632     }
633     default:
634       UNREACHABLE();
635       break;
636   }
637   return advance;
638 }
639 
640 
641 // Returns number of bytes used by machine instruction, including *data byte.
642 // Writes immediate instructions to 'tmp_buffer_'.
PrintImmediateOp(byte * data)643 int DisassemblerX64::PrintImmediateOp(byte* data) {
644   bool byte_size_immediate = (*data & 0x02) != 0;
645   byte modrm = *(data + 1);
646   int mod, regop, rm;
647   get_modrm(modrm, &mod, &regop, &rm);
648   const char* mnem = "Imm???";
649   switch (regop) {
650     case 0:
651       mnem = "add";
652       break;
653     case 1:
654       mnem = "or";
655       break;
656     case 2:
657       mnem = "adc";
658       break;
659     case 3:
660       mnem = "sbb";
661       break;
662     case 4:
663       mnem = "and";
664       break;
665     case 5:
666       mnem = "sub";
667       break;
668     case 6:
669       mnem = "xor";
670       break;
671     case 7:
672       mnem = "cmp";
673       break;
674     default:
675       UnimplementedInstruction();
676   }
677   AppendToBuffer("%s%c ", mnem, operand_size_code());
678   int count = PrintRightOperand(data + 1);
679   AppendToBuffer(",0x");
680   OperandSize immediate_size = byte_size_immediate ? BYTE_SIZE : operand_size();
681   count += PrintImmediate(data + 1 + count, immediate_size);
682   return 1 + count;
683 }
684 
685 
686 // Returns number of bytes used, including *data.
F6F7Instruction(byte * data)687 int DisassemblerX64::F6F7Instruction(byte* data) {
688   ASSERT(*data == 0xF7 || *data == 0xF6);
689   byte modrm = *(data + 1);
690   int mod, regop, rm;
691   get_modrm(modrm, &mod, &regop, &rm);
692   if (mod == 3 && regop != 0) {
693     const char* mnem = NULL;
694     switch (regop) {
695       case 2:
696         mnem = "not";
697         break;
698       case 3:
699         mnem = "neg";
700         break;
701       case 4:
702         mnem = "mul";
703         break;
704       case 7:
705         mnem = "idiv";
706         break;
707       default:
708         UnimplementedInstruction();
709     }
710     AppendToBuffer("%s%c %s",
711                    mnem,
712                    operand_size_code(),
713                    NameOfCPURegister(rm));
714     return 2;
715   } else if (regop == 0) {
716     AppendToBuffer("test%c ", operand_size_code());
717     int count = PrintRightOperand(data + 1);  // Use name of 64-bit register.
718     AppendToBuffer(",0x");
719     count += PrintImmediate(data + 1 + count, operand_size());
720     return 1 + count;
721   } else {
722     UnimplementedInstruction();
723     return 2;
724   }
725 }
726 
727 
ShiftInstruction(byte * data)728 int DisassemblerX64::ShiftInstruction(byte* data) {
729   byte op = *data & (~1);
730   if (op != 0xD0 && op != 0xD2 && op != 0xC0) {
731     UnimplementedInstruction();
732     return 1;
733   }
734   byte modrm = *(data + 1);
735   int mod, regop, rm;
736   get_modrm(modrm, &mod, &regop, &rm);
737   regop &= 0x7;  // The REX.R bit does not affect the operation.
738   int imm8 = -1;
739   int num_bytes = 2;
740   if (mod != 3) {
741     UnimplementedInstruction();
742     return num_bytes;
743   }
744   const char* mnem = NULL;
745   switch (regop) {
746     case 0:
747       mnem = "rol";
748       break;
749     case 1:
750       mnem = "ror";
751       break;
752     case 2:
753       mnem = "rcl";
754       break;
755     case 3:
756       mnem = "rcr";
757       break;
758     case 4:
759       mnem = "shl";
760       break;
761     case 5:
762       mnem = "shr";
763       break;
764     case 7:
765       mnem = "sar";
766       break;
767     default:
768       UnimplementedInstruction();
769       return num_bytes;
770   }
771   ASSERT_NE(NULL, mnem);
772   if (op == 0xD0) {
773     imm8 = 1;
774   } else if (op == 0xC0) {
775     imm8 = *(data + 2);
776     num_bytes = 3;
777   }
778   AppendToBuffer("%s%c %s,",
779                  mnem,
780                  operand_size_code(),
781                  byte_size_operand_ ? NameOfByteCPURegister(rm)
782                                     : NameOfCPURegister(rm));
783   if (op == 0xD2) {
784     AppendToBuffer("cl");
785   } else {
786     AppendToBuffer("%d", imm8);
787   }
788   return num_bytes;
789 }
790 
791 
792 // Returns number of bytes used, including *data.
JumpShort(byte * data)793 int DisassemblerX64::JumpShort(byte* data) {
794   ASSERT_EQ(0xEB, *data);
795   byte b = *(data + 1);
796   byte* dest = data + static_cast<int8_t>(b) + 2;
797   AppendToBuffer("jmp %s", NameOfAddress(dest));
798   return 2;
799 }
800 
801 
802 // Returns number of bytes used, including *data.
JumpConditional(byte * data)803 int DisassemblerX64::JumpConditional(byte* data) {
804   ASSERT_EQ(0x0F, *data);
805   byte cond = *(data + 1) & 0x0F;
806   byte* dest = data + *reinterpret_cast<int32_t*>(data + 2) + 6;
807   const char* mnem = conditional_code_suffix[cond];
808   AppendToBuffer("j%s %s", mnem, NameOfAddress(dest));
809   return 6;  // includes 0x0F
810 }
811 
812 
813 // Returns number of bytes used, including *data.
JumpConditionalShort(byte * data)814 int DisassemblerX64::JumpConditionalShort(byte* data) {
815   byte cond = *data & 0x0F;
816   byte b = *(data + 1);
817   byte* dest = data + static_cast<int8_t>(b) + 2;
818   const char* mnem = conditional_code_suffix[cond];
819   AppendToBuffer("j%s %s", mnem, NameOfAddress(dest));
820   return 2;
821 }
822 
823 
824 // Returns number of bytes used, including *data.
SetCC(byte * data)825 int DisassemblerX64::SetCC(byte* data) {
826   ASSERT_EQ(0x0F, *data);
827   byte cond = *(data + 1) & 0x0F;
828   const char* mnem = conditional_code_suffix[cond];
829   AppendToBuffer("set%s%c ", mnem, operand_size_code());
830   PrintRightByteOperand(data + 2);
831   return 3;  // includes 0x0F
832 }
833 
834 
835 // Returns number of bytes used, including *data.
FPUInstruction(byte * data)836 int DisassemblerX64::FPUInstruction(byte* data) {
837   byte escape_opcode = *data;
838   ASSERT_EQ(0xD8, escape_opcode & 0xF8);
839   byte modrm_byte = *(data+1);
840 
841   if (modrm_byte >= 0xC0) {
842     return RegisterFPUInstruction(escape_opcode, modrm_byte);
843   } else {
844     return MemoryFPUInstruction(escape_opcode, modrm_byte, data+1);
845   }
846 }
847 
MemoryFPUInstruction(int escape_opcode,int modrm_byte,byte * modrm_start)848 int DisassemblerX64::MemoryFPUInstruction(int escape_opcode,
849                                            int modrm_byte,
850                                            byte* modrm_start) {
851   const char* mnem = "?";
852   int regop = (modrm_byte >> 3) & 0x7;  // reg/op field of modrm byte.
853   switch (escape_opcode) {
854     case 0xD9: switch (regop) {
855         case 0: mnem = "fld_s"; break;
856         case 3: mnem = "fstp_s"; break;
857         case 7: mnem = "fstcw"; break;
858         default: UnimplementedInstruction();
859       }
860       break;
861 
862     case 0xDB: switch (regop) {
863         case 0: mnem = "fild_s"; break;
864         case 1: mnem = "fisttp_s"; break;
865         case 2: mnem = "fist_s"; break;
866         case 3: mnem = "fistp_s"; break;
867         default: UnimplementedInstruction();
868       }
869       break;
870 
871     case 0xDD: switch (regop) {
872         case 0: mnem = "fld_d"; break;
873         case 3: mnem = "fstp_d"; break;
874         default: UnimplementedInstruction();
875       }
876       break;
877 
878     case 0xDF: switch (regop) {
879         case 5: mnem = "fild_d"; break;
880         case 7: mnem = "fistp_d"; break;
881         default: UnimplementedInstruction();
882       }
883       break;
884 
885     default: UnimplementedInstruction();
886   }
887   AppendToBuffer("%s ", mnem);
888   int count = PrintRightOperand(modrm_start);
889   return count + 1;
890 }
891 
RegisterFPUInstruction(int escape_opcode,byte modrm_byte)892 int DisassemblerX64::RegisterFPUInstruction(int escape_opcode,
893                                              byte modrm_byte) {
894   bool has_register = false;  // Is the FPU register encoded in modrm_byte?
895   const char* mnem = "?";
896 
897   switch (escape_opcode) {
898     case 0xD8:
899       UnimplementedInstruction();
900       break;
901 
902     case 0xD9:
903       switch (modrm_byte & 0xF8) {
904         case 0xC0:
905           mnem = "fld";
906           has_register = true;
907           break;
908         case 0xC8:
909           mnem = "fxch";
910           has_register = true;
911           break;
912         default:
913           switch (modrm_byte) {
914             case 0xE0: mnem = "fchs"; break;
915             case 0xE1: mnem = "fabs"; break;
916             case 0xE3: mnem = "fninit"; break;
917             case 0xE4: mnem = "ftst"; break;
918             case 0xE8: mnem = "fld1"; break;
919             case 0xEB: mnem = "fldpi"; break;
920             case 0xED: mnem = "fldln2"; break;
921             case 0xEE: mnem = "fldz"; break;
922             case 0xF0: mnem = "f2xm1"; break;
923             case 0xF1: mnem = "fyl2x"; break;
924             case 0xF2: mnem = "fptan"; break;
925             case 0xF5: mnem = "fprem1"; break;
926             case 0xF7: mnem = "fincstp"; break;
927             case 0xF8: mnem = "fprem"; break;
928             case 0xFD: mnem = "fscale"; break;
929             case 0xFE: mnem = "fsin"; break;
930             case 0xFF: mnem = "fcos"; break;
931             default: UnimplementedInstruction();
932           }
933       }
934       break;
935 
936     case 0xDA:
937       if (modrm_byte == 0xE9) {
938         mnem = "fucompp";
939       } else {
940         UnimplementedInstruction();
941       }
942       break;
943 
944     case 0xDB:
945       if ((modrm_byte & 0xF8) == 0xE8) {
946         mnem = "fucomi";
947         has_register = true;
948       } else if (modrm_byte  == 0xE2) {
949         mnem = "fclex";
950       } else {
951         UnimplementedInstruction();
952       }
953       break;
954 
955     case 0xDC:
956       has_register = true;
957       switch (modrm_byte & 0xF8) {
958         case 0xC0: mnem = "fadd"; break;
959         case 0xE8: mnem = "fsub"; break;
960         case 0xC8: mnem = "fmul"; break;
961         case 0xF8: mnem = "fdiv"; break;
962         default: UnimplementedInstruction();
963       }
964       break;
965 
966     case 0xDD:
967       has_register = true;
968       switch (modrm_byte & 0xF8) {
969         case 0xC0: mnem = "ffree"; break;
970         case 0xD8: mnem = "fstp"; break;
971         default: UnimplementedInstruction();
972       }
973       break;
974 
975     case 0xDE:
976       if (modrm_byte  == 0xD9) {
977         mnem = "fcompp";
978       } else {
979         has_register = true;
980         switch (modrm_byte & 0xF8) {
981           case 0xC0: mnem = "faddp"; break;
982           case 0xE8: mnem = "fsubp"; break;
983           case 0xC8: mnem = "fmulp"; break;
984           case 0xF8: mnem = "fdivp"; break;
985           default: UnimplementedInstruction();
986         }
987       }
988       break;
989 
990     case 0xDF:
991       if (modrm_byte == 0xE0) {
992         mnem = "fnstsw_ax";
993       } else if ((modrm_byte & 0xF8) == 0xE8) {
994         mnem = "fucomip";
995         has_register = true;
996       }
997       break;
998 
999     default: UnimplementedInstruction();
1000   }
1001 
1002   if (has_register) {
1003     AppendToBuffer("%s st%d", mnem, modrm_byte & 0x7);
1004   } else {
1005     AppendToBuffer("%s", mnem);
1006   }
1007   return 2;
1008 }
1009 
1010 
1011 
1012 // Handle all two-byte opcodes, which start with 0x0F.
1013 // These instructions may be affected by an 0x66, 0xF2, or 0xF3 prefix.
1014 // We do not use any three-byte opcodes, which start with 0x0F38 or 0x0F3A.
TwoByteOpcodeInstruction(byte * data)1015 int DisassemblerX64::TwoByteOpcodeInstruction(byte* data) {
1016   byte opcode = *(data + 1);
1017   byte* current = data + 2;
1018   // At return, "current" points to the start of the next instruction.
1019   const char* mnemonic = TwoByteMnemonic(opcode);
1020   if (operand_size_ == 0x66) {
1021     // 0x66 0x0F prefix.
1022     int mod, regop, rm;
1023     if (opcode == 0x3A) {
1024       byte third_byte = *current;
1025       current = data + 3;
1026       if (third_byte == 0x17) {
1027         get_modrm(*current, &mod, &regop, &rm);
1028         AppendToBuffer("extractps ");  // reg/m32, xmm, imm8
1029         current += PrintRightOperand(current);
1030         AppendToBuffer(", %s, %d", NameOfCPURegister(regop), (*current) & 3);
1031         current += 1;
1032       } else if (third_byte == 0x0b) {
1033         get_modrm(*current, &mod, &regop, &rm);
1034          // roundsd xmm, xmm/m64, imm8
1035         AppendToBuffer("roundsd %s, ", NameOfCPURegister(regop));
1036         current += PrintRightOperand(current);
1037         AppendToBuffer(", %d", (*current) & 3);
1038         current += 1;
1039       } else {
1040         UnimplementedInstruction();
1041       }
1042     } else {
1043       get_modrm(*current, &mod, &regop, &rm);
1044       if (opcode == 0x1f) {
1045         current++;
1046         if (rm == 4) {  // SIB byte present.
1047           current++;
1048         }
1049         if (mod == 1) {  // Byte displacement.
1050           current += 1;
1051         } else if (mod == 2) {  // 32-bit displacement.
1052           current += 4;
1053         }  // else no immediate displacement.
1054         AppendToBuffer("nop");
1055       } else if (opcode == 0x28) {
1056         AppendToBuffer("movapd %s, ", NameOfXMMRegister(regop));
1057         current += PrintRightXMMOperand(current);
1058       } else if (opcode == 0x29) {
1059         AppendToBuffer("movapd ");
1060         current += PrintRightXMMOperand(current);
1061         AppendToBuffer(", %s", NameOfXMMRegister(regop));
1062       } else if (opcode == 0x6E) {
1063         AppendToBuffer("mov%c %s,",
1064                        rex_w() ? 'q' : 'd',
1065                        NameOfXMMRegister(regop));
1066         current += PrintRightOperand(current);
1067       } else if (opcode == 0x6F) {
1068         AppendToBuffer("movdqa %s,",
1069                        NameOfXMMRegister(regop));
1070         current += PrintRightXMMOperand(current);
1071       } else if (opcode == 0x7E) {
1072         AppendToBuffer("mov%c ",
1073                        rex_w() ? 'q' : 'd');
1074         current += PrintRightOperand(current);
1075         AppendToBuffer(", %s", NameOfXMMRegister(regop));
1076       } else if (opcode == 0x7F) {
1077         AppendToBuffer("movdqa ");
1078         current += PrintRightXMMOperand(current);
1079         AppendToBuffer(", %s", NameOfXMMRegister(regop));
1080       } else if (opcode == 0xD6) {
1081         AppendToBuffer("movq ");
1082         current += PrintRightXMMOperand(current);
1083         AppendToBuffer(", %s", NameOfXMMRegister(regop));
1084       } else if (opcode == 0x50) {
1085         AppendToBuffer("movmskpd %s,", NameOfCPURegister(regop));
1086         current += PrintRightXMMOperand(current);
1087       } else {
1088         const char* mnemonic = "?";
1089         if (opcode == 0x54) {
1090           mnemonic = "andpd";
1091         } else  if (opcode == 0x56) {
1092           mnemonic = "orpd";
1093         } else  if (opcode == 0x57) {
1094           mnemonic = "xorpd";
1095         } else if (opcode == 0x2E) {
1096           mnemonic = "ucomisd";
1097         } else if (opcode == 0x2F) {
1098           mnemonic = "comisd";
1099         } else {
1100           UnimplementedInstruction();
1101         }
1102         AppendToBuffer("%s %s,", mnemonic, NameOfXMMRegister(regop));
1103         current += PrintRightXMMOperand(current);
1104       }
1105     }
1106   } else if (group_1_prefix_ == 0xF2) {
1107     // Beginning of instructions with prefix 0xF2.
1108 
1109     if (opcode == 0x11 || opcode == 0x10) {
1110       // MOVSD: Move scalar double-precision fp to/from/between XMM registers.
1111       AppendToBuffer("movsd ");
1112       int mod, regop, rm;
1113       get_modrm(*current, &mod, &regop, &rm);
1114       if (opcode == 0x11) {
1115         current += PrintRightXMMOperand(current);
1116         AppendToBuffer(",%s", NameOfXMMRegister(regop));
1117       } else {
1118         AppendToBuffer("%s,", NameOfXMMRegister(regop));
1119         current += PrintRightXMMOperand(current);
1120       }
1121     } else if (opcode == 0x2A) {
1122       // CVTSI2SD: integer to XMM double conversion.
1123       int mod, regop, rm;
1124       get_modrm(*current, &mod, &regop, &rm);
1125       AppendToBuffer("%sd %s,", mnemonic, NameOfXMMRegister(regop));
1126       current += PrintRightOperand(current);
1127     } else if (opcode == 0x2C) {
1128       // CVTTSD2SI:
1129       // Convert with truncation scalar double-precision FP to integer.
1130       int mod, regop, rm;
1131       get_modrm(*current, &mod, &regop, &rm);
1132       AppendToBuffer("cvttsd2si%c %s,",
1133           operand_size_code(), NameOfCPURegister(regop));
1134       current += PrintRightXMMOperand(current);
1135     } else if (opcode == 0x2D) {
1136       // CVTSD2SI: Convert scalar double-precision FP to integer.
1137       int mod, regop, rm;
1138       get_modrm(*current, &mod, &regop, &rm);
1139       AppendToBuffer("cvtsd2si%c %s,",
1140           operand_size_code(), NameOfCPURegister(regop));
1141       current += PrintRightXMMOperand(current);
1142     } else if ((opcode & 0xF8) == 0x58 || opcode == 0x51) {
1143       // XMM arithmetic. Mnemonic was retrieved at the start of this function.
1144       int mod, regop, rm;
1145       get_modrm(*current, &mod, &regop, &rm);
1146       AppendToBuffer("%s %s,", mnemonic, NameOfXMMRegister(regop));
1147       current += PrintRightXMMOperand(current);
1148     } else {
1149       UnimplementedInstruction();
1150     }
1151   } else if (group_1_prefix_ == 0xF3) {
1152     // Instructions with prefix 0xF3.
1153     if (opcode == 0x11 || opcode == 0x10) {
1154       // MOVSS: Move scalar double-precision fp to/from/between XMM registers.
1155       AppendToBuffer("movss ");
1156       int mod, regop, rm;
1157       get_modrm(*current, &mod, &regop, &rm);
1158       if (opcode == 0x11) {
1159         current += PrintRightOperand(current);
1160         AppendToBuffer(",%s", NameOfXMMRegister(regop));
1161       } else {
1162         AppendToBuffer("%s,", NameOfXMMRegister(regop));
1163         current += PrintRightOperand(current);
1164       }
1165     } else if (opcode == 0x2A) {
1166       // CVTSI2SS: integer to XMM single conversion.
1167       int mod, regop, rm;
1168       get_modrm(*current, &mod, &regop, &rm);
1169       AppendToBuffer("%ss %s,", mnemonic, NameOfXMMRegister(regop));
1170       current += PrintRightOperand(current);
1171     } else if (opcode == 0x2C) {
1172       // CVTTSS2SI:
1173       // Convert with truncation scalar single-precision FP to dword integer.
1174       int mod, regop, rm;
1175       get_modrm(*current, &mod, &regop, &rm);
1176       AppendToBuffer("cvttss2si%c %s,",
1177           operand_size_code(), NameOfCPURegister(regop));
1178       current += PrintRightXMMOperand(current);
1179     } else if (opcode == 0x5A) {
1180       // CVTSS2SD:
1181       // Convert scalar single-precision FP to scalar double-precision FP.
1182       int mod, regop, rm;
1183       get_modrm(*current, &mod, &regop, &rm);
1184       AppendToBuffer("cvtss2sd %s,", NameOfXMMRegister(regop));
1185       current += PrintRightXMMOperand(current);
1186     } else if (opcode == 0x7E) {
1187       int mod, regop, rm;
1188       get_modrm(*current, &mod, &regop, &rm);
1189       AppendToBuffer("movq %s, ", NameOfXMMRegister(regop));
1190       current += PrintRightXMMOperand(current);
1191     } else {
1192       UnimplementedInstruction();
1193     }
1194   } else if (opcode == 0x1F) {
1195     // NOP
1196     int mod, regop, rm;
1197     get_modrm(*current, &mod, &regop, &rm);
1198     current++;
1199     if (rm == 4) {  // SIB byte present.
1200       current++;
1201     }
1202     if (mod == 1) {  // Byte displacement.
1203       current += 1;
1204     } else if (mod == 2) {  // 32-bit displacement.
1205       current += 4;
1206     }  // else no immediate displacement.
1207     AppendToBuffer("nop");
1208 
1209   } else if (opcode == 0x28) {
1210     // movaps xmm, xmm/m128
1211     int mod, regop, rm;
1212     get_modrm(*current, &mod, &regop, &rm);
1213     AppendToBuffer("movaps %s, ", NameOfXMMRegister(regop));
1214     current += PrintRightXMMOperand(current);
1215 
1216   } else if (opcode == 0x29) {
1217     // movaps xmm/m128, xmm
1218     int mod, regop, rm;
1219     get_modrm(*current, &mod, &regop, &rm);
1220     AppendToBuffer("movaps ");
1221     current += PrintRightXMMOperand(current);
1222     AppendToBuffer(", %s", NameOfXMMRegister(regop));
1223 
1224   } else if (opcode == 0xA2 || opcode == 0x31) {
1225     // RDTSC or CPUID
1226     AppendToBuffer("%s", mnemonic);
1227 
1228   } else if ((opcode & 0xF0) == 0x40) {
1229     // CMOVcc: conditional move.
1230     int condition = opcode & 0x0F;
1231     const InstructionDesc& idesc = cmov_instructions[condition];
1232     byte_size_operand_ = idesc.byte_size_operation;
1233     current += PrintOperands(idesc.mnem, idesc.op_order_, current);
1234 
1235   } else if (opcode == 0x57) {
1236     // xorps xmm, xmm/m128
1237     int mod, regop, rm;
1238     get_modrm(*current, &mod, &regop, &rm);
1239     AppendToBuffer("xorps %s, ", NameOfXMMRegister(regop));
1240     current += PrintRightXMMOperand(current);
1241 
1242   } else if ((opcode & 0xF0) == 0x80) {
1243     // Jcc: Conditional jump (branch).
1244     current = data + JumpConditional(data);
1245 
1246   } else if (opcode == 0xBE || opcode == 0xBF || opcode == 0xB6 ||
1247              opcode == 0xB7 || opcode == 0xAF) {
1248     // Size-extending moves, IMUL.
1249     current += PrintOperands(mnemonic, REG_OPER_OP_ORDER, current);
1250 
1251   } else if ((opcode & 0xF0) == 0x90) {
1252     // SETcc: Set byte on condition. Needs pointer to beginning of instruction.
1253     current = data + SetCC(data);
1254 
1255   } else if (opcode == 0xAB || opcode == 0xA5 || opcode == 0xAD) {
1256     // SHLD, SHRD (double-precision shift), BTS (bit set).
1257     AppendToBuffer("%s ", mnemonic);
1258     int mod, regop, rm;
1259     get_modrm(*current, &mod, &regop, &rm);
1260     current += PrintRightOperand(current);
1261     if (opcode == 0xAB) {
1262       AppendToBuffer(",%s", NameOfCPURegister(regop));
1263     } else {
1264       AppendToBuffer(",%s,cl", NameOfCPURegister(regop));
1265     }
1266   } else {
1267     UnimplementedInstruction();
1268   }
1269   return static_cast<int>(current - data);
1270 }
1271 
1272 
1273 // Mnemonics for two-byte opcode instructions starting with 0x0F.
1274 // The argument is the second byte of the two-byte opcode.
1275 // Returns NULL if the instruction is not handled here.
TwoByteMnemonic(byte opcode)1276 const char* DisassemblerX64::TwoByteMnemonic(byte opcode) {
1277   switch (opcode) {
1278     case 0x1F:
1279       return "nop";
1280     case 0x2A:  // F2/F3 prefix.
1281       return "cvtsi2s";
1282     case 0x31:
1283       return "rdtsc";
1284     case 0x51:  // F2 prefix.
1285       return "sqrtsd";
1286     case 0x58:  // F2 prefix.
1287       return "addsd";
1288     case 0x59:  // F2 prefix.
1289       return "mulsd";
1290     case 0x5C:  // F2 prefix.
1291       return "subsd";
1292     case 0x5E:  // F2 prefix.
1293       return "divsd";
1294     case 0xA2:
1295       return "cpuid";
1296     case 0xA5:
1297       return "shld";
1298     case 0xAB:
1299       return "bts";
1300     case 0xAD:
1301       return "shrd";
1302     case 0xAF:
1303       return "imul";
1304     case 0xB6:
1305       return "movzxb";
1306     case 0xB7:
1307       return "movzxw";
1308     case 0xBE:
1309       return "movsxb";
1310     case 0xBF:
1311       return "movsxw";
1312     default:
1313       return NULL;
1314   }
1315 }
1316 
1317 
1318 // Disassembles the instruction at instr, and writes it into out_buffer.
InstructionDecode(v8::internal::Vector<char> out_buffer,byte * instr)1319 int DisassemblerX64::InstructionDecode(v8::internal::Vector<char> out_buffer,
1320                                        byte* instr) {
1321   tmp_buffer_pos_ = 0;  // starting to write as position 0
1322   byte* data = instr;
1323   bool processed = true;  // Will be set to false if the current instruction
1324                           // is not in 'instructions' table.
1325   byte current;
1326 
1327   // Scan for prefixes.
1328   while (true) {
1329     current = *data;
1330     if (current == OPERAND_SIZE_OVERRIDE_PREFIX) {  // Group 3 prefix.
1331       operand_size_ = current;
1332     } else if ((current & 0xF0) == 0x40) {  // REX prefix.
1333       setRex(current);
1334       if (rex_w()) AppendToBuffer("REX.W ");
1335     } else if ((current & 0xFE) == 0xF2) {  // Group 1 prefix (0xF2 or 0xF3).
1336       group_1_prefix_ = current;
1337     } else {  // Not a prefix - an opcode.
1338       break;
1339     }
1340     data++;
1341   }
1342 
1343   const InstructionDesc& idesc = instruction_table.Get().Get(current);
1344   byte_size_operand_ = idesc.byte_size_operation;
1345   switch (idesc.type) {
1346     case ZERO_OPERANDS_INSTR:
1347       if (current >= 0xA4 && current <= 0xA7) {
1348         // String move or compare operations.
1349         if (group_1_prefix_ == REP_PREFIX) {
1350           // REP.
1351           AppendToBuffer("rep ");
1352         }
1353         if (rex_w()) AppendToBuffer("REX.W ");
1354         AppendToBuffer("%s%c", idesc.mnem, operand_size_code());
1355       } else {
1356         AppendToBuffer("%s", idesc.mnem, operand_size_code());
1357       }
1358       data++;
1359       break;
1360 
1361     case TWO_OPERANDS_INSTR:
1362       data++;
1363       data += PrintOperands(idesc.mnem, idesc.op_order_, data);
1364       break;
1365 
1366     case JUMP_CONDITIONAL_SHORT_INSTR:
1367       data += JumpConditionalShort(data);
1368       break;
1369 
1370     case REGISTER_INSTR:
1371       AppendToBuffer("%s%c %s",
1372                      idesc.mnem,
1373                      operand_size_code(),
1374                      NameOfCPURegister(base_reg(current & 0x07)));
1375       data++;
1376       break;
1377     case PUSHPOP_INSTR:
1378       AppendToBuffer("%s %s",
1379                      idesc.mnem,
1380                      NameOfCPURegister(base_reg(current & 0x07)));
1381       data++;
1382       break;
1383     case MOVE_REG_INSTR: {
1384       byte* addr = NULL;
1385       switch (operand_size()) {
1386         case WORD_SIZE:
1387           addr = reinterpret_cast<byte*>(*reinterpret_cast<int16_t*>(data + 1));
1388           data += 3;
1389           break;
1390         case DOUBLEWORD_SIZE:
1391           addr = reinterpret_cast<byte*>(*reinterpret_cast<int32_t*>(data + 1));
1392           data += 5;
1393           break;
1394         case QUADWORD_SIZE:
1395           addr = reinterpret_cast<byte*>(*reinterpret_cast<int64_t*>(data + 1));
1396           data += 9;
1397           break;
1398         default:
1399           UNREACHABLE();
1400       }
1401       AppendToBuffer("mov%c %s,%s",
1402                      operand_size_code(),
1403                      NameOfCPURegister(base_reg(current & 0x07)),
1404                      NameOfAddress(addr));
1405       break;
1406     }
1407 
1408     case CALL_JUMP_INSTR: {
1409       byte* addr = data + *reinterpret_cast<int32_t*>(data + 1) + 5;
1410       AppendToBuffer("%s %s", idesc.mnem, NameOfAddress(addr));
1411       data += 5;
1412       break;
1413     }
1414 
1415     case SHORT_IMMEDIATE_INSTR: {
1416       byte* addr =
1417           reinterpret_cast<byte*>(*reinterpret_cast<int32_t*>(data + 1));
1418       AppendToBuffer("%s rax, %s", idesc.mnem, NameOfAddress(addr));
1419       data += 5;
1420       break;
1421     }
1422 
1423     case NO_INSTR:
1424       processed = false;
1425       break;
1426 
1427     default:
1428       UNIMPLEMENTED();  // This type is not implemented.
1429   }
1430 
1431   // The first byte didn't match any of the simple opcodes, so we
1432   // need to do special processing on it.
1433   if (!processed) {
1434     switch (*data) {
1435       case 0xC2:
1436         AppendToBuffer("ret 0x%x", *reinterpret_cast<uint16_t*>(data + 1));
1437         data += 3;
1438         break;
1439 
1440       case 0x69:  // fall through
1441       case 0x6B: {
1442         int mod, regop, rm;
1443         get_modrm(*(data + 1), &mod, &regop, &rm);
1444         int32_t imm = *data == 0x6B ? *(data + 2)
1445             : *reinterpret_cast<int32_t*>(data + 2);
1446         AppendToBuffer("imul%c %s,%s,0x%x",
1447                        operand_size_code(),
1448                        NameOfCPURegister(regop),
1449                        NameOfCPURegister(rm), imm);
1450         data += 2 + (*data == 0x6B ? 1 : 4);
1451         break;
1452       }
1453 
1454       case 0x81:  // fall through
1455       case 0x83:  // 0x81 with sign extension bit set
1456         data += PrintImmediateOp(data);
1457         break;
1458 
1459       case 0x0F:
1460         data += TwoByteOpcodeInstruction(data);
1461         break;
1462 
1463       case 0x8F: {
1464         data++;
1465         int mod, regop, rm;
1466         get_modrm(*data, &mod, &regop, &rm);
1467         if (regop == 0) {
1468           AppendToBuffer("pop ");
1469           data += PrintRightOperand(data);
1470         }
1471       }
1472         break;
1473 
1474       case 0xFF: {
1475         data++;
1476         int mod, regop, rm;
1477         get_modrm(*data, &mod, &regop, &rm);
1478         const char* mnem = NULL;
1479         switch (regop) {
1480           case 0:
1481             mnem = "inc";
1482             break;
1483           case 1:
1484             mnem = "dec";
1485             break;
1486           case 2:
1487             mnem = "call";
1488             break;
1489           case 4:
1490             mnem = "jmp";
1491             break;
1492           case 6:
1493             mnem = "push";
1494             break;
1495           default:
1496             mnem = "???";
1497         }
1498         AppendToBuffer(((regop <= 1) ? "%s%c " : "%s "),
1499                        mnem,
1500                        operand_size_code());
1501         data += PrintRightOperand(data);
1502       }
1503         break;
1504 
1505       case 0xC7:  // imm32, fall through
1506       case 0xC6:  // imm8
1507       {
1508         bool is_byte = *data == 0xC6;
1509         data++;
1510         if (is_byte) {
1511           AppendToBuffer("movb ");
1512           data += PrintRightByteOperand(data);
1513           int32_t imm = *data;
1514           AppendToBuffer(",0x%x", imm);
1515           data++;
1516         } else {
1517           AppendToBuffer("mov%c ", operand_size_code());
1518           data += PrintRightOperand(data);
1519           int32_t imm = *reinterpret_cast<int32_t*>(data);
1520           AppendToBuffer(",0x%x", imm);
1521           data += 4;
1522         }
1523       }
1524         break;
1525 
1526       case 0x80: {
1527         data++;
1528         AppendToBuffer("cmpb ");
1529         data += PrintRightByteOperand(data);
1530         int32_t imm = *data;
1531         AppendToBuffer(",0x%x", imm);
1532         data++;
1533       }
1534         break;
1535 
1536       case 0x88:  // 8bit, fall through
1537       case 0x89:  // 32bit
1538       {
1539         bool is_byte = *data == 0x88;
1540         int mod, regop, rm;
1541         data++;
1542         get_modrm(*data, &mod, &regop, &rm);
1543         if (is_byte) {
1544           AppendToBuffer("movb ");
1545           data += PrintRightByteOperand(data);
1546           AppendToBuffer(",%s", NameOfByteCPURegister(regop));
1547         } else {
1548           AppendToBuffer("mov%c ", operand_size_code());
1549           data += PrintRightOperand(data);
1550           AppendToBuffer(",%s", NameOfCPURegister(regop));
1551         }
1552       }
1553         break;
1554 
1555       case 0x90:
1556       case 0x91:
1557       case 0x92:
1558       case 0x93:
1559       case 0x94:
1560       case 0x95:
1561       case 0x96:
1562       case 0x97: {
1563         int reg = (*data & 0x7) | (rex_b() ? 8 : 0);
1564         if (reg == 0) {
1565           AppendToBuffer("nop");  // Common name for xchg rax,rax.
1566         } else {
1567           AppendToBuffer("xchg%c rax, %s",
1568                          operand_size_code(),
1569                          NameOfCPURegister(reg));
1570         }
1571         data++;
1572       }
1573         break;
1574       case 0xB0:
1575       case 0xB1:
1576       case 0xB2:
1577       case 0xB3:
1578       case 0xB4:
1579       case 0xB5:
1580       case 0xB6:
1581       case 0xB7:
1582       case 0xB8:
1583       case 0xB9:
1584       case 0xBA:
1585       case 0xBB:
1586       case 0xBC:
1587       case 0xBD:
1588       case 0xBE:
1589       case 0xBF: {
1590         // mov reg8,imm8 or mov reg32,imm32
1591         byte opcode = *data;
1592         data++;
1593         bool is_32bit = (opcode >= 0xB8);
1594         int reg = (opcode & 0x7) | (rex_b() ? 8 : 0);
1595         if (is_32bit) {
1596           AppendToBuffer("mov%c %s, ",
1597                          operand_size_code(),
1598                          NameOfCPURegister(reg));
1599           data += PrintImmediate(data, DOUBLEWORD_SIZE);
1600         } else {
1601           AppendToBuffer("movb %s, ",
1602                          NameOfByteCPURegister(reg));
1603           data += PrintImmediate(data, BYTE_SIZE);
1604         }
1605         break;
1606       }
1607       case 0xFE: {
1608         data++;
1609         int mod, regop, rm;
1610         get_modrm(*data, &mod, &regop, &rm);
1611         if (regop == 1) {
1612           AppendToBuffer("decb ");
1613           data += PrintRightByteOperand(data);
1614         } else {
1615           UnimplementedInstruction();
1616         }
1617         break;
1618       }
1619       case 0x68:
1620         AppendToBuffer("push 0x%x", *reinterpret_cast<int32_t*>(data + 1));
1621         data += 5;
1622         break;
1623 
1624       case 0x6A:
1625         AppendToBuffer("push 0x%x", *reinterpret_cast<int8_t*>(data + 1));
1626         data += 2;
1627         break;
1628 
1629       case 0xA1:  // Fall through.
1630       case 0xA3:
1631         switch (operand_size()) {
1632           case DOUBLEWORD_SIZE: {
1633             const char* memory_location = NameOfAddress(
1634                 reinterpret_cast<byte*>(
1635                     *reinterpret_cast<int32_t*>(data + 1)));
1636             if (*data == 0xA1) {  // Opcode 0xA1
1637               AppendToBuffer("movzxlq rax,(%s)", memory_location);
1638             } else {  // Opcode 0xA3
1639               AppendToBuffer("movzxlq (%s),rax", memory_location);
1640             }
1641             data += 5;
1642             break;
1643           }
1644           case QUADWORD_SIZE: {
1645             // New x64 instruction mov rax,(imm_64).
1646             const char* memory_location = NameOfAddress(
1647                 *reinterpret_cast<byte**>(data + 1));
1648             if (*data == 0xA1) {  // Opcode 0xA1
1649               AppendToBuffer("movq rax,(%s)", memory_location);
1650             } else {  // Opcode 0xA3
1651               AppendToBuffer("movq (%s),rax", memory_location);
1652             }
1653             data += 9;
1654             break;
1655           }
1656           default:
1657             UnimplementedInstruction();
1658             data += 2;
1659         }
1660         break;
1661 
1662       case 0xA8:
1663         AppendToBuffer("test al,0x%x", *reinterpret_cast<uint8_t*>(data + 1));
1664         data += 2;
1665         break;
1666 
1667       case 0xA9: {
1668         int64_t value = 0;
1669         switch (operand_size()) {
1670           case WORD_SIZE:
1671             value = *reinterpret_cast<uint16_t*>(data + 1);
1672             data += 3;
1673             break;
1674           case DOUBLEWORD_SIZE:
1675             value = *reinterpret_cast<uint32_t*>(data + 1);
1676             data += 5;
1677             break;
1678           case QUADWORD_SIZE:
1679             value = *reinterpret_cast<int32_t*>(data + 1);
1680             data += 5;
1681             break;
1682           default:
1683             UNREACHABLE();
1684         }
1685         AppendToBuffer("test%c rax,0x%"V8_PTR_PREFIX"x",
1686                        operand_size_code(),
1687                        value);
1688         break;
1689       }
1690       case 0xD1:  // fall through
1691       case 0xD3:  // fall through
1692       case 0xC1:
1693         data += ShiftInstruction(data);
1694         break;
1695       case 0xD0:  // fall through
1696       case 0xD2:  // fall through
1697       case 0xC0:
1698         byte_size_operand_ = true;
1699         data += ShiftInstruction(data);
1700         break;
1701 
1702       case 0xD9:  // fall through
1703       case 0xDA:  // fall through
1704       case 0xDB:  // fall through
1705       case 0xDC:  // fall through
1706       case 0xDD:  // fall through
1707       case 0xDE:  // fall through
1708       case 0xDF:
1709         data += FPUInstruction(data);
1710         break;
1711 
1712       case 0xEB:
1713         data += JumpShort(data);
1714         break;
1715 
1716       case 0xF6:
1717         byte_size_operand_ = true;  // fall through
1718       case 0xF7:
1719         data += F6F7Instruction(data);
1720         break;
1721 
1722       default:
1723         UnimplementedInstruction();
1724         data += 1;
1725     }
1726   }  // !processed
1727 
1728   if (tmp_buffer_pos_ < sizeof tmp_buffer_) {
1729     tmp_buffer_[tmp_buffer_pos_] = '\0';
1730   }
1731 
1732   int instr_len = static_cast<int>(data - instr);
1733   ASSERT(instr_len > 0);  // Ensure progress.
1734 
1735   int outp = 0;
1736   // Instruction bytes.
1737   for (byte* bp = instr; bp < data; bp++) {
1738     outp += v8::internal::OS::SNPrintF(out_buffer + outp, "%02x", *bp);
1739   }
1740   for (int i = 6 - instr_len; i >= 0; i--) {
1741     outp += v8::internal::OS::SNPrintF(out_buffer + outp, "  ");
1742   }
1743 
1744   outp += v8::internal::OS::SNPrintF(out_buffer + outp, " %s",
1745                                      tmp_buffer_.start());
1746   return instr_len;
1747 }
1748 
1749 //------------------------------------------------------------------------------
1750 
1751 
1752 static const char* cpu_regs[16] = {
1753   "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi",
1754   "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"
1755 };
1756 
1757 
1758 static const char* byte_cpu_regs[16] = {
1759   "al", "cl", "dl", "bl", "spl", "bpl", "sil", "dil",
1760   "r8l", "r9l", "r10l", "r11l", "r12l", "r13l", "r14l", "r15l"
1761 };
1762 
1763 
1764 static const char* xmm_regs[16] = {
1765   "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7",
1766   "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15"
1767 };
1768 
1769 
NameOfAddress(byte * addr) const1770 const char* NameConverter::NameOfAddress(byte* addr) const {
1771   v8::internal::OS::SNPrintF(tmp_buffer_, "%p", addr);
1772   return tmp_buffer_.start();
1773 }
1774 
1775 
NameOfConstant(byte * addr) const1776 const char* NameConverter::NameOfConstant(byte* addr) const {
1777   return NameOfAddress(addr);
1778 }
1779 
1780 
NameOfCPURegister(int reg) const1781 const char* NameConverter::NameOfCPURegister(int reg) const {
1782   if (0 <= reg && reg < 16)
1783     return cpu_regs[reg];
1784   return "noreg";
1785 }
1786 
1787 
NameOfByteCPURegister(int reg) const1788 const char* NameConverter::NameOfByteCPURegister(int reg) const {
1789   if (0 <= reg && reg < 16)
1790     return byte_cpu_regs[reg];
1791   return "noreg";
1792 }
1793 
1794 
NameOfXMMRegister(int reg) const1795 const char* NameConverter::NameOfXMMRegister(int reg) const {
1796   if (0 <= reg && reg < 16)
1797     return xmm_regs[reg];
1798   return "noxmmreg";
1799 }
1800 
1801 
NameInCode(byte * addr) const1802 const char* NameConverter::NameInCode(byte* addr) const {
1803   // X64 does not embed debug strings at the moment.
1804   UNREACHABLE();
1805   return "";
1806 }
1807 
1808 //------------------------------------------------------------------------------
1809 
Disassembler(const NameConverter & converter)1810 Disassembler::Disassembler(const NameConverter& converter)
1811     : converter_(converter) { }
1812 
~Disassembler()1813 Disassembler::~Disassembler() { }
1814 
1815 
InstructionDecode(v8::internal::Vector<char> buffer,byte * instruction)1816 int Disassembler::InstructionDecode(v8::internal::Vector<char> buffer,
1817                                     byte* instruction) {
1818   DisassemblerX64 d(converter_, CONTINUE_ON_UNIMPLEMENTED_OPCODE);
1819   return d.InstructionDecode(buffer, instruction);
1820 }
1821 
1822 
1823 // The X64 assembler does not use constant pools.
ConstantPoolSizeAt(byte * instruction)1824 int Disassembler::ConstantPoolSizeAt(byte* instruction) {
1825   return -1;
1826 }
1827 
1828 
Disassemble(FILE * f,byte * begin,byte * end)1829 void Disassembler::Disassemble(FILE* f, byte* begin, byte* end) {
1830   NameConverter converter;
1831   Disassembler d(converter);
1832   for (byte* pc = begin; pc < end;) {
1833     v8::internal::EmbeddedVector<char, 128> buffer;
1834     buffer[0] = '\0';
1835     byte* prev_pc = pc;
1836     pc += d.InstructionDecode(buffer, pc);
1837     fprintf(f, "%p", prev_pc);
1838     fprintf(f, "    ");
1839 
1840     for (byte* bp = prev_pc; bp < pc; bp++) {
1841       fprintf(f, "%02x", *bp);
1842     }
1843     for (int i = 6 - static_cast<int>(pc - prev_pc); i >= 0; i--) {
1844       fprintf(f, "  ");
1845     }
1846     fprintf(f, "  %s\n", buffer.start());
1847   }
1848 }
1849 
1850 }  // namespace disasm
1851 
1852 #endif  // V8_TARGET_ARCH_X64
1853