Index: src/x64/disasm-x64.cc |
=================================================================== |
--- src/x64/disasm-x64.cc (revision 2512) |
+++ src/x64/disasm-x64.cc (working copy) |
@@ -253,13 +253,16 @@ |
static InstructionTable instruction_table; |
-// The X64 disassembler implementation. |
+//------------------------------------------------------------------------------ |
+// DisassemblerX64 implementation. |
+ |
enum UnimplementedOpcodeAction { |
CONTINUE_ON_UNIMPLEMENTED_OPCODE, |
ABORT_ON_UNIMPLEMENTED_OPCODE |
}; |
- |
+// A new DisassemblerX64 object is created to disassemble each instruction. |
+// The object can only disassemble a single instruction. |
class DisassemblerX64 { |
public: |
DisassemblerX64(const NameConverter& converter, |
@@ -271,6 +274,7 @@ |
unimplemented_action == ABORT_ON_UNIMPLEMENTED_OPCODE), |
rex_(0), |
operand_size_(0), |
+ group_1_prefix_(0), |
byte_size_operand_(false) { |
tmp_buffer_[0] = '\0'; |
} |
@@ -296,15 +300,11 @@ |
bool abort_on_unimplemented_; |
// Prefixes parsed |
byte rex_; |
- byte operand_size_; |
+ byte operand_size_; // 0x66 or (if no group 3 prefix is present) 0x0. |
+ byte group_1_prefix_; // 0xF2, 0xF3, or (if no group 1 prefix is present) 0. |
// Byte size operand override. |
bool byte_size_operand_; |
- void setOperandSizePrefix(byte prefix) { |
- ASSERT_EQ(0x66, prefix); |
- operand_size_ = prefix; |
- } |
- |
void setRex(byte rex) { |
ASSERT_EQ(0x40, rex & 0xF0); |
rex_ = rex; |
@@ -380,6 +380,8 @@ |
byte* data); |
int PrintImmediate(byte* data, OperandSize size); |
int PrintImmediateOp(byte* data); |
+ const char* TwoByteMnemonic(byte opcode); |
+ int TwoByteOpcodeInstruction(byte* data); |
int F7Instruction(byte* data); |
int ShiftInstruction(byte* data); |
int JumpShort(byte* data); |
@@ -391,7 +393,7 @@ |
void UnimplementedInstruction() { |
if (abort_on_unimplemented_) { |
- UNIMPLEMENTED(); |
+ CHECK(false); |
} else { |
AppendToBuffer("'Unimplemented Instruction'"); |
} |
@@ -936,38 +938,146 @@ |
return 2; |
} |
-// Mnemonics for instructions 0xF0 byte. |
+ |
+// Handle all two-byte opcodes, which start with 0x0F. |
+// These instructions may be affected by an 0x66, 0xF2, or 0xF3 prefix. |
+// We do not use any three-byte opcodes, which start with 0x0F38 or 0x0F3A. |
+int DisassemblerX64::TwoByteOpcodeInstruction(byte* data) { |
+ byte opcode = *(data + 1); |
+ byte* current = data + 2; |
+ // At return, "current" points to the start of the next instruction. |
+ const char* mnemonic = TwoByteMnemonic(opcode); |
+ if (opcode == 0x1F) { |
+ // NOP |
+ int mod, regop, rm; |
+ get_modrm(*current, &mod, ®op, &rm); |
+ current++; |
+ if (regop == 4) { // SIB byte present. |
+ current++; |
+ } |
+ if (mod == 1) { // Byte displacement. |
+ current += 1; |
+ } else if (mod == 2) { // 32-bit displacement. |
+ current += 4; |
+ } // else no immediate displacement. |
+ AppendToBuffer("nop"); |
+ |
+ } else if (opcode == 0xA2 || opcode == 0x31) { |
+ // RDTSC or CPUID |
+ AppendToBuffer("%s", mnemonic); |
+ |
+ } else if ((opcode & 0xF0) == 0x80) { |
+ // Jcc: Conditional jump (branch). |
+ current = data + JumpConditional(data); |
+ |
+ } else if (opcode == 0xBE || opcode == 0xBF || opcode == 0xB6 || |
+ opcode == 0xB7 || opcode == 0xAF) { |
+ // Size-extending moves, IMUL. |
+ current += PrintOperands(mnemonic, REG_OPER_OP_ORDER, current); |
+ |
+ } else if ((opcode & 0xF0) == 0x90) { |
+ // SETcc: Set byte on condition. Needs pointer to beginning of instruction. |
+ current = data + SetCC(data); |
+ |
+ } else if (opcode == 0xAB || opcode == 0xA5 || opcode == 0xAD) { |
+ // SHLD, SHRD (double-precision shift), BTS (bit set). |
+ AppendToBuffer("%s ", mnemonic); |
+ int mod, regop, rm; |
+ get_modrm(*current, &mod, ®op, &rm); |
+ current += PrintRightOperand(current); |
+ if (opcode == 0xAB) { |
+ AppendToBuffer(",%s", NameOfCPURegister(regop)); |
+ } else { |
+ AppendToBuffer(",%s,cl", NameOfCPURegister(regop)); |
+ } |
+ } else if (group_1_prefix_ == 0xF2) { |
+ // Beginning of instructions with prefix 0xF2. |
+ |
+ if (opcode == 0x11 || opcode == 0x10) { |
+ // MOVSD: Move scalar double-precision fp to/from/between XMM registers. |
+ AppendToBuffer("movsd "); |
+ int mod, regop, rm; |
+ get_modrm(*current, &mod, ®op, &rm); |
+ if (opcode == 0x11) { |
+ current += PrintRightOperand(current); |
+ AppendToBuffer(",%s", NameOfXMMRegister(regop)); |
+ } else { |
+ AppendToBuffer("%s,", NameOfXMMRegister(regop)); |
+ current += PrintRightOperand(current); |
+ } |
+ } else if (opcode == 0x2A) { |
+ // CVTSI2SD: integer to XMM double conversion. |
+ int mod, regop, rm; |
+ get_modrm(*current, &mod, ®op, &rm); |
+ AppendToBuffer("%s %s,", mnemonic, NameOfXMMRegister(regop)); |
+ data += PrintRightOperand(data); |
+ } else if ((opcode & 0xF8) == 0x58) { |
+ // XMM arithmetic. Mnemonic was retrieved at the start of this function. |
+ int mod, regop, rm; |
+ get_modrm(*current, &mod, ®op, &rm); |
+ AppendToBuffer("%s %s,%s", mnemonic, NameOfXMMRegister(regop), |
+ NameOfXMMRegister(rm)); |
+ } else { |
+ UnimplementedInstruction(); |
+ } |
+ } else if (opcode == 0x2C && group_1_prefix_ == 0xF3) { |
+ // Instruction with prefix 0xF3. |
+ |
+ // CVTTSS2SI: Convert scalar single-precision FP to dword integer. |
+ // Assert that mod is not 3, so source is memory, not an XMM register. |
+ ASSERT((*current & 0xC0) != 0xC0); |
+ current += PrintOperands("cvttss2si", REG_OPER_OP_ORDER, current); |
+ } else { |
+ UnimplementedInstruction(); |
+ } |
+ return current - data; |
+} |
+ |
+ |
+// Mnemonics for two-byte opcode instructions starting with 0x0F. |
+// The argument is the second byte of the two-byte opcode. |
// Returns NULL if the instruction is not handled here. |
-static const char* F0Mnem(byte f0byte) { |
- switch (f0byte) { |
+const char* DisassemblerX64::TwoByteMnemonic(byte opcode) { |
+ switch (opcode) { |
case 0x1F: |
return "nop"; |
+ case 0x2A: // F2 prefix. |
+ return "cvtsi2sd"; |
case 0x31: |
return "rdtsc"; |
+ case 0x58: // F2 prefix. |
+ return "addsd"; |
+ case 0x59: // F2 prefix. |
+ return "mulsd"; |
+ case 0x5C: // F2 prefix. |
+ return "subsd"; |
+ case 0x5E: // F2 prefix. |
+ return "divsd"; |
case 0xA2: |
return "cpuid"; |
- case 0xBE: |
- return "movsxb"; |
- case 0xBF: |
- return "movsxw"; |
- case 0xB6: |
- return "movzxb"; |
- case 0xB7: |
- return "movzxw"; |
- case 0xAF: |
- return "imul"; |
case 0xA5: |
return "shld"; |
- case 0xAD: |
- return "shrd"; |
case 0xAB: |
return "bts"; |
+ case 0xAD: |
+ return "shrd"; |
+ case 0xAF: |
+ return "imul"; |
+ case 0xB6: |
+ return "movzxb"; |
+ case 0xB7: |
+ return "movzxw"; |
+ case 0xBE: |
+ return "movsxb"; |
+ case 0xBF: |
+ return "movsxw"; |
default: |
return NULL; |
} |
} |
-// Disassembled instruction '*instr' and writes it into 'out_buffer'. |
+ |
+// Disassembles the instruction at instr, and writes it into out_buffer. |
int DisassemblerX64::InstructionDecode(v8::internal::Vector<char> out_buffer, |
byte* instr) { |
tmp_buffer_pos_ = 0; // starting to write as position 0 |
@@ -979,17 +1089,17 @@ |
// Scan for prefixes. |
while (true) { |
current = *data; |
- if (current == 0x66) { |
- // If the sequence is 66 0f, it's not a prefix, but a SSE escape. |
- if (*(data + 1) == 0x0F) break; |
- data++; |
- } else if ((current & 0xF0) == 0x40) { |
+ if (current == 0x66) { // Group 3 prefix. |
+ operand_size_ = current; |
+ } else if ((current & 0xF0) == 0x40) { // REX prefix. |
setRex(current); |
if (rex_w()) AppendToBuffer("REX.W "); |
- data++; |
- } else { |
+ } else if ((current & 0xFE) == 0xF2) { // Group 1 prefix. |
+ group_1_prefix_ = current; |
+ } else { // Not a prefix - an opcode. |
break; |
} |
+ data++; |
} |
const InstructionDesc& idesc = instruction_table.Get(current); |
@@ -1108,55 +1218,8 @@ |
data += PrintImmediateOp(data); |
break; |
- case 0x0F: { |
- byte f0byte = *(data + 1); |
- const char* f0mnem = F0Mnem(f0byte); |
- if (f0byte == 0x1F) { |
- data += 1; |
- byte modrm = *data; |
- data += 1; |
- if (((modrm >> 3) & 7) == 4) { |
- // SIB byte present. |
- data += 1; |
- } |
- int mod = modrm >> 6; |
- if (mod == 1) { |
- // Byte displacement. |
- data += 1; |
- } else if (mod == 2) { |
- // 32-bit displacement. |
- data += 4; |
- } |
- AppendToBuffer("nop"); |
- } else if (f0byte == 0xA2 || f0byte == 0x31) { |
- AppendToBuffer("%s", f0mnem); |
- data += 2; |
- } else if ((f0byte & 0xF0) == 0x80) { |
- data += JumpConditional(data); |
- } else if (f0byte == 0xBE || f0byte == 0xBF || f0byte == 0xB6 || f0byte |
- == 0xB7 || f0byte == 0xAF) { |
- data += 2; |
- data += PrintOperands(f0mnem, REG_OPER_OP_ORDER, data); |
- } else if ((f0byte & 0xF0) == 0x90) { |
- data += SetCC(data); |
- } else { |
- data += 2; |
- if (f0byte == 0xAB || f0byte == 0xA5 || f0byte == 0xAD) { |
- // shrd, shld, bts |
- AppendToBuffer("%s ", f0mnem); |
- int mod, regop, rm; |
- get_modrm(*data, &mod, ®op, &rm); |
- data += PrintRightOperand(data); |
- if (f0byte == 0xAB) { |
- AppendToBuffer(",%s", NameOfCPURegister(regop)); |
- } else { |
- AppendToBuffer(",%s,cl", NameOfCPURegister(regop)); |
- } |
- } else { |
- UnimplementedInstruction(); |
- } |
- } |
- } |
+ case 0x0F: |
+ data += TwoByteOpcodeInstruction(data); |
break; |
case 0x8F: { |
@@ -1303,10 +1366,10 @@ |
default: |
UNREACHABLE(); |
} |
- break; |
AppendToBuffer("test%c rax,0x%"V8_PTR_PREFIX"ux", |
operand_size_code(), |
value); |
+ break; |
} |
case 0xD1: // fall through |
case 0xD3: // fall through |
@@ -1334,73 +1397,6 @@ |
data += JumpShort(data); |
break; |
- case 0xF2: |
- if (*(data + 1) == 0x0F) { |
- byte b2 = *(data + 2); |
- if (b2 == 0x11) { |
- AppendToBuffer("movsd "); |
- data += 3; |
- int mod, regop, rm; |
- get_modrm(*data, &mod, ®op, &rm); |
- data += PrintRightOperand(data); |
- AppendToBuffer(",%s", NameOfXMMRegister(regop)); |
- } else if (b2 == 0x10) { |
- data += 3; |
- int mod, regop, rm; |
- get_modrm(*data, &mod, ®op, &rm); |
- AppendToBuffer("movsd %s,", NameOfXMMRegister(regop)); |
- data += PrintRightOperand(data); |
- } else { |
- const char* mnem = "?"; |
- switch (b2) { |
- case 0x2A: |
- mnem = "cvtsi2sd"; |
- break; |
- case 0x58: |
- mnem = "addsd"; |
- break; |
- case 0x59: |
- mnem = "mulsd"; |
- break; |
- case 0x5C: |
- mnem = "subsd"; |
- break; |
- case 0x5E: |
- mnem = "divsd"; |
- break; |
- } |
- data += 3; |
- int mod, regop, rm; |
- get_modrm(*data, &mod, ®op, &rm); |
- if (b2 == 0x2A) { |
- AppendToBuffer("%s %s,", mnem, NameOfXMMRegister(regop)); |
- data += PrintRightOperand(data); |
- } else { |
- AppendToBuffer("%s %s,%s", mnem, NameOfXMMRegister(regop), |
- NameOfXMMRegister(rm)); |
- data++; |
- } |
- } |
- } else { |
- UnimplementedInstruction(); |
- } |
- break; |
- |
- case 0xF3: |
- if (*(data + 1) == 0x0F) { |
- if (*(data + 2) == 0x2C) { |
- data += 3; |
- data += PrintOperands("cvttss2si", REG_OPER_OP_ORDER, data); |
- } else { |
- UnimplementedInstruction(); |
- data += 1; |
- } |
- } else { |
- UnimplementedInstruction(); |
- data += 1; |
- } |
- break; |
- |
case 0xF7: |
data += F7Instruction(data); |
break; |