Index: src/x64/disasm-x64.cc |
diff --git a/src/x64/disasm-x64.cc b/src/x64/disasm-x64.cc |
index 837da27941d33666b527f29a77632b7ee7dc84cc..bb8f5430ccb0d3e03ad565f9e38290ae81f26b6c 100644 |
--- a/src/x64/disasm-x64.cc |
+++ b/src/x64/disasm-x64.cc |
@@ -148,6 +148,8 @@ enum Prefixes { |
ESCAPE_PREFIX = 0x0F, |
OPERAND_SIZE_OVERRIDE_PREFIX = 0x66, |
ADDRESS_SIZE_OVERRIDE_PREFIX = 0x67, |
+ VEX3_PREFIX = 0xC4, |
+ VEX2_PREFIX = 0xC5, |
REPNE_PREFIX = 0xF2, |
REP_PREFIX = 0xF3, |
REPEQ_PREFIX = REP_PREFIX |
@@ -290,11 +292,14 @@ class DisassemblerX64 { |
ABORT_ON_UNIMPLEMENTED_OPCODE) |
: converter_(converter), |
tmp_buffer_pos_(0), |
- abort_on_unimplemented_( |
- unimplemented_action == ABORT_ON_UNIMPLEMENTED_OPCODE), |
+ abort_on_unimplemented_(unimplemented_action == |
+ ABORT_ON_UNIMPLEMENTED_OPCODE), |
rex_(0), |
operand_size_(0), |
group_1_prefix_(0), |
+ vex_byte0_(0), |
+ vex_byte1_(0), |
+ vex_byte2_(0), |
byte_size_operand_(false), |
instruction_table_(instruction_table.Pointer()) { |
tmp_buffer_[0] = '\0'; |
@@ -323,6 +328,9 @@ class DisassemblerX64 { |
byte rex_; |
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 vex_byte0_; // 0xc4 or 0xc5 |
+ byte vex_byte1_; |
+ byte vex_byte2_; // only for 3 bytes vex prefix |
// Byte size operand override. |
bool byte_size_operand_; |
const InstructionTable* const instruction_table_; |
@@ -345,6 +353,51 @@ class DisassemblerX64 { |
bool rex_w() { return (rex_ & 0x08) != 0; } |
+ bool vex_128() { |
+ DCHECK(vex_byte0_ == VEX3_PREFIX || vex_byte0_ == VEX2_PREFIX); |
+ byte checked = vex_byte0_ == VEX3_PREFIX ? vex_byte2_ : vex_byte1_; |
+ return (checked & 4) != 1; |
+ } |
+ |
+ bool vex_66() { |
+ DCHECK(vex_byte0_ == VEX3_PREFIX || vex_byte0_ == VEX2_PREFIX); |
+ byte checked = vex_byte0_ == VEX3_PREFIX ? vex_byte2_ : vex_byte1_; |
+ return (checked & 3) == 1; |
+ } |
+ |
+ bool vex_f3() { |
+ DCHECK(vex_byte0_ == VEX3_PREFIX || vex_byte0_ == VEX2_PREFIX); |
+ byte checked = vex_byte0_ == VEX3_PREFIX ? vex_byte2_ : vex_byte1_; |
+ return (checked & 3) == 2; |
+ } |
+ |
+ bool vex_f2() { |
+ DCHECK(vex_byte0_ == VEX3_PREFIX || vex_byte0_ == VEX2_PREFIX); |
+ byte checked = vex_byte0_ == VEX3_PREFIX ? vex_byte2_ : vex_byte1_; |
+ return (checked & 3) == 3; |
+ } |
+ |
+ bool vex_0f() { |
+ if (vex_byte0_ == VEX2_PREFIX) return true; |
+ return (vex_byte1_ & 3) == 1; |
+ } |
+ |
+ bool vex_0f38() { |
+ DCHECK(vex_byte0_ == VEX3_PREFIX); |
+ return (vex_byte1_ & 3) == 2; |
+ } |
+ |
+ bool vex_0f3a() { |
+ DCHECK(vex_byte0_ == VEX3_PREFIX); |
+ return (vex_byte1_ & 3) == 3; |
+ } |
+ |
+ int vex_vreg() { |
+ DCHECK(vex_byte0_ == VEX3_PREFIX || vex_byte0_ == VEX2_PREFIX); |
+ byte checked = vex_byte0_ == VEX3_PREFIX ? vex_byte2_ : vex_byte1_; |
+ return ~(checked >> 3) & 0xf; |
+ } |
+ |
OperandSize operand_size() { |
if (byte_size_operand_) return OPERAND_BYTE_SIZE; |
if (rex_w()) return OPERAND_QUADWORD_SIZE; |
@@ -356,6 +409,8 @@ class DisassemblerX64 { |
return "bwlq"[operand_size()]; |
} |
+ char float_size_code() { return "sd"[rex_w()]; } |
+ |
const char* NameOfCPURegister(int reg) const { |
return converter_.NameOfCPURegister(reg); |
} |
@@ -414,6 +469,7 @@ class DisassemblerX64 { |
int FPUInstruction(byte* data); |
int MemoryFPUInstruction(int escape_opcode, int regop, byte* modrm_start); |
int RegisterFPUInstruction(int escape_opcode, byte modrm_byte); |
+ int AVXInstruction(byte* data); |
void AppendToBuffer(const char* format, ...); |
void UnimplementedInstruction() { |
@@ -811,6 +867,92 @@ int DisassemblerX64::SetCC(byte* data) { |
} |
+int DisassemblerX64::AVXInstruction(byte* data) { |
+ byte opcode = *data; |
+ byte* current = data + 1; |
+ if (vex_byte0_ == VEX3_PREFIX) { |
+ if (vex_128()) { |
+ if (vex_66() && vex_0f38()) { |
+ int mod, regop, rm, vvvv = vex_vreg(); |
+ get_modrm(*current, &mod, ®op, &rm); |
+ switch (opcode) { |
+ case 0x99: |
+ AppendToBuffer("vfmadd132s%c %s,%s,", float_size_code(), |
+ NameOfXMMRegister(regop), NameOfXMMRegister(vvvv)); |
+ current += PrintRightXMMOperand(current); |
+ break; |
+ case 0xa9: |
+ AppendToBuffer("vfmadd213s%c %s,%s,", float_size_code(), |
+ NameOfXMMRegister(regop), NameOfXMMRegister(vvvv)); |
+ current += PrintRightXMMOperand(current); |
+ break; |
+ case 0xb9: |
+ AppendToBuffer("vfmadd231s%c %s,%s,", float_size_code(), |
+ NameOfXMMRegister(regop), NameOfXMMRegister(vvvv)); |
+ current += PrintRightXMMOperand(current); |
+ break; |
+ case 0x9b: |
+ AppendToBuffer("vfmsub132s%c %s,%s,", float_size_code(), |
+ NameOfXMMRegister(regop), NameOfXMMRegister(vvvv)); |
+ current += PrintRightXMMOperand(current); |
+ break; |
+ case 0xab: |
+ AppendToBuffer("vfmsub213s%c %s,%s,", float_size_code(), |
+ NameOfXMMRegister(regop), NameOfXMMRegister(vvvv)); |
+ current += PrintRightXMMOperand(current); |
+ break; |
+ case 0xbb: |
+ AppendToBuffer("vfmsub231s%c %s,%s,", float_size_code(), |
+ NameOfXMMRegister(regop), NameOfXMMRegister(vvvv)); |
+ current += PrintRightXMMOperand(current); |
+ break; |
+ case 0x9d: |
+ AppendToBuffer("vfnmadd132s%c %s,%s,", float_size_code(), |
+ NameOfXMMRegister(regop), NameOfXMMRegister(vvvv)); |
+ current += PrintRightXMMOperand(current); |
+ break; |
+ case 0xad: |
+ AppendToBuffer("vfnmadd213s%c %s,%s,", float_size_code(), |
+ NameOfXMMRegister(regop), NameOfXMMRegister(vvvv)); |
+ current += PrintRightXMMOperand(current); |
+ break; |
+ case 0xbd: |
+ AppendToBuffer("vfnmadd231s%c %s,%s,", float_size_code(), |
+ NameOfXMMRegister(regop), NameOfXMMRegister(vvvv)); |
+ current += PrintRightXMMOperand(current); |
+ break; |
+ case 0x9f: |
+ AppendToBuffer("vfnmsub132s%c %s,%s,", float_size_code(), |
+ NameOfXMMRegister(regop), NameOfXMMRegister(vvvv)); |
+ current += PrintRightXMMOperand(current); |
+ break; |
+ case 0xaf: |
+ AppendToBuffer("vfnmsub213s%c %s,%s,", float_size_code(), |
+ NameOfXMMRegister(regop), NameOfXMMRegister(vvvv)); |
+ current += PrintRightXMMOperand(current); |
+ break; |
+ case 0xbf: |
+ AppendToBuffer("vfnmsub231s%c %s,%s,", float_size_code(), |
+ NameOfXMMRegister(regop), NameOfXMMRegister(vvvv)); |
+ current += PrintRightXMMOperand(current); |
+ break; |
+ default: |
+ UnimplementedInstruction(); |
+ } |
+ } |
+ } else { |
+ UnimplementedInstruction(); |
+ } |
+ } else if (vex_byte0_ == VEX2_PREFIX) { |
+ UnimplementedInstruction(); |
+ } else { |
+ UNREACHABLE(); |
+ } |
+ |
+ return static_cast<int>(current - data); |
+} |
+ |
+ |
// Returns number of bytes used, including *data. |
int DisassemblerX64::FPUInstruction(byte* data) { |
byte escape_opcode = *data; |
@@ -1189,6 +1331,16 @@ int DisassemblerX64::TwoByteOpcodeInstruction(byte* data) { |
AppendToBuffer("cvttss2si%c %s,", |
operand_size_code(), NameOfCPURegister(regop)); |
current += PrintRightXMMOperand(current); |
+ } else if (opcode == 0x58) { |
+ int mod, regop, rm; |
+ get_modrm(*current, &mod, ®op, &rm); |
+ AppendToBuffer("addss %s,", NameOfXMMRegister(regop)); |
+ current += PrintRightXMMOperand(current); |
+ } else if (opcode == 0x59) { |
+ int mod, regop, rm; |
+ get_modrm(*current, &mod, ®op, &rm); |
+ AppendToBuffer("mulss %s,", NameOfXMMRegister(regop)); |
+ current += PrintRightXMMOperand(current); |
} else if (opcode == 0x5A) { |
// CVTSS2SD: |
// Convert scalar single-precision FP to scalar double-precision FP. |
@@ -1196,6 +1348,16 @@ int DisassemblerX64::TwoByteOpcodeInstruction(byte* data) { |
get_modrm(*current, &mod, ®op, &rm); |
AppendToBuffer("cvtss2sd %s,", NameOfXMMRegister(regop)); |
current += PrintRightXMMOperand(current); |
+ } else if (opcode == 0x5c) { |
+ int mod, regop, rm; |
+ get_modrm(*current, &mod, ®op, &rm); |
+ AppendToBuffer("subss %s,", NameOfXMMRegister(regop)); |
+ current += PrintRightXMMOperand(current); |
+ } else if (opcode == 0x5e) { |
+ int mod, regop, rm; |
+ get_modrm(*current, &mod, ®op, &rm); |
+ AppendToBuffer("divss %s,", NameOfXMMRegister(regop)); |
+ current += PrintRightXMMOperand(current); |
} else if (opcode == 0x7E) { |
int mod, regop, rm; |
get_modrm(*current, &mod, ®op, &rm); |
@@ -1234,6 +1396,11 @@ int DisassemblerX64::TwoByteOpcodeInstruction(byte* data) { |
current += PrintRightXMMOperand(current); |
AppendToBuffer(",%s", NameOfXMMRegister(regop)); |
+ } else if (opcode == 0x2e) { |
+ int mod, regop, rm; |
+ get_modrm(*current, &mod, ®op, &rm); |
+ AppendToBuffer("ucomiss %s,", NameOfXMMRegister(regop)); |
+ current += PrintRightXMMOperand(current); |
} else if (opcode == 0xA2) { |
// CPUID |
AppendToBuffer("%s", mnemonic); |
@@ -1387,99 +1554,114 @@ int DisassemblerX64::InstructionDecode(v8::internal::Vector<char> out_buffer, |
if (rex_w()) AppendToBuffer("REX.W "); |
} else if ((current & 0xFE) == 0xF2) { // Group 1 prefix (0xF2 or 0xF3). |
group_1_prefix_ = current; |
+ } else if (current == VEX3_PREFIX) { |
+ vex_byte0_ = current; |
+ vex_byte1_ = *(data + 1); |
+ vex_byte2_ = *(data + 2); |
+ setRex(0x40 | (~(vex_byte1_ >> 5) & 7) | ((vex_byte2_ >> 4) & 8)); |
+ data += 2; |
+ } else if (current == VEX2_PREFIX) { |
+ vex_byte0_ = current; |
+ vex_byte1_ = *(data + 1); |
+ setRex(0x40 | (~(vex_byte1_ >> 5) & 4)); |
+ data++; |
} else { // Not a prefix - an opcode. |
break; |
} |
data++; |
} |
- const InstructionDesc& idesc = instruction_table_->Get(current); |
- byte_size_operand_ = idesc.byte_size_operation; |
- switch (idesc.type) { |
- case ZERO_OPERANDS_INSTR: |
- if (current >= 0xA4 && current <= 0xA7) { |
- // String move or compare operations. |
- if (group_1_prefix_ == REP_PREFIX) { |
- // REP. |
- AppendToBuffer("rep "); |
+ // Decode AVX instructions. |
+ if (vex_byte0_ != 0) { |
+ processed = true; |
+ data += AVXInstruction(data); |
+ } else { |
+ const InstructionDesc& idesc = instruction_table_->Get(current); |
+ byte_size_operand_ = idesc.byte_size_operation; |
+ switch (idesc.type) { |
+ case ZERO_OPERANDS_INSTR: |
+ if (current >= 0xA4 && current <= 0xA7) { |
+ // String move or compare operations. |
+ if (group_1_prefix_ == REP_PREFIX) { |
+ // REP. |
+ AppendToBuffer("rep "); |
+ } |
+ if (rex_w()) AppendToBuffer("REX.W "); |
+ AppendToBuffer("%s%c", idesc.mnem, operand_size_code()); |
+ } else { |
+ AppendToBuffer("%s", idesc.mnem, operand_size_code()); |
} |
- if (rex_w()) AppendToBuffer("REX.W "); |
- AppendToBuffer("%s%c", idesc.mnem, operand_size_code()); |
- } else { |
- AppendToBuffer("%s", idesc.mnem, operand_size_code()); |
- } |
- data++; |
- break; |
+ data++; |
+ break; |
- case TWO_OPERANDS_INSTR: |
- data++; |
- data += PrintOperands(idesc.mnem, idesc.op_order_, data); |
- break; |
+ case TWO_OPERANDS_INSTR: |
+ data++; |
+ data += PrintOperands(idesc.mnem, idesc.op_order_, data); |
+ break; |
- case JUMP_CONDITIONAL_SHORT_INSTR: |
- data += JumpConditionalShort(data); |
- break; |
+ case JUMP_CONDITIONAL_SHORT_INSTR: |
+ data += JumpConditionalShort(data); |
+ break; |
- case REGISTER_INSTR: |
- AppendToBuffer("%s%c %s", |
- idesc.mnem, |
- operand_size_code(), |
- NameOfCPURegister(base_reg(current & 0x07))); |
- data++; |
- break; |
- case PUSHPOP_INSTR: |
- AppendToBuffer("%s %s", |
- idesc.mnem, |
- NameOfCPURegister(base_reg(current & 0x07))); |
- data++; |
- break; |
- case MOVE_REG_INSTR: { |
- byte* addr = NULL; |
- switch (operand_size()) { |
- case OPERAND_WORD_SIZE: |
- addr = reinterpret_cast<byte*>(*reinterpret_cast<int16_t*>(data + 1)); |
- data += 3; |
- break; |
- case OPERAND_DOUBLEWORD_SIZE: |
- addr = |
- reinterpret_cast<byte*>(*reinterpret_cast<uint32_t*>(data + 1)); |
- data += 5; |
- break; |
- case OPERAND_QUADWORD_SIZE: |
- addr = reinterpret_cast<byte*>(*reinterpret_cast<int64_t*>(data + 1)); |
- data += 9; |
- break; |
- default: |
- UNREACHABLE(); |
+ case REGISTER_INSTR: |
+ AppendToBuffer("%s%c %s", idesc.mnem, operand_size_code(), |
+ NameOfCPURegister(base_reg(current & 0x07))); |
+ data++; |
+ break; |
+ case PUSHPOP_INSTR: |
+ AppendToBuffer("%s %s", idesc.mnem, |
+ NameOfCPURegister(base_reg(current & 0x07))); |
+ data++; |
+ break; |
+ case MOVE_REG_INSTR: { |
+ byte* addr = NULL; |
+ switch (operand_size()) { |
+ case OPERAND_WORD_SIZE: |
+ addr = |
+ reinterpret_cast<byte*>(*reinterpret_cast<int16_t*>(data + 1)); |
+ data += 3; |
+ break; |
+ case OPERAND_DOUBLEWORD_SIZE: |
+ addr = |
+ reinterpret_cast<byte*>(*reinterpret_cast<uint32_t*>(data + 1)); |
+ data += 5; |
+ break; |
+ case OPERAND_QUADWORD_SIZE: |
+ addr = |
+ reinterpret_cast<byte*>(*reinterpret_cast<int64_t*>(data + 1)); |
+ data += 9; |
+ break; |
+ default: |
+ UNREACHABLE(); |
+ } |
+ AppendToBuffer("mov%c %s,%s", operand_size_code(), |
+ NameOfCPURegister(base_reg(current & 0x07)), |
+ NameOfAddress(addr)); |
+ break; |
} |
- AppendToBuffer("mov%c %s,%s", |
- operand_size_code(), |
- NameOfCPURegister(base_reg(current & 0x07)), |
- NameOfAddress(addr)); |
- break; |
- } |
- case CALL_JUMP_INSTR: { |
- byte* addr = data + *reinterpret_cast<int32_t*>(data + 1) + 5; |
- AppendToBuffer("%s %s", idesc.mnem, NameOfAddress(addr)); |
- data += 5; |
- break; |
- } |
+ case CALL_JUMP_INSTR: { |
+ byte* addr = data + *reinterpret_cast<int32_t*>(data + 1) + 5; |
+ AppendToBuffer("%s %s", idesc.mnem, NameOfAddress(addr)); |
+ data += 5; |
+ break; |
+ } |
- case SHORT_IMMEDIATE_INSTR: { |
- byte* addr = |
- reinterpret_cast<byte*>(*reinterpret_cast<int32_t*>(data + 1)); |
- AppendToBuffer("%s rax,%s", idesc.mnem, NameOfAddress(addr)); |
- data += 5; |
- break; |
- } |
+ case SHORT_IMMEDIATE_INSTR: { |
+ byte* addr = |
+ reinterpret_cast<byte*>(*reinterpret_cast<int32_t*>(data + 1)); |
+ AppendToBuffer("%s rax,%s", idesc.mnem, NameOfAddress(addr)); |
+ data += 5; |
+ break; |
+ } |
- case NO_INSTR: |
- processed = false; |
- break; |
+ case NO_INSTR: |
+ processed = false; |
+ break; |
- default: |
- UNIMPLEMENTED(); // This type is not implemented. |
+ default: |
+ UNIMPLEMENTED(); // This type is not implemented. |
+ } |
} |
// The first byte didn't match any of the simple opcodes, so we |