1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "disassembler_x86.h"
18
19#include <iostream>
20
21#include "base/logging.h"
22#include "base/stringprintf.h"
23#include "thread.h"
24#include <inttypes.h>
25
26namespace art {
27namespace x86 {
28
29size_t DisassemblerX86::Dump(std::ostream& os, const uint8_t* begin) {
30  return DumpInstruction(os, begin);
31}
32
33void DisassemblerX86::Dump(std::ostream& os, const uint8_t* begin, const uint8_t* end) {
34  size_t length = 0;
35  for (const uint8_t* cur = begin; cur < end; cur += length) {
36    length = DumpInstruction(os, cur);
37  }
38}
39
40static const char* gReg8Names[]  = {
41  "al", "cl", "dl", "bl", "ah", "ch", "dh", "bh"
42};
43static const char* gExtReg8Names[] = {
44  "al", "cl", "dl", "bl", "spl", "bpl", "sil", "dil",
45  "r8l", "r9l", "r10l", "r11l", "r12l", "r13l", "r14l", "r15l"
46};
47static const char* gReg16Names[] = {
48  "ax", "cx", "dx", "bx", "sp", "bp", "si", "di",
49  "r8w", "r9w", "r10w", "r11w", "r12w", "r13w", "r14w", "r15w"
50};
51static const char* gReg32Names[] = {
52  "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi",
53  "r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d"
54};
55static const char* gReg64Names[] = {
56  "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi",
57  "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"
58};
59
60// 64-bit opcode REX modifier.
61constexpr uint8_t REX_W = 0b1000;
62constexpr uint8_t REX_R = 0b0100;
63constexpr uint8_t REX_X = 0b0010;
64constexpr uint8_t REX_B = 0b0001;
65
66static void DumpReg0(std::ostream& os, uint8_t rex, size_t reg,
67                     bool byte_operand, uint8_t size_override) {
68  DCHECK_LT(reg, (rex == 0) ? 8u : 16u);
69  bool rex_w = (rex & REX_W) != 0;
70  if (byte_operand) {
71    os << ((rex == 0) ? gReg8Names[reg] : gExtReg8Names[reg]);
72  } else if (rex_w) {
73    os << gReg64Names[reg];
74  } else if (size_override == 0x66) {
75    os << gReg16Names[reg];
76  } else {
77    os << gReg32Names[reg];
78  }
79}
80
81enum RegFile { GPR, MMX, SSE };
82
83static void DumpAnyReg(std::ostream& os, uint8_t rex, size_t reg,
84                       bool byte_operand, uint8_t size_override, RegFile reg_file) {
85  if (reg_file == GPR) {
86    DumpReg0(os, rex, reg, byte_operand, size_override);
87  } else if (reg_file == SSE) {
88    os << "xmm" << reg;
89  } else {
90    os << "mm" << reg;
91  }
92}
93
94static void DumpReg(std::ostream& os, uint8_t rex, uint8_t reg,
95                    bool byte_operand, uint8_t size_override, RegFile reg_file) {
96  bool rex_r = (rex & REX_R) != 0;
97  size_t reg_num = rex_r ? (reg + 8) : reg;
98  DumpAnyReg(os, rex, reg_num, byte_operand, size_override, reg_file);
99}
100
101static void DumpRmReg(std::ostream& os, uint8_t rex, uint8_t reg,
102                      bool byte_operand, uint8_t size_override, RegFile reg_file) {
103  bool rex_b = (rex & REX_B) != 0;
104  size_t reg_num = rex_b ? (reg + 8) : reg;
105  DumpAnyReg(os, rex, reg_num, byte_operand, size_override, reg_file);
106}
107
108static void DumpAddrReg(std::ostream& os, uint8_t rex, uint8_t reg) {
109  if (rex != 0) {
110    os << gReg64Names[reg];
111  } else {
112    os << gReg32Names[reg];
113  }
114}
115
116static void DumpBaseReg(std::ostream& os, uint8_t rex, uint8_t reg) {
117  bool rex_b = (rex & REX_B) != 0;
118  size_t reg_num = rex_b ? (reg + 8) : reg;
119  DumpAddrReg(os, rex, reg_num);
120}
121
122static void DumpIndexReg(std::ostream& os, uint8_t rex, uint8_t reg) {
123  bool rex_x = (rex & REX_X) != 0;
124  uint8_t reg_num = rex_x ? (reg + 8) : reg;
125  DumpAddrReg(os, rex, reg_num);
126}
127
128static void DumpOpcodeReg(std::ostream& os, uint8_t rex, uint8_t reg,
129                          bool byte_operand, uint8_t size_override) {
130  bool rex_b = (rex & REX_B) != 0;
131  size_t reg_num = rex_b ? (reg + 8) : reg;
132  DumpReg0(os, rex, reg_num, byte_operand, size_override);
133}
134
135enum SegmentPrefix {
136  kCs = 0x2e,
137  kSs = 0x36,
138  kDs = 0x3e,
139  kEs = 0x26,
140  kFs = 0x64,
141  kGs = 0x65,
142};
143
144static void DumpSegmentOverride(std::ostream& os, uint8_t segment_prefix) {
145  switch (segment_prefix) {
146    case kCs: os << "cs:"; break;
147    case kSs: os << "ss:"; break;
148    case kDs: os << "ds:"; break;
149    case kEs: os << "es:"; break;
150    case kFs: os << "fs:"; break;
151    case kGs: os << "gs:"; break;
152    default: break;
153  }
154}
155
156size_t DisassemblerX86::DumpInstruction(std::ostream& os, const uint8_t* instr) {
157  const uint8_t* begin_instr = instr;
158  bool have_prefixes = true;
159  uint8_t prefix[4] = {0, 0, 0, 0};
160  const char** modrm_opcodes = NULL;
161  do {
162    switch (*instr) {
163        // Group 1 - lock and repeat prefixes:
164      case 0xF0:
165      case 0xF2:
166      case 0xF3:
167        prefix[0] = *instr;
168        break;
169        // Group 2 - segment override prefixes:
170      case kCs:
171      case kSs:
172      case kDs:
173      case kEs:
174      case kFs:
175      case kGs:
176        prefix[1] = *instr;
177        break;
178        // Group 3 - operand size override:
179      case 0x66:
180        prefix[2] = *instr;
181        break;
182        // Group 4 - address size override:
183      case 0x67:
184        prefix[3] = *instr;
185        break;
186      default:
187        have_prefixes = false;
188        break;
189    }
190    if (have_prefixes) {
191      instr++;
192    }
193  } while (have_prefixes);
194  uint8_t rex = (supports_rex_ && (*instr >= 0x40) && (*instr <= 0x4F)) ? *instr : 0;
195  if (rex != 0) {
196    instr++;
197  }
198  bool has_modrm = false;
199  bool reg_is_opcode = false;
200  size_t immediate_bytes = 0;
201  size_t branch_bytes = 0;
202  std::ostringstream opcode;
203  bool store = false;  // stores to memory (ie rm is on the left)
204  bool load = false;  // loads from memory (ie rm is on the right)
205  bool byte_operand = false;  // true when the opcode is dealing with byte operands
206  bool byte_second_operand = false;  // true when the source operand is a byte register but the target register isn't (ie movsxb/movzxb).
207  bool target_specific = false;  // register name depends on target (64 vs 32 bits).
208  bool ax = false;  // implicit use of ax
209  bool cx = false;  // implicit use of cx
210  bool reg_in_opcode = false;  // low 3-bits of opcode encode register parameter
211  bool no_ops = false;
212  RegFile src_reg_file = GPR;
213  RegFile dst_reg_file = GPR;
214  switch (*instr) {
215#define DISASSEMBLER_ENTRY(opname, \
216                     rm8_r8, rm32_r32, \
217                     r8_rm8, r32_rm32, \
218                     ax8_i8, ax32_i32) \
219  case rm8_r8:   opcode << #opname; store = true; has_modrm = true; byte_operand = true; break; \
220  case rm32_r32: opcode << #opname; store = true; has_modrm = true; break; \
221  case r8_rm8:   opcode << #opname; load = true; has_modrm = true; byte_operand = true; break; \
222  case r32_rm32: opcode << #opname; load = true; has_modrm = true; break; \
223  case ax8_i8:   opcode << #opname; ax = true; immediate_bytes = 1; byte_operand = true; break; \
224  case ax32_i32: opcode << #opname; ax = true; immediate_bytes = 4; break;
225
226DISASSEMBLER_ENTRY(add,
227  0x00 /* RegMem8/Reg8 */,     0x01 /* RegMem32/Reg32 */,
228  0x02 /* Reg8/RegMem8 */,     0x03 /* Reg32/RegMem32 */,
229  0x04 /* Rax8/imm8 opcode */, 0x05 /* Rax32/imm32 */)
230DISASSEMBLER_ENTRY(or,
231  0x08 /* RegMem8/Reg8 */,     0x09 /* RegMem32/Reg32 */,
232  0x0A /* Reg8/RegMem8 */,     0x0B /* Reg32/RegMem32 */,
233  0x0C /* Rax8/imm8 opcode */, 0x0D /* Rax32/imm32 */)
234DISASSEMBLER_ENTRY(adc,
235  0x10 /* RegMem8/Reg8 */,     0x11 /* RegMem32/Reg32 */,
236  0x12 /* Reg8/RegMem8 */,     0x13 /* Reg32/RegMem32 */,
237  0x14 /* Rax8/imm8 opcode */, 0x15 /* Rax32/imm32 */)
238DISASSEMBLER_ENTRY(sbb,
239  0x18 /* RegMem8/Reg8 */,     0x19 /* RegMem32/Reg32 */,
240  0x1A /* Reg8/RegMem8 */,     0x1B /* Reg32/RegMem32 */,
241  0x1C /* Rax8/imm8 opcode */, 0x1D /* Rax32/imm32 */)
242DISASSEMBLER_ENTRY(and,
243  0x20 /* RegMem8/Reg8 */,     0x21 /* RegMem32/Reg32 */,
244  0x22 /* Reg8/RegMem8 */,     0x23 /* Reg32/RegMem32 */,
245  0x24 /* Rax8/imm8 opcode */, 0x25 /* Rax32/imm32 */)
246DISASSEMBLER_ENTRY(sub,
247  0x28 /* RegMem8/Reg8 */,     0x29 /* RegMem32/Reg32 */,
248  0x2A /* Reg8/RegMem8 */,     0x2B /* Reg32/RegMem32 */,
249  0x2C /* Rax8/imm8 opcode */, 0x2D /* Rax32/imm32 */)
250DISASSEMBLER_ENTRY(xor,
251  0x30 /* RegMem8/Reg8 */,     0x31 /* RegMem32/Reg32 */,
252  0x32 /* Reg8/RegMem8 */,     0x33 /* Reg32/RegMem32 */,
253  0x34 /* Rax8/imm8 opcode */, 0x35 /* Rax32/imm32 */)
254DISASSEMBLER_ENTRY(cmp,
255  0x38 /* RegMem8/Reg8 */,     0x39 /* RegMem32/Reg32 */,
256  0x3A /* Reg8/RegMem8 */,     0x3B /* Reg32/RegMem32 */,
257  0x3C /* Rax8/imm8 opcode */, 0x3D /* Rax32/imm32 */)
258
259#undef DISASSEMBLER_ENTRY
260  case 0x50: case 0x51: case 0x52: case 0x53: case 0x54: case 0x55: case 0x56: case 0x57:
261    opcode << "push";
262    reg_in_opcode = true;
263    target_specific = true;
264    break;
265  case 0x58: case 0x59: case 0x5A: case 0x5B: case 0x5C: case 0x5D: case 0x5E: case 0x5F:
266    opcode << "pop";
267    reg_in_opcode = true;
268    target_specific = true;
269    break;
270  case 0x63:
271    if ((rex & REX_W) != 0) {
272      opcode << "movsxd";
273      has_modrm = true;
274      load = true;
275    } else {
276      // In 32-bit mode (!supports_rex_) this is ARPL, with no REX prefix the functionality is the
277      // same as 'mov' but the use of the instruction is discouraged.
278      opcode << StringPrintf("unknown opcode '%02X'", *instr);
279    }
280    break;
281  case 0x68: opcode << "push"; immediate_bytes = 4; break;
282  case 0x69: opcode << "imul"; load = true; has_modrm = true; immediate_bytes = 4; break;
283  case 0x6A: opcode << "push"; immediate_bytes = 1; break;
284  case 0x6B: opcode << "imul"; load = true; has_modrm = true; immediate_bytes = 1; break;
285  case 0x70: case 0x71: case 0x72: case 0x73: case 0x74: case 0x75: case 0x76: case 0x77:
286  case 0x78: case 0x79: case 0x7A: case 0x7B: case 0x7C: case 0x7D: case 0x7E: case 0x7F:
287    static const char* condition_codes[] =
288    {"o", "no", "b/nae/c", "nb/ae/nc", "z/eq",  "nz/ne", "be/na", "nbe/a",
289     "s", "ns", "p/pe",    "np/po",    "l/nge", "nl/ge", "le/ng", "nle/g"
290    };
291    opcode << "j" << condition_codes[*instr & 0xF];
292    branch_bytes = 1;
293    break;
294  case 0x86: case 0x87:
295    opcode << "xchg";
296    store = true;
297    has_modrm = true;
298    byte_operand = (*instr == 0x86);
299    break;
300  case 0x88: opcode << "mov"; store = true; has_modrm = true; byte_operand = true; break;
301  case 0x89: opcode << "mov"; store = true; has_modrm = true; break;
302  case 0x8A: opcode << "mov"; load = true; has_modrm = true; byte_operand = true; break;
303  case 0x8B: opcode << "mov"; load = true; has_modrm = true; break;
304
305  case 0x0F:  // 2 byte extended opcode
306    instr++;
307    switch (*instr) {
308      case 0x10: case 0x11:
309        if (prefix[0] == 0xF2) {
310          opcode << "movsd";
311          prefix[0] = 0;  // clear prefix now it's served its purpose as part of the opcode
312        } else if (prefix[0] == 0xF3) {
313          opcode << "movss";
314          prefix[0] = 0;  // clear prefix now it's served its purpose as part of the opcode
315        } else if (prefix[2] == 0x66) {
316          opcode << "movupd";
317          prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
318        } else {
319          opcode << "movups";
320        }
321        has_modrm = true;
322        src_reg_file = dst_reg_file = SSE;
323        load = *instr == 0x10;
324        store = !load;
325        break;
326      case 0x12: case 0x13:
327        if (prefix[2] == 0x66) {
328          opcode << "movlpd";
329          prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
330        } else if (prefix[0] == 0) {
331          opcode << "movlps";
332        }
333        has_modrm = true;
334        src_reg_file = dst_reg_file = SSE;
335        load = *instr == 0x12;
336        store = !load;
337        break;
338      case 0x16: case 0x17:
339        if (prefix[2] == 0x66) {
340          opcode << "movhpd";
341          prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
342        } else if (prefix[0] == 0) {
343          opcode << "movhps";
344        }
345        has_modrm = true;
346        src_reg_file = dst_reg_file = SSE;
347        load = *instr == 0x16;
348        store = !load;
349        break;
350      case 0x28: case 0x29:
351        if (prefix[2] == 0x66) {
352          opcode << "movapd";
353          prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
354        } else if (prefix[0] == 0) {
355          opcode << "movaps";
356        }
357        has_modrm = true;
358        src_reg_file = dst_reg_file = SSE;
359        load = *instr == 0x28;
360        store = !load;
361        break;
362      case 0x2A:
363        if (prefix[2] == 0x66) {
364          opcode << "cvtpi2pd";
365          prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
366        } else if (prefix[0] == 0xF2) {
367          opcode << "cvtsi2sd";
368          prefix[0] = 0;  // clear prefix now it's served its purpose as part of the opcode
369        } else if (prefix[0] == 0xF3) {
370          opcode << "cvtsi2ss";
371          prefix[0] = 0;  // clear prefix now it's served its purpose as part of the opcode
372        } else {
373          opcode << "cvtpi2ps";
374        }
375        load = true;
376        has_modrm = true;
377        dst_reg_file = SSE;
378        break;
379      case 0x2C:
380        if (prefix[2] == 0x66) {
381          opcode << "cvttpd2pi";
382          prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
383        } else if (prefix[0] == 0xF2) {
384          opcode << "cvttsd2si";
385          prefix[0] = 0;  // clear prefix now it's served its purpose as part of the opcode
386        } else if (prefix[0] == 0xF3) {
387          opcode << "cvttss2si";
388          prefix[0] = 0;  // clear prefix now it's served its purpose as part of the opcode
389        } else {
390          opcode << "cvttps2pi";
391        }
392        load = true;
393        has_modrm = true;
394        src_reg_file = SSE;
395        break;
396      case 0x2D:
397        if (prefix[2] == 0x66) {
398          opcode << "cvtpd2pi";
399          prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
400        } else if (prefix[0] == 0xF2) {
401          opcode << "cvtsd2si";
402          prefix[0] = 0;  // clear prefix now it's served its purpose as part of the opcode
403        } else if (prefix[0] == 0xF3) {
404          opcode << "cvtss2si";
405          prefix[0] = 0;  // clear prefix now it's served its purpose as part of the opcode
406        } else {
407          opcode << "cvtps2pi";
408        }
409        load = true;
410        has_modrm = true;
411        src_reg_file = SSE;
412        break;
413      case 0x2E:
414        opcode << "u";
415        // FALLTHROUGH
416      case 0x2F:
417        if (prefix[2] == 0x66) {
418          opcode << "comisd";
419          prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
420        } else {
421          opcode << "comiss";
422        }
423        has_modrm = true;
424        load = true;
425        src_reg_file = dst_reg_file = SSE;
426        break;
427      case 0x38:  // 3 byte extended opcode
428        instr++;
429        if (prefix[2] == 0x66) {
430          switch (*instr) {
431            case 0x01:
432              opcode << "phaddw";
433              prefix[2] = 0;
434              has_modrm = true;
435              load = true;
436              src_reg_file = dst_reg_file = SSE;
437              break;
438            case 0x02:
439              opcode << "phaddd";
440              prefix[2] = 0;
441              has_modrm = true;
442              load = true;
443              src_reg_file = dst_reg_file = SSE;
444              break;
445            case 0x40:
446              opcode << "pmulld";
447              prefix[2] = 0;
448              has_modrm = true;
449              load = true;
450              src_reg_file = dst_reg_file = SSE;
451              break;
452            default:
453              opcode << StringPrintf("unknown opcode '0F 38 %02X'", *instr);
454          }
455        } else {
456          opcode << StringPrintf("unknown opcode '0F 38 %02X'", *instr);
457        }
458        break;
459      case 0x3A:  // 3 byte extended opcode
460        instr++;
461        if (prefix[2] == 0x66) {
462          switch (*instr) {
463            case 0x14:
464              opcode << "pextrb";
465              prefix[2] = 0;
466              has_modrm = true;
467              store = true;
468              src_reg_file = SSE;
469              immediate_bytes = 1;
470              break;
471            case 0x16:
472              opcode << "pextrd";
473              prefix[2] = 0;
474              has_modrm = true;
475              store = true;
476              src_reg_file = SSE;
477              immediate_bytes = 1;
478              break;
479            default:
480              opcode << StringPrintf("unknown opcode '0F 3A %02X'", *instr);
481          }
482        } else {
483          opcode << StringPrintf("unknown opcode '0F 3A %02X'", *instr);
484        }
485        break;
486      case 0x40: case 0x41: case 0x42: case 0x43: case 0x44: case 0x45: case 0x46: case 0x47:
487      case 0x48: case 0x49: case 0x4A: case 0x4B: case 0x4C: case 0x4D: case 0x4E: case 0x4F:
488        opcode << "cmov" << condition_codes[*instr & 0xF];
489        has_modrm = true;
490        load = true;
491        break;
492      case 0x50: case 0x51: case 0x52: case 0x53: case 0x54: case 0x55: case 0x56: case 0x57:
493      case 0x58: case 0x59: case 0x5C: case 0x5D: case 0x5E: case 0x5F: {
494        switch (*instr) {
495          case 0x50: opcode << "movmsk"; break;
496          case 0x51: opcode << "sqrt"; break;
497          case 0x52: opcode << "rsqrt"; break;
498          case 0x53: opcode << "rcp"; break;
499          case 0x54: opcode << "and"; break;
500          case 0x55: opcode << "andn"; break;
501          case 0x56: opcode << "or"; break;
502          case 0x57: opcode << "xor"; break;
503          case 0x58: opcode << "add"; break;
504          case 0x59: opcode << "mul"; break;
505          case 0x5C: opcode << "sub"; break;
506          case 0x5D: opcode << "min"; break;
507          case 0x5E: opcode << "div"; break;
508          case 0x5F: opcode << "max"; break;
509          default: LOG(FATAL) << "Unreachable";
510        }
511        if (prefix[2] == 0x66) {
512          opcode << "pd";
513          prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
514        } else if (prefix[0] == 0xF2) {
515          opcode << "sd";
516          prefix[0] = 0;  // clear prefix now it's served its purpose as part of the opcode
517        } else if (prefix[0] == 0xF3) {
518          opcode << "ss";
519          prefix[0] = 0;  // clear prefix now it's served its purpose as part of the opcode
520        } else {
521          opcode << "ps";
522        }
523        load = true;
524        has_modrm = true;
525        src_reg_file = dst_reg_file = SSE;
526        break;
527      }
528      case 0x5A:
529        if (prefix[2] == 0x66) {
530          opcode << "cvtpd2ps";
531          prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
532        } else if (prefix[0] == 0xF2) {
533          opcode << "cvtsd2ss";
534          prefix[0] = 0;  // clear prefix now it's served its purpose as part of the opcode
535        } else if (prefix[0] == 0xF3) {
536          opcode << "cvtss2sd";
537          prefix[0] = 0;  // clear prefix now it's served its purpose as part of the opcode
538        } else {
539          opcode << "cvtps2pd";
540        }
541        load = true;
542        has_modrm = true;
543        src_reg_file = dst_reg_file = SSE;
544        break;
545      case 0x5B:
546        if (prefix[2] == 0x66) {
547          opcode << "cvtps2dq";
548          prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
549        } else if (prefix[0] == 0xF2) {
550          opcode << "bad opcode F2 0F 5B";
551        } else if (prefix[0] == 0xF3) {
552          opcode << "cvttps2dq";
553          prefix[0] = 0;  // clear prefix now it's served its purpose as part of the opcode
554        } else {
555          opcode << "cvtdq2ps";
556        }
557        load = true;
558        has_modrm = true;
559        src_reg_file = dst_reg_file = SSE;
560        break;
561      case 0x62:
562        if (prefix[2] == 0x66) {
563          src_reg_file = dst_reg_file = SSE;
564          prefix[2] = 0;  // Clear prefix now. It has served its purpose as part of the opcode.
565        } else {
566          src_reg_file = dst_reg_file = MMX;
567        }
568        opcode << "punpckldq";
569        load = true;
570        has_modrm = true;
571        break;
572      case 0x6E:
573        if (prefix[2] == 0x66) {
574          dst_reg_file = SSE;
575          prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
576        } else {
577          dst_reg_file = MMX;
578        }
579        opcode << "movd";
580        load = true;
581        has_modrm = true;
582        break;
583      case 0x6F:
584        if (prefix[2] == 0x66) {
585          src_reg_file = dst_reg_file = SSE;
586          opcode << "movdqa";
587          prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
588        } else if (prefix[0] == 0xF3) {
589          src_reg_file = dst_reg_file = SSE;
590          opcode << "movdqu";
591          prefix[0] = 0;  // clear prefix now it's served its purpose as part of the opcode
592        } else {
593          dst_reg_file = MMX;
594          opcode << "movq";
595        }
596        load = true;
597        has_modrm = true;
598        break;
599      case 0x70:
600        if (prefix[2] == 0x66) {
601          opcode << "pshufd";
602          prefix[2] = 0;
603          has_modrm = true;
604          store = true;
605          src_reg_file = dst_reg_file = SSE;
606          immediate_bytes = 1;
607        } else if (prefix[0] == 0xF2) {
608          opcode << "pshuflw";
609          prefix[0] = 0;
610          has_modrm = true;
611          store = true;
612          src_reg_file = dst_reg_file = SSE;
613          immediate_bytes = 1;
614        } else {
615          opcode << StringPrintf("unknown opcode '0F %02X'", *instr);
616        }
617        break;
618      case 0x71:
619        if (prefix[2] == 0x66) {
620          dst_reg_file = SSE;
621          prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
622        } else {
623          dst_reg_file = MMX;
624        }
625        static const char* x71_opcodes[] = {"unknown-71", "unknown-71", "psrlw", "unknown-71", "psraw", "unknown-71", "psllw", "unknown-71"};
626        modrm_opcodes = x71_opcodes;
627        reg_is_opcode = true;
628        has_modrm = true;
629        store = true;
630        immediate_bytes = 1;
631        break;
632      case 0x72:
633        if (prefix[2] == 0x66) {
634          dst_reg_file = SSE;
635          prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
636        } else {
637          dst_reg_file = MMX;
638        }
639        static const char* x72_opcodes[] = {"unknown-72", "unknown-72", "psrld", "unknown-72", "psrad", "unknown-72", "pslld", "unknown-72"};
640        modrm_opcodes = x72_opcodes;
641        reg_is_opcode = true;
642        has_modrm = true;
643        store = true;
644        immediate_bytes = 1;
645        break;
646      case 0x73:
647        if (prefix[2] == 0x66) {
648          dst_reg_file = SSE;
649          prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
650        } else {
651          dst_reg_file = MMX;
652        }
653        static const char* x73_opcodes[] = {"unknown-73", "unknown-73", "psrlq", "unknown-73", "unknown-73", "unknown-73", "psllq", "unknown-73"};
654        modrm_opcodes = x73_opcodes;
655        reg_is_opcode = true;
656        has_modrm = true;
657        store = true;
658        immediate_bytes = 1;
659        break;
660      case 0x7C:
661        if (prefix[0] == 0xF2) {
662          opcode << "haddps";
663          prefix[0] = 0;  // clear prefix now it's served its purpose as part of the opcode
664        } else if (prefix[2] == 0x66) {
665          opcode << "haddpd";
666          prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
667        } else {
668          opcode << StringPrintf("unknown opcode '0F %02X'", *instr);
669          break;
670        }
671        src_reg_file = dst_reg_file = SSE;
672        has_modrm = true;
673        load = true;
674        break;
675      case 0x7E:
676        if (prefix[2] == 0x66) {
677          src_reg_file = SSE;
678          prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
679        } else {
680          src_reg_file = MMX;
681        }
682        opcode << "movd";
683        has_modrm = true;
684        store = true;
685        break;
686      case 0x80: case 0x81: case 0x82: case 0x83: case 0x84: case 0x85: case 0x86: case 0x87:
687      case 0x88: case 0x89: case 0x8A: case 0x8B: case 0x8C: case 0x8D: case 0x8E: case 0x8F:
688        opcode << "j" << condition_codes[*instr & 0xF];
689        branch_bytes = 4;
690        break;
691      case 0x90: case 0x91: case 0x92: case 0x93: case 0x94: case 0x95: case 0x96: case 0x97:
692      case 0x98: case 0x99: case 0x9A: case 0x9B: case 0x9C: case 0x9D: case 0x9E: case 0x9F:
693        opcode << "set" << condition_codes[*instr & 0xF];
694        modrm_opcodes = NULL;
695        reg_is_opcode = true;
696        has_modrm = true;
697        store = true;
698        break;
699      case 0xA4:
700        opcode << "shld";
701        has_modrm = true;
702        load = true;
703        immediate_bytes = 1;
704        break;
705      case 0xAC:
706        opcode << "shrd";
707        has_modrm = true;
708        load = true;
709        immediate_bytes = 1;
710        break;
711      case 0xAE:
712        if (prefix[0] == 0xF3) {
713          prefix[0] = 0;  // clear prefix now it's served its purpose as part of the opcode
714          static const char* xAE_opcodes[] = {"rdfsbase", "rdgsbase", "wrfsbase", "wrgsbase", "unknown-AE", "unknown-AE", "unknown-AE", "unknown-AE"};
715          modrm_opcodes = xAE_opcodes;
716          reg_is_opcode = true;
717          has_modrm = true;
718          uint8_t reg_or_opcode = (instr[1] >> 3) & 7;
719          switch (reg_or_opcode) {
720            case 0:
721              prefix[1] = kFs;
722              load = true;
723              break;
724            case 1:
725              prefix[1] = kGs;
726              load = true;
727              break;
728            case 2:
729              prefix[1] = kFs;
730              store = true;
731              break;
732            case 3:
733              prefix[1] = kGs;
734              store = true;
735              break;
736            default:
737              load = true;
738              break;
739          }
740        } else {
741          static const char* xAE_opcodes[] = {"unknown-AE", "unknown-AE", "unknown-AE", "unknown-AE", "unknown-AE", "lfence", "mfence", "sfence"};
742          modrm_opcodes = xAE_opcodes;
743          reg_is_opcode = true;
744          has_modrm = true;
745          load = true;
746          no_ops = true;
747        }
748        break;
749      case 0xAF: opcode << "imul"; has_modrm = true; load = true; break;
750      case 0xB1: opcode << "cmpxchg"; has_modrm = true; store = true; break;
751      case 0xB6: opcode << "movzxb"; has_modrm = true; load = true; byte_second_operand = true; break;
752      case 0xB7: opcode << "movzxw"; has_modrm = true; load = true; break;
753      case 0xBE: opcode << "movsxb"; has_modrm = true; load = true; byte_second_operand = true; rex |= (rex == 0 ? 0 : 0b1000); break;
754      case 0xBF: opcode << "movsxw"; has_modrm = true; load = true; break;
755      case 0xC5:
756        if (prefix[2] == 0x66) {
757          opcode << "pextrw";
758          prefix[2] = 0;
759          has_modrm = true;
760          store = true;
761          src_reg_file = SSE;
762          immediate_bytes = 1;
763        } else {
764          opcode << StringPrintf("unknown opcode '0F %02X'", *instr);
765        }
766        break;
767      case 0xC6:
768        if (prefix[2] == 0x66) {
769          opcode << "shufpd";
770          prefix[2] = 0;
771        } else {
772          opcode << "shufps";
773        }
774        has_modrm = true;
775        store = true;
776        src_reg_file = dst_reg_file = SSE;
777        immediate_bytes = 1;
778        break;
779      case 0xC7:
780        static const char* x0FxC7_opcodes[] = { "unknown-0f-c7", "cmpxchg8b", "unknown-0f-c7", "unknown-0f-c7", "unknown-0f-c7", "unknown-0f-c7", "unknown-0f-c7", "unknown-0f-c7" };
781        modrm_opcodes = x0FxC7_opcodes;
782        has_modrm = true;
783        reg_is_opcode = true;
784        store = true;
785        break;
786      case 0xC8: case 0xC9: case 0xCA: case 0xCB: case 0xCC: case 0xCD: case 0xCE: case 0xCF:
787        opcode << "bswap";
788        reg_in_opcode = true;
789        break;
790      case 0xDB:
791        if (prefix[2] == 0x66) {
792          src_reg_file = dst_reg_file = SSE;
793          prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
794        } else {
795          src_reg_file = dst_reg_file = MMX;
796        }
797        opcode << "pand";
798        prefix[2] = 0;
799        has_modrm = true;
800        load = true;
801        break;
802      case 0xD5:
803        if (prefix[2] == 0x66) {
804          opcode << "pmullw";
805          prefix[2] = 0;
806          has_modrm = true;
807          load = true;
808          src_reg_file = dst_reg_file = SSE;
809        } else {
810          opcode << StringPrintf("unknown opcode '0F %02X'", *instr);
811        }
812        break;
813      case 0xEB:
814        if (prefix[2] == 0x66) {
815          src_reg_file = dst_reg_file = SSE;
816          prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
817        } else {
818          src_reg_file = dst_reg_file = MMX;
819        }
820        opcode << "por";
821        prefix[2] = 0;
822        has_modrm = true;
823        load = true;
824        break;
825      case 0xEF:
826        if (prefix[2] == 0x66) {
827          src_reg_file = dst_reg_file = SSE;
828          prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
829        } else {
830          src_reg_file = dst_reg_file = MMX;
831        }
832        opcode << "pxor";
833        prefix[2] = 0;
834        has_modrm = true;
835        load = true;
836        break;
837      case 0xF8:
838        if (prefix[2] == 0x66) {
839          src_reg_file = dst_reg_file = SSE;
840          prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
841        } else {
842          src_reg_file = dst_reg_file = MMX;
843        }
844        opcode << "psubb";
845        prefix[2] = 0;
846        has_modrm = true;
847        load = true;
848        break;
849      case 0xF9:
850        if (prefix[2] == 0x66) {
851          src_reg_file = dst_reg_file = SSE;
852          prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
853        } else {
854          src_reg_file = dst_reg_file = MMX;
855        }
856        opcode << "psubw";
857        prefix[2] = 0;
858        has_modrm = true;
859        load = true;
860        break;
861      case 0xFA:
862        if (prefix[2] == 0x66) {
863          src_reg_file = dst_reg_file = SSE;
864          prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
865        } else {
866          src_reg_file = dst_reg_file = MMX;
867        }
868        opcode << "psubd";
869        prefix[2] = 0;
870        has_modrm = true;
871        load = true;
872        break;
873      case 0xFC:
874        if (prefix[2] == 0x66) {
875          src_reg_file = dst_reg_file = SSE;
876          prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
877        } else {
878          src_reg_file = dst_reg_file = MMX;
879        }
880        opcode << "paddb";
881        prefix[2] = 0;
882        has_modrm = true;
883        load = true;
884        break;
885      case 0xFD:
886        if (prefix[2] == 0x66) {
887          src_reg_file = dst_reg_file = SSE;
888          prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
889        } else {
890          src_reg_file = dst_reg_file = MMX;
891        }
892        opcode << "paddw";
893        prefix[2] = 0;
894        has_modrm = true;
895        load = true;
896        break;
897      case 0xFE:
898        if (prefix[2] == 0x66) {
899          src_reg_file = dst_reg_file = SSE;
900          prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
901        } else {
902          src_reg_file = dst_reg_file = MMX;
903        }
904        opcode << "paddd";
905        prefix[2] = 0;
906        has_modrm = true;
907        load = true;
908        break;
909      default:
910        opcode << StringPrintf("unknown opcode '0F %02X'", *instr);
911        break;
912    }
913    break;
914  case 0x80: case 0x81: case 0x82: case 0x83:
915    static const char* x80_opcodes[] = {"add", "or", "adc", "sbb", "and", "sub", "xor", "cmp"};
916    modrm_opcodes = x80_opcodes;
917    has_modrm = true;
918    reg_is_opcode = true;
919    store = true;
920    byte_operand = (*instr & 1) == 0;
921    immediate_bytes = *instr == 0x81 ? 4 : 1;
922    break;
923  case 0x84: case 0x85:
924    opcode << "test";
925    has_modrm = true;
926    load = true;
927    byte_operand = (*instr & 1) == 0;
928    break;
929  case 0x8D:
930    opcode << "lea";
931    has_modrm = true;
932    load = true;
933    break;
934  case 0x8F:
935    opcode << "pop";
936    has_modrm = true;
937    reg_is_opcode = true;
938    store = true;
939    break;
940  case 0x99:
941    opcode << "cdq";
942    break;
943  case 0x9B:
944    if (instr[1] == 0xDF && instr[2] == 0xE0) {
945      opcode << "fstsw\tax";
946      instr += 2;
947    } else {
948      opcode << StringPrintf("unknown opcode '%02X'", *instr);
949    }
950    break;
951  case 0xAF:
952    opcode << (prefix[2] == 0x66 ? "scasw" : "scasl");
953    break;
954  case 0xB0: case 0xB1: case 0xB2: case 0xB3: case 0xB4: case 0xB5: case 0xB6: case 0xB7:
955    opcode << "mov";
956    immediate_bytes = 1;
957    byte_operand = true;
958    reg_in_opcode = true;
959    byte_operand = true;
960    break;
961  case 0xB8: case 0xB9: case 0xBA: case 0xBB: case 0xBC: case 0xBD: case 0xBE: case 0xBF:
962    if ((rex & REX_W) != 0) {
963      opcode << "movabsq";
964      immediate_bytes = 8;
965      reg_in_opcode = true;
966      break;
967    }
968    opcode << "mov";
969    immediate_bytes = 4;
970    reg_in_opcode = true;
971    break;
972  case 0xC0: case 0xC1:
973  case 0xD0: case 0xD1: case 0xD2: case 0xD3:
974    static const char* shift_opcodes[] =
975        {"rol", "ror", "rcl", "rcr", "shl", "shr", "unknown-shift", "sar"};
976    modrm_opcodes = shift_opcodes;
977    has_modrm = true;
978    reg_is_opcode = true;
979    store = true;
980    immediate_bytes = ((*instr & 0xf0) == 0xc0) ? 1 : 0;
981    cx = (*instr == 0xD2) || (*instr == 0xD3);
982    byte_operand = (*instr == 0xC0);
983    break;
984  case 0xC3: opcode << "ret"; break;
985  case 0xC6:
986    static const char* c6_opcodes[] = {"mov", "unknown-c6", "unknown-c6", "unknown-c6", "unknown-c6", "unknown-c6", "unknown-c6", "unknown-c6"};
987    modrm_opcodes = c6_opcodes;
988    store = true;
989    immediate_bytes = 1;
990    has_modrm = true;
991    reg_is_opcode = true;
992    byte_operand = true;
993    break;
994  case 0xC7:
995    static const char* c7_opcodes[] = {"mov", "unknown-c7", "unknown-c7", "unknown-c7", "unknown-c7", "unknown-c7", "unknown-c7", "unknown-c7"};
996    modrm_opcodes = c7_opcodes;
997    store = true;
998    immediate_bytes = 4;
999    has_modrm = true;
1000    reg_is_opcode = true;
1001    break;
1002  case 0xCC: opcode << "int 3"; break;
1003  case 0xD9:
1004    if (instr[1] == 0xF8) {
1005      opcode << "fprem";
1006      instr++;
1007    } else {
1008      static const char* d9_opcodes[] = {"flds", "unknown-d9", "fsts", "fstps", "fldenv", "fldcw",
1009                                         "fnstenv", "fnstcw"};
1010      modrm_opcodes = d9_opcodes;
1011      store = true;
1012      has_modrm = true;
1013      reg_is_opcode = true;
1014    }
1015    break;
1016  case 0xDA:
1017    if (instr[1] == 0xE9) {
1018      opcode << "fucompp";
1019      instr++;
1020    } else {
1021      opcode << StringPrintf("unknown opcode '%02X'", *instr);
1022    }
1023    break;
1024  case 0xDB:
1025    static const char* db_opcodes[] = {"fildl", "unknown-db", "unknown-db", "unknown-db", "unknown-db", "unknown-db", "unknown-db", "unknown-db"};
1026    modrm_opcodes = db_opcodes;
1027    load = true;
1028    has_modrm = true;
1029    reg_is_opcode = true;
1030    break;
1031  case 0xDD:
1032    static const char* dd_opcodes[] = {"fldl", "fisttp", "fstl", "fstpl", "frstor", "unknown-dd", "fnsave", "fnstsw"};
1033    modrm_opcodes = dd_opcodes;
1034    store = true;
1035    has_modrm = true;
1036    reg_is_opcode = true;
1037    break;
1038  case 0xDF:
1039    static const char* df_opcodes[] = {"fild", "unknown-df", "unknown-df", "unknown-df", "unknown-df", "fildll", "unknown-df", "unknown-df"};
1040    modrm_opcodes = df_opcodes;
1041    load = true;
1042    has_modrm = true;
1043    reg_is_opcode = true;
1044    break;
1045  case 0xE3: opcode << "jecxz"; branch_bytes = 1; break;
1046  case 0xE8: opcode << "call"; branch_bytes = 4; break;
1047  case 0xE9: opcode << "jmp"; branch_bytes = 4; break;
1048  case 0xEB: opcode << "jmp"; branch_bytes = 1; break;
1049  case 0xF5: opcode << "cmc"; break;
1050  case 0xF6: case 0xF7:
1051    static const char* f7_opcodes[] = {"test", "unknown-f7", "not", "neg", "mul edx:eax, eax *", "imul edx:eax, eax *", "div edx:eax, edx:eax /", "idiv edx:eax, edx:eax /"};
1052    modrm_opcodes = f7_opcodes;
1053    has_modrm = true;
1054    reg_is_opcode = true;
1055    store = true;
1056    immediate_bytes = ((instr[1] & 0x38) == 0) ? 1 : 0;
1057    break;
1058  case 0xFF:
1059    {
1060      static const char* ff_opcodes[] = {"inc", "dec", "call", "call", "jmp", "jmp", "push", "unknown-ff"};
1061      modrm_opcodes = ff_opcodes;
1062      has_modrm = true;
1063      reg_is_opcode = true;
1064      load = true;
1065      const uint8_t opcode_digit = (instr[1] >> 3) & 7;
1066      // 'call', 'jmp' and 'push' are target specific instructions
1067      if (opcode_digit == 2 || opcode_digit == 4 || opcode_digit == 6) {
1068        target_specific = true;
1069      }
1070    }
1071    break;
1072  default:
1073    opcode << StringPrintf("unknown opcode '%02X'", *instr);
1074    break;
1075  }
1076  std::ostringstream args;
1077  // We force the REX prefix to be available for 64-bit target
1078  // in order to dump addr (base/index) registers correctly.
1079  uint8_t rex64 = supports_rex_ ? (rex | 0x40) : rex;
1080  // REX.W should be forced for 64-target and target-specific instructions (i.e., push or pop).
1081  uint8_t rex_w = (supports_rex_ && target_specific) ? (rex | 0x48) : rex;
1082  if (reg_in_opcode) {
1083    DCHECK(!has_modrm);
1084    DumpOpcodeReg(args, rex_w, *instr & 0x7, byte_operand, prefix[2]);
1085  }
1086  instr++;
1087  uint32_t address_bits = 0;
1088  if (has_modrm) {
1089    uint8_t modrm = *instr;
1090    instr++;
1091    uint8_t mod = modrm >> 6;
1092    uint8_t reg_or_opcode = (modrm >> 3) & 7;
1093    uint8_t rm = modrm & 7;
1094    std::ostringstream address;
1095    if (mod == 0 && rm == 5) {
1096      if (!supports_rex_) {  // Absolute address.
1097        address_bits = *reinterpret_cast<const uint32_t*>(instr);
1098        address << StringPrintf("[0x%x]", address_bits);
1099      } else {  // 64-bit RIP relative addressing.
1100        address << StringPrintf("[RIP + 0x%x]",  *reinterpret_cast<const uint32_t*>(instr));
1101      }
1102      instr += 4;
1103    } else if (rm == 4 && mod != 3) {  // SIB
1104      uint8_t sib = *instr;
1105      instr++;
1106      uint8_t scale = (sib >> 6) & 3;
1107      uint8_t index = (sib >> 3) & 7;
1108      uint8_t base = sib & 7;
1109      address << "[";
1110      if (base != 5 || mod != 0) {
1111        DumpBaseReg(address, rex64, base);
1112        if (index != 4) {
1113          address << " + ";
1114        }
1115      }
1116      if (index != 4) {
1117        DumpIndexReg(address, rex64, index);
1118        if (scale != 0) {
1119          address << StringPrintf(" * %d", 1 << scale);
1120        }
1121      }
1122      if (mod == 0) {
1123        if (base == 5) {
1124          if (index != 4) {
1125            address << StringPrintf(" + %d", *reinterpret_cast<const int32_t*>(instr));
1126          } else {
1127            // 64-bit low 32-bit absolute address, redundant absolute address encoding on 32-bit.
1128            address_bits = *reinterpret_cast<const uint32_t*>(instr);
1129            address << StringPrintf("%d", address_bits);
1130          }
1131          instr += 4;
1132        }
1133      } else if (mod == 1) {
1134        address << StringPrintf(" + %d", *reinterpret_cast<const int8_t*>(instr));
1135        instr++;
1136      } else if (mod == 2) {
1137        address << StringPrintf(" + %d", *reinterpret_cast<const int32_t*>(instr));
1138        instr += 4;
1139      }
1140      address << "]";
1141    } else {
1142      if (mod == 3) {
1143        if (!no_ops) {
1144          DumpRmReg(address, rex_w, rm, byte_operand || byte_second_operand,
1145                    prefix[2], load ? src_reg_file : dst_reg_file);
1146        }
1147      } else {
1148        address << "[";
1149        DumpBaseReg(address, rex64, rm);
1150        if (mod == 1) {
1151          address << StringPrintf(" + %d", *reinterpret_cast<const int8_t*>(instr));
1152          instr++;
1153        } else if (mod == 2) {
1154          address << StringPrintf(" + %d", *reinterpret_cast<const int32_t*>(instr));
1155          instr += 4;
1156        }
1157        address << "]";
1158      }
1159    }
1160
1161    if (reg_is_opcode && modrm_opcodes != NULL) {
1162      opcode << modrm_opcodes[reg_or_opcode];
1163    }
1164
1165    // Add opcode suffixes to indicate size.
1166    if (byte_operand) {
1167      opcode << 'b';
1168    } else if ((rex & REX_W) != 0) {
1169      opcode << 'q';
1170    } else if (prefix[2] == 0x66) {
1171      opcode << 'w';
1172    }
1173
1174    if (load) {
1175      if (!reg_is_opcode) {
1176        DumpReg(args, rex, reg_or_opcode, byte_operand, prefix[2], dst_reg_file);
1177        args << ", ";
1178      }
1179      DumpSegmentOverride(args, prefix[1]);
1180      args << address.str();
1181    } else {
1182      DCHECK(store);
1183      DumpSegmentOverride(args, prefix[1]);
1184      args << address.str();
1185      if (!reg_is_opcode) {
1186        args << ", ";
1187        DumpReg(args, rex, reg_or_opcode, byte_operand, prefix[2], src_reg_file);
1188      }
1189    }
1190  }
1191  if (ax) {
1192    // If this opcode implicitly uses ax, ax is always the first arg.
1193    DumpReg(args, rex, 0 /* EAX */, byte_operand, prefix[2], GPR);
1194  }
1195  if (cx) {
1196    args << ", ";
1197    DumpReg(args, rex, 1 /* ECX */, true, prefix[2], GPR);
1198  }
1199  if (immediate_bytes > 0) {
1200    if (has_modrm || reg_in_opcode || ax || cx) {
1201      args << ", ";
1202    }
1203    if (immediate_bytes == 1) {
1204      args << StringPrintf("%d", *reinterpret_cast<const int8_t*>(instr));
1205      instr++;
1206    } else if (immediate_bytes == 4) {
1207      if (prefix[2] == 0x66) {  // Operand size override from 32-bit to 16-bit.
1208        args << StringPrintf("%d", *reinterpret_cast<const int16_t*>(instr));
1209        instr += 2;
1210      } else {
1211        args << StringPrintf("%d", *reinterpret_cast<const int32_t*>(instr));
1212        instr += 4;
1213      }
1214    } else {
1215      CHECK_EQ(immediate_bytes, 8u);
1216      args << StringPrintf("%" PRId64, *reinterpret_cast<const int64_t*>(instr));
1217      instr += 8;
1218    }
1219  } else if (branch_bytes > 0) {
1220    DCHECK(!has_modrm);
1221    int32_t displacement;
1222    if (branch_bytes == 1) {
1223      displacement = *reinterpret_cast<const int8_t*>(instr);
1224      instr++;
1225    } else {
1226      CHECK_EQ(branch_bytes, 4u);
1227      displacement = *reinterpret_cast<const int32_t*>(instr);
1228      instr += 4;
1229    }
1230    args << StringPrintf("%+d (", displacement)
1231         << FormatInstructionPointer(instr + displacement)
1232         << ")";
1233  }
1234  if (prefix[1] == kFs && !supports_rex_) {
1235    args << "  ; ";
1236    Thread::DumpThreadOffset<4>(args, address_bits);
1237  }
1238  if (prefix[1] == kGs && supports_rex_) {
1239    args << "  ; ";
1240    Thread::DumpThreadOffset<8>(args, address_bits);
1241  }
1242  std::stringstream hex;
1243  for (size_t i = 0; begin_instr + i < instr; ++i) {
1244    hex << StringPrintf("%02X", begin_instr[i]);
1245  }
1246  std::stringstream prefixed_opcode;
1247  switch (prefix[0]) {
1248    case 0xF0: prefixed_opcode << "lock "; break;
1249    case 0xF2: prefixed_opcode << "repne "; break;
1250    case 0xF3: prefixed_opcode << "repe "; break;
1251    case 0: break;
1252    default: LOG(FATAL) << "Unreachable";
1253  }
1254  prefixed_opcode << opcode.str();
1255  os << FormatInstructionPointer(begin_instr)
1256     << StringPrintf(": %22s    \t%-7s ", hex.str().c_str(), prefixed_opcode.str().c_str())
1257     << args.str() << '\n';
1258  return instr - begin_instr;
1259}  // NOLINT(readability/fn_size)
1260
1261}  // namespace x86
1262}  // namespace art
1263