mirror of
https://github.com/dart-lang/sdk
synced 2024-11-05 18:22:09 +00:00
dfb67ecbf8
Removes some redundancy in the Emit* methods in intermediate_language_*.cc for the 6 architectures we support. This is a reapplication of https://codereview.chromium.org/2937933002/ - see the comments there. It fixes a bug in the way the TestCids instruction was emitted on DBC. R=vegorov@google.com BUG= Review-Url: https://codereview.chromium.org/2947633002 .
1545 lines
48 KiB
C++
1545 lines
48 KiB
C++
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
#include "vm/disassembler.h"
|
|
|
|
#include "vm/globals.h" // Needed here to get TARGET_ARCH_ARM.
|
|
#if defined(TARGET_ARCH_ARM)
|
|
#include "platform/assert.h"
|
|
#include "vm/cpu.h"
|
|
#include "vm/instructions.h"
|
|
|
|
namespace dart {
|
|
|
|
#ifndef PRODUCT
|
|
|
|
class ARMDecoder : public ValueObject {
|
|
public:
|
|
ARMDecoder(char* buffer, size_t buffer_size)
|
|
: buffer_(buffer), buffer_size_(buffer_size), buffer_pos_(0) {
|
|
buffer_[buffer_pos_] = '\0';
|
|
}
|
|
|
|
~ARMDecoder() {}
|
|
|
|
// Writes one disassembled instruction into 'buffer' (0-terminated).
|
|
// Returns true if the instruction was successfully decoded, false otherwise.
|
|
void InstructionDecode(uword pc);
|
|
|
|
private:
|
|
// Bottleneck functions to print into the out_buffer.
|
|
void Print(const char* str);
|
|
|
|
// Printing of common values.
|
|
void PrintRegister(int reg);
|
|
void PrintSRegister(int reg);
|
|
void PrintDRegister(int reg);
|
|
void PrintDRegisterList(int start, int reg_count);
|
|
void PrintQRegister(int reg);
|
|
void PrintCondition(Instr* instr);
|
|
void PrintShiftRm(Instr* instr);
|
|
void PrintShiftImm(Instr* instr);
|
|
void PrintPU(Instr* instr);
|
|
|
|
// Handle formatting of instructions and their options.
|
|
int FormatRegister(Instr* instr, const char* option);
|
|
int FormatSRegister(Instr* instr, const char* option);
|
|
int FormatDRegister(Instr* instr, const char* option);
|
|
int FormatQRegister(Instr* instr, const char* option);
|
|
int FormatOption(Instr* instr, const char* option);
|
|
void Format(Instr* instr, const char* format);
|
|
void Unknown(Instr* instr);
|
|
|
|
// Each of these functions decodes one particular instruction type, a 3-bit
|
|
// field in the instruction encoding.
|
|
// Types 0 and 1 are combined as they are largely the same except for the way
|
|
// they interpret the shifter operand.
|
|
void DecodeType01(Instr* instr);
|
|
void DecodeType2(Instr* instr);
|
|
void DecodeType3(Instr* instr);
|
|
void DecodeType4(Instr* instr);
|
|
void DecodeType5(Instr* instr);
|
|
void DecodeType6(Instr* instr);
|
|
void DecodeType7(Instr* instr);
|
|
void DecodeSIMDDataProcessing(Instr* instr);
|
|
|
|
// Convenience functions.
|
|
char* get_buffer() const { return buffer_; }
|
|
char* current_position_in_buffer() { return buffer_ + buffer_pos_; }
|
|
size_t remaining_size_in_buffer() { return buffer_size_ - buffer_pos_; }
|
|
|
|
char* buffer_; // Decode instructions into this buffer.
|
|
size_t buffer_size_; // The size of the character buffer.
|
|
size_t buffer_pos_; // Current character position in buffer.
|
|
|
|
DISALLOW_ALLOCATION();
|
|
DISALLOW_COPY_AND_ASSIGN(ARMDecoder);
|
|
};
|
|
|
|
|
|
// Support for assertions in the ARMDecoder formatting functions.
|
|
#define STRING_STARTS_WITH(string, compare_string) \
|
|
(strncmp(string, compare_string, strlen(compare_string)) == 0)
|
|
|
|
|
|
// Append the str to the output buffer.
|
|
void ARMDecoder::Print(const char* str) {
|
|
char cur = *str++;
|
|
while (cur != '\0' && (buffer_pos_ < (buffer_size_ - 1))) {
|
|
buffer_[buffer_pos_++] = cur;
|
|
cur = *str++;
|
|
}
|
|
buffer_[buffer_pos_] = '\0';
|
|
}
|
|
|
|
|
|
// These condition names are defined in a way to match the native disassembler
|
|
// formatting. See for example the command "objdump -d <binary file>".
|
|
static const char* cond_names[kNumberOfConditions] = {
|
|
"eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc",
|
|
"hi", "ls", "ge", "lt", "gt", "le", "", "invalid",
|
|
};
|
|
|
|
|
|
// Print the condition guarding the instruction.
|
|
void ARMDecoder::PrintCondition(Instr* instr) {
|
|
Print(cond_names[instr->ConditionField()]);
|
|
}
|
|
|
|
|
|
// These register names are defined in a way to match the native disassembler
|
|
// formatting, except for register alias pp (r5).
|
|
// See for example the command "objdump -d <binary file>".
|
|
static const char* reg_names[kNumberOfCpuRegisters] = {
|
|
#if defined(TARGET_OS_MACOS) || defined(TARGET_OS_MACOS_IOS)
|
|
"r0", "r1", "r2", "r3", "r4", "pp", "r6", "fp",
|
|
"r8", "r9", "thr", "r11", "ip", "sp", "lr", "pc",
|
|
#else
|
|
"r0", "r1", "r2", "r3", "r4", "pp", "r6", "r7",
|
|
"r8", "r9", "thr", "fp", "ip", "sp", "lr", "pc",
|
|
#endif
|
|
};
|
|
|
|
|
|
// Print the register name according to the active name converter.
|
|
void ARMDecoder::PrintRegister(int reg) {
|
|
ASSERT(0 <= reg);
|
|
ASSERT(reg < kNumberOfCpuRegisters);
|
|
Print(reg_names[reg]);
|
|
}
|
|
|
|
|
|
void ARMDecoder::PrintSRegister(int reg) {
|
|
ASSERT(0 <= reg);
|
|
ASSERT(reg < kNumberOfSRegisters);
|
|
buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
|
|
remaining_size_in_buffer(), "s%d", reg);
|
|
}
|
|
|
|
|
|
void ARMDecoder::PrintDRegister(int reg) {
|
|
ASSERT(0 <= reg);
|
|
ASSERT(reg < kNumberOfDRegisters);
|
|
buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
|
|
remaining_size_in_buffer(), "d%d", reg);
|
|
}
|
|
|
|
|
|
void ARMDecoder::PrintQRegister(int reg) {
|
|
ASSERT(0 <= reg);
|
|
ASSERT(reg < kNumberOfQRegisters);
|
|
buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
|
|
remaining_size_in_buffer(), "q%d", reg);
|
|
}
|
|
|
|
|
|
// These shift names are defined in a way to match the native disassembler
|
|
// formatting. See for example the command "objdump -d <binary file>".
|
|
static const char* shift_names[kMaxShift] = {"lsl", "lsr", "asr", "ror"};
|
|
|
|
|
|
// Print the register shift operands for the instruction. Generally used for
|
|
// data processing instructions.
|
|
void ARMDecoder::PrintShiftRm(Instr* instr) {
|
|
Shift shift = instr->ShiftField();
|
|
int shift_amount = instr->ShiftAmountField();
|
|
int rm = instr->RmField();
|
|
|
|
PrintRegister(rm);
|
|
|
|
if ((instr->RegShiftField() == 0) && (shift == LSL) && (shift_amount == 0)) {
|
|
// Special case for using rm only.
|
|
return;
|
|
}
|
|
if (instr->RegShiftField() == 0) {
|
|
// by immediate
|
|
if ((shift == ROR) && (shift_amount == 0)) {
|
|
Print(", RRX");
|
|
return;
|
|
} else if (((shift == LSR) || (shift == ASR)) && (shift_amount == 0)) {
|
|
shift_amount = 32;
|
|
}
|
|
buffer_pos_ +=
|
|
OS::SNPrint(current_position_in_buffer(), remaining_size_in_buffer(),
|
|
", %s #%d", shift_names[shift], shift_amount);
|
|
} else {
|
|
// by register
|
|
int rs = instr->RsField();
|
|
buffer_pos_ +=
|
|
OS::SNPrint(current_position_in_buffer(), remaining_size_in_buffer(),
|
|
", %s ", shift_names[shift]);
|
|
PrintRegister(rs);
|
|
}
|
|
}
|
|
|
|
|
|
// Print the immediate operand for the instruction. Generally used for data
|
|
// processing instructions.
|
|
void ARMDecoder::PrintShiftImm(Instr* instr) {
|
|
int rotate = instr->RotateField() * 2;
|
|
int immed8 = instr->Immed8Field();
|
|
int imm = (immed8 >> rotate) | (immed8 << (32 - rotate));
|
|
buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
|
|
remaining_size_in_buffer(), "#%d", imm);
|
|
}
|
|
|
|
|
|
// Print PU formatting to reduce complexity of FormatOption.
|
|
void ARMDecoder::PrintPU(Instr* instr) {
|
|
switch (instr->PUField()) {
|
|
case 0: {
|
|
Print("da");
|
|
break;
|
|
}
|
|
case 1: {
|
|
Print("ia");
|
|
break;
|
|
}
|
|
case 2: {
|
|
Print("db");
|
|
break;
|
|
}
|
|
case 3: {
|
|
Print("ib");
|
|
break;
|
|
}
|
|
default: {
|
|
UNREACHABLE();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Handle all register based formatting in these functions to reduce the
|
|
// complexity of FormatOption.
|
|
int ARMDecoder::FormatRegister(Instr* instr, const char* format) {
|
|
ASSERT(format[0] == 'r');
|
|
if (format[1] == 'n') { // 'rn: Rn register
|
|
int reg = instr->RnField();
|
|
PrintRegister(reg);
|
|
return 2;
|
|
} else if (format[1] == 'd') { // 'rd: Rd register
|
|
int reg = instr->RdField();
|
|
PrintRegister(reg);
|
|
if (format[2] == '2') { // 'rd2: possibly Rd, Rd+1 register pair
|
|
if (instr->HasSign() && !instr->HasL()) {
|
|
if ((reg % 2) != 0) {
|
|
Print(" *** unknown (odd register pair) ***");
|
|
} else {
|
|
Print(", ");
|
|
PrintRegister(reg + 1);
|
|
}
|
|
}
|
|
return 3;
|
|
}
|
|
return 2;
|
|
} else if (format[1] == 's') { // 'rs: Rs register
|
|
int reg = instr->RsField();
|
|
PrintRegister(reg);
|
|
return 2;
|
|
} else if (format[1] == 'm') { // 'rm: Rm register
|
|
int reg = instr->RmField();
|
|
PrintRegister(reg);
|
|
return 2;
|
|
} else if (format[1] == 'l') {
|
|
// 'rlist: register list for load and store multiple instructions
|
|
ASSERT(STRING_STARTS_WITH(format, "rlist"));
|
|
int rlist = instr->RlistField();
|
|
int reg = 0;
|
|
Print("{");
|
|
// Print register list in ascending order, by scanning the bit mask.
|
|
while (rlist != 0) {
|
|
if ((rlist & 1) != 0) {
|
|
PrintRegister(reg);
|
|
if ((rlist >> 1) != 0) {
|
|
Print(", ");
|
|
}
|
|
}
|
|
reg++;
|
|
rlist >>= 1;
|
|
}
|
|
Print("}");
|
|
return 5;
|
|
}
|
|
UNREACHABLE();
|
|
return -1;
|
|
}
|
|
|
|
|
|
int ARMDecoder::FormatSRegister(Instr* instr, const char* format) {
|
|
ASSERT(format[0] == 's');
|
|
if (format[1] == 'n') { // 'sn: Sn register
|
|
int reg = instr->SnField();
|
|
PrintSRegister(reg);
|
|
return 2;
|
|
} else if (format[1] == 'd') { // 'sd: Sd register
|
|
int reg = instr->SdField();
|
|
PrintSRegister(reg);
|
|
return 2;
|
|
} else if (format[1] == 'm') {
|
|
int reg = instr->SmField();
|
|
if (format[2] == '1') { // 'sm1: S[m+1] register
|
|
reg++;
|
|
ASSERT(reg < kNumberOfSRegisters);
|
|
PrintSRegister(reg);
|
|
return 3;
|
|
} else { // 'sm: Sm register
|
|
PrintSRegister(reg);
|
|
return 2;
|
|
}
|
|
} else if (format[1] == 'l') {
|
|
ASSERT(STRING_STARTS_WITH(format, "slist"));
|
|
int reg_count = instr->Bits(0, 8);
|
|
int start = instr->Bit(22) | (instr->Bits(12, 4) << 1);
|
|
Print("{");
|
|
for (int i = start; i < start + reg_count; i++) {
|
|
PrintSRegister(i);
|
|
if (i != start + reg_count - 1) {
|
|
Print(", ");
|
|
}
|
|
}
|
|
Print("}");
|
|
return 5;
|
|
}
|
|
UNREACHABLE();
|
|
return -1;
|
|
}
|
|
|
|
|
|
void ARMDecoder::PrintDRegisterList(int start, int reg_count) {
|
|
Print("{");
|
|
for (int i = start; i < start + reg_count; i++) {
|
|
PrintDRegister(i);
|
|
if (i != start + reg_count - 1) {
|
|
Print(", ");
|
|
}
|
|
}
|
|
Print("}");
|
|
}
|
|
|
|
|
|
int ARMDecoder::FormatDRegister(Instr* instr, const char* format) {
|
|
ASSERT(format[0] == 'd');
|
|
if (format[1] == 'n') { // 'dn: Dn register
|
|
int reg = instr->DnField();
|
|
PrintDRegister(reg);
|
|
return 2;
|
|
} else if (format[1] == 'd') { // 'dd: Dd register
|
|
int reg = instr->DdField();
|
|
PrintDRegister(reg);
|
|
return 2;
|
|
} else if (format[1] == 'm') { // 'dm: Dm register
|
|
int reg = instr->DmField();
|
|
PrintDRegister(reg);
|
|
return 2;
|
|
} else if (format[1] == 'l') {
|
|
ASSERT(STRING_STARTS_WITH(format, "dlist"));
|
|
int reg_count = instr->Bits(0, 8) >> 1;
|
|
int start = (instr->Bit(22) << 4) | instr->Bits(12, 4);
|
|
PrintDRegisterList(start, reg_count);
|
|
return 5;
|
|
} else if (format[1] == 't') {
|
|
ASSERT(STRING_STARTS_WITH(format, "dtbllist"));
|
|
int reg_count = instr->Bits(8, 2) + 1;
|
|
int start = (instr->Bit(7) << 4) | instr->Bits(16, 4);
|
|
PrintDRegisterList(start, reg_count);
|
|
return 8;
|
|
}
|
|
UNREACHABLE();
|
|
return -1;
|
|
}
|
|
|
|
|
|
int ARMDecoder::FormatQRegister(Instr* instr, const char* format) {
|
|
ASSERT(format[0] == 'q');
|
|
if (format[1] == 'n') { // 'qn: Qn register
|
|
int reg = instr->QnField();
|
|
PrintQRegister(reg);
|
|
return 2;
|
|
} else if (format[1] == 'd') { // 'qd: Qd register
|
|
int reg = instr->QdField();
|
|
PrintQRegister(reg);
|
|
return 2;
|
|
} else if (format[1] == 'm') { // 'qm: Qm register
|
|
int reg = instr->QmField();
|
|
PrintQRegister(reg);
|
|
return 2;
|
|
}
|
|
UNREACHABLE();
|
|
return -1;
|
|
}
|
|
|
|
|
|
// FormatOption takes a formatting string and interprets it based on
|
|
// the current instructions. The format string points to the first
|
|
// character of the option string (the option escape has already been
|
|
// consumed by the caller.) FormatOption returns the number of
|
|
// characters that were consumed from the formatting string.
|
|
int ARMDecoder::FormatOption(Instr* instr, const char* format) {
|
|
switch (format[0]) {
|
|
case 'a': { // 'a: accumulate multiplies
|
|
if (instr->Bit(21) == 0) {
|
|
Print("ul");
|
|
} else {
|
|
Print("la");
|
|
}
|
|
return 1;
|
|
}
|
|
case 'b': { // 'b: byte loads or stores
|
|
if (instr->HasB()) {
|
|
Print("b");
|
|
}
|
|
return 1;
|
|
}
|
|
case 'c': { // 'cond: conditional execution
|
|
ASSERT(STRING_STARTS_WITH(format, "cond"));
|
|
PrintCondition(instr);
|
|
return 4;
|
|
}
|
|
case 'd': {
|
|
if (format[1] == 'e') { // 'dest: branch destination
|
|
ASSERT(STRING_STARTS_WITH(format, "dest"));
|
|
int off = (instr->SImmed24Field() << 2) + 8;
|
|
uword destination = reinterpret_cast<uword>(instr) + off;
|
|
buffer_pos_ +=
|
|
OS::SNPrint(current_position_in_buffer(),
|
|
remaining_size_in_buffer(), "%#" Px "", destination);
|
|
return 4;
|
|
} else {
|
|
return FormatDRegister(instr, format);
|
|
}
|
|
}
|
|
case 'q': {
|
|
return FormatQRegister(instr, format);
|
|
}
|
|
case 'i': { // 'imm12_4, imm4_12, immf, or immd
|
|
uint16_t immed16;
|
|
if (format[3] == 'f') {
|
|
ASSERT(STRING_STARTS_WITH(format, "immf"));
|
|
buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
|
|
remaining_size_in_buffer(), "%f",
|
|
instr->ImmFloatField());
|
|
return 4;
|
|
} else if (format[3] == 'd') {
|
|
ASSERT(STRING_STARTS_WITH(format, "immd"));
|
|
buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
|
|
remaining_size_in_buffer(), "%g",
|
|
instr->ImmDoubleField());
|
|
return 4;
|
|
} else if (format[3] == '1') {
|
|
ASSERT(STRING_STARTS_WITH(format, "imm12_4"));
|
|
immed16 = instr->BkptField();
|
|
} else {
|
|
ASSERT(format[3] == '4');
|
|
if (format[5] == 'v') {
|
|
ASSERT(STRING_STARTS_WITH(format, "imm4_vdup"));
|
|
int32_t idx = -1;
|
|
int32_t imm4 = instr->Bits(16, 4);
|
|
if ((imm4 & 1) != 0)
|
|
idx = imm4 >> 1;
|
|
else if ((imm4 & 2) != 0)
|
|
idx = imm4 >> 2;
|
|
else if ((imm4 & 4) != 0)
|
|
idx = imm4 >> 3;
|
|
buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
|
|
remaining_size_in_buffer(), "%d", idx);
|
|
return 9;
|
|
} else {
|
|
ASSERT(STRING_STARTS_WITH(format, "imm4_12"));
|
|
immed16 = instr->MovwField();
|
|
}
|
|
}
|
|
buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
|
|
remaining_size_in_buffer(), "0x%x", immed16);
|
|
return 7;
|
|
}
|
|
case 'l': { // 'l: branch and link
|
|
if (instr->HasLink()) {
|
|
Print("l");
|
|
}
|
|
return 1;
|
|
}
|
|
case 'm': { // 'memop: load/store instructions
|
|
ASSERT(STRING_STARTS_WITH(format, "memop"));
|
|
if (instr->HasL() ||
|
|
// Extra load/store instructions.
|
|
((instr->TypeField() == 0) && instr->HasSign() && !instr->HasH())) {
|
|
Print("ldr");
|
|
} else {
|
|
Print("str");
|
|
}
|
|
return 5;
|
|
}
|
|
case 'o': {
|
|
if (format[3] == '1') {
|
|
if (format[4] == '0') {
|
|
// 'off10: 10-bit offset for VFP load and store instructions
|
|
buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
|
|
remaining_size_in_buffer(), "%d",
|
|
instr->Bits(0, 8) << 2);
|
|
} else {
|
|
// 'off12: 12-bit offset for load and store instructions.
|
|
ASSERT(STRING_STARTS_WITH(format, "off12"));
|
|
buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
|
|
remaining_size_in_buffer(), "%d",
|
|
instr->Offset12Field());
|
|
}
|
|
return 5;
|
|
}
|
|
// 'off8: 8-bit offset for extra load and store instructions.
|
|
ASSERT(STRING_STARTS_WITH(format, "off8"));
|
|
int offs8 = (instr->ImmedHField() << 4) | instr->ImmedLField();
|
|
buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
|
|
remaining_size_in_buffer(), "%d", offs8);
|
|
return 4;
|
|
}
|
|
case 'p': { // 'pu: P and U bits for load and store instructions.
|
|
ASSERT(STRING_STARTS_WITH(format, "pu"));
|
|
PrintPU(instr);
|
|
return 2;
|
|
}
|
|
case 'r': {
|
|
return FormatRegister(instr, format);
|
|
}
|
|
case 's': {
|
|
if (format[1] == 'h') { // 'shift_op or 'shift_rm
|
|
if (format[6] == 'o') { // 'shift_op
|
|
ASSERT(STRING_STARTS_WITH(format, "shift_op"));
|
|
if (instr->TypeField() == 0) {
|
|
PrintShiftRm(instr);
|
|
} else {
|
|
ASSERT(instr->TypeField() == 1);
|
|
PrintShiftImm(instr);
|
|
}
|
|
return 8;
|
|
} else { // 'shift_rm
|
|
ASSERT(STRING_STARTS_WITH(format, "shift_rm"));
|
|
PrintShiftRm(instr);
|
|
return 8;
|
|
}
|
|
} else if (format[1] == 'v') { // 'svc
|
|
ASSERT(STRING_STARTS_WITH(format, "svc"));
|
|
buffer_pos_ +=
|
|
OS::SNPrint(current_position_in_buffer(),
|
|
remaining_size_in_buffer(), "0x%x", instr->SvcField());
|
|
return 3;
|
|
} else if (format[1] == 'z') {
|
|
// 'sz: Size field of SIMD instructions.
|
|
int sz = instr->Bits(20, 2);
|
|
char const* sz_str;
|
|
switch (sz) {
|
|
case 0:
|
|
sz_str = "b";
|
|
break;
|
|
case 1:
|
|
sz_str = "h";
|
|
break;
|
|
case 2:
|
|
sz_str = "w";
|
|
break;
|
|
case 3:
|
|
sz_str = "l";
|
|
break;
|
|
default:
|
|
sz_str = "?";
|
|
break;
|
|
}
|
|
buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
|
|
remaining_size_in_buffer(), "%s", sz_str);
|
|
return 2;
|
|
} else if (format[1] == ' ') {
|
|
// 's: S field of data processing instructions.
|
|
if (instr->HasS()) {
|
|
Print("s");
|
|
}
|
|
return 1;
|
|
} else {
|
|
return FormatSRegister(instr, format);
|
|
}
|
|
}
|
|
case 't': { // 'target: target of branch instructions.
|
|
ASSERT(STRING_STARTS_WITH(format, "target"));
|
|
int off = (instr->SImmed24Field() << 2) + 8;
|
|
buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
|
|
remaining_size_in_buffer(), "%+d", off);
|
|
return 6;
|
|
}
|
|
case 'u': { // 'u: signed or unsigned multiplies.
|
|
if (instr->Bit(22) == 0) {
|
|
Print("u");
|
|
} else {
|
|
Print("s");
|
|
}
|
|
return 1;
|
|
}
|
|
case 'w': { // 'w: W field of load and store instructions.
|
|
if (instr->HasW()) {
|
|
Print("!");
|
|
}
|
|
return 1;
|
|
}
|
|
case 'x': { // 'x: type of extra load/store instructions.
|
|
if (!instr->HasSign()) {
|
|
Print("h");
|
|
} else if (instr->HasL()) {
|
|
if (instr->HasH()) {
|
|
Print("sh");
|
|
} else {
|
|
Print("sb");
|
|
}
|
|
} else {
|
|
Print("d");
|
|
}
|
|
return 1;
|
|
}
|
|
default: {
|
|
UNREACHABLE();
|
|
break;
|
|
}
|
|
}
|
|
UNREACHABLE();
|
|
return -1;
|
|
}
|
|
|
|
|
|
// Format takes a formatting string for a whole instruction and prints it into
|
|
// the output buffer. All escaped options are handed to FormatOption to be
|
|
// parsed further.
|
|
void ARMDecoder::Format(Instr* instr, const char* format) {
|
|
char cur = *format++;
|
|
while ((cur != 0) && (buffer_pos_ < (buffer_size_ - 1))) {
|
|
if (cur == '\'') { // Single quote is used as the formatting escape.
|
|
format += FormatOption(instr, format);
|
|
} else {
|
|
buffer_[buffer_pos_++] = cur;
|
|
}
|
|
cur = *format++;
|
|
}
|
|
buffer_[buffer_pos_] = '\0';
|
|
}
|
|
|
|
|
|
// For currently unimplemented decodings the disassembler calls Unknown(instr)
|
|
// which will just print "unknown" of the instruction bits.
|
|
void ARMDecoder::Unknown(Instr* instr) {
|
|
Format(instr, "unknown");
|
|
}
|
|
|
|
|
|
void ARMDecoder::DecodeType01(Instr* instr) {
|
|
if (!instr->IsDataProcessing()) {
|
|
// miscellaneous, multiply, sync primitives, extra loads and stores.
|
|
if (instr->IsMiscellaneous()) {
|
|
switch (instr->Bits(4, 3)) {
|
|
case 1: {
|
|
if (instr->Bits(21, 2) == 0x3) {
|
|
Format(instr, "clz'cond 'rd, 'rm");
|
|
} else if (instr->Bits(21, 2) == 0x1) {
|
|
Format(instr, "bx'cond 'rm");
|
|
} else {
|
|
Unknown(instr);
|
|
}
|
|
break;
|
|
}
|
|
case 3: {
|
|
if (instr->Bits(21, 2) == 0x1) {
|
|
Format(instr, "blx'cond 'rm");
|
|
} else {
|
|
// Could be inlined constant.
|
|
Unknown(instr);
|
|
}
|
|
break;
|
|
}
|
|
case 7: {
|
|
if ((instr->Bits(21, 2) == 0x1) && (instr->ConditionField() == AL)) {
|
|
Format(instr, "bkpt #'imm12_4");
|
|
if (instr->BkptField() == Instr::kStopMessageCode) {
|
|
const char* message = *reinterpret_cast<const char**>(
|
|
reinterpret_cast<intptr_t>(instr) - Instr::kInstrSize);
|
|
buffer_pos_ +=
|
|
OS::SNPrint(current_position_in_buffer(),
|
|
remaining_size_in_buffer(), " ; \"%s\"", message);
|
|
}
|
|
} else {
|
|
// Format(instr, "smc'cond");
|
|
Unknown(instr); // Not used.
|
|
}
|
|
break;
|
|
}
|
|
default: {
|
|
Unknown(instr); // Not used.
|
|
break;
|
|
}
|
|
}
|
|
} else if (instr->IsMultiplyOrSyncPrimitive()) {
|
|
if (instr->Bit(24) == 0) {
|
|
// multiply instructions
|
|
switch (instr->Bits(21, 3)) {
|
|
case 0: {
|
|
// Assembler registers rd, rn, rm are encoded as rn, rm, rs.
|
|
Format(instr, "mul'cond's 'rn, 'rm, 'rs");
|
|
break;
|
|
}
|
|
case 1: {
|
|
// Assembler registers rd, rn, rm, ra are encoded as rn, rm, rs, rd.
|
|
Format(instr, "mla'cond's 'rn, 'rm, 'rs, 'rd");
|
|
break;
|
|
}
|
|
case 2: {
|
|
if (TargetCPUFeatures::arm_version() == ARMv5TE) {
|
|
Unknown(instr);
|
|
return;
|
|
}
|
|
// Registers rd_lo, rd_hi, rn, rm are encoded as rd, rn, rm, rs.
|
|
Format(instr, "umaal'cond's 'rd, 'rn, 'rm, 'rs");
|
|
break;
|
|
}
|
|
case 3: {
|
|
if (TargetCPUFeatures::arm_version() != ARMv7) {
|
|
Unknown(instr);
|
|
return;
|
|
}
|
|
// Assembler registers rd, rn, rm, ra are encoded as rn, rm, rs, rd.
|
|
Format(instr, "mls'cond's 'rn, 'rm, 'rs, 'rd");
|
|
break;
|
|
}
|
|
case 4: {
|
|
// Registers rd_lo, rd_hi, rn, rm are encoded as rd, rn, rm, rs.
|
|
Format(instr, "umull'cond's 'rd, 'rn, 'rm, 'rs");
|
|
break;
|
|
}
|
|
case 5: {
|
|
// Registers rd_lo, rd_hi, rn, rm are encoded as rd, rn, rm, rs.
|
|
Format(instr, "umlal'cond's 'rd, 'rn, 'rm, 'rs");
|
|
break;
|
|
}
|
|
case 6: {
|
|
// Registers rd_lo, rd_hi, rn, rm are encoded as rd, rn, rm, rs.
|
|
Format(instr, "smull'cond's 'rd, 'rn, 'rm, 'rs");
|
|
break;
|
|
}
|
|
default: {
|
|
Unknown(instr); // Not used.
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
if (TargetCPUFeatures::arm_version() == ARMv5TE) {
|
|
// strex and ldrex are only supported after ARMv6.
|
|
Unknown(instr);
|
|
return;
|
|
}
|
|
// synchronization primitives
|
|
switch (instr->Bits(20, 4)) {
|
|
case 8: {
|
|
Format(instr, "strex'cond 'rd, 'rm, ['rn]");
|
|
break;
|
|
}
|
|
case 9: {
|
|
Format(instr, "ldrex'cond 'rd, ['rn]");
|
|
break;
|
|
}
|
|
default: {
|
|
Unknown(instr); // Not used.
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else if (instr->Bit(25) == 1) {
|
|
// 16-bit immediate loads, msr (immediate), and hints
|
|
switch (instr->Bits(20, 5)) {
|
|
case 16: {
|
|
if (TargetCPUFeatures::arm_version() == ARMv7) {
|
|
Format(instr, "movw'cond 'rd, #'imm4_12");
|
|
} else {
|
|
Unknown(instr);
|
|
}
|
|
break;
|
|
}
|
|
case 18: {
|
|
if ((instr->Bits(16, 4) == 0) && (instr->Bits(0, 8) == 0)) {
|
|
Format(instr, "nop'cond");
|
|
} else {
|
|
Unknown(instr); // Not used.
|
|
}
|
|
break;
|
|
}
|
|
case 20: {
|
|
if (TargetCPUFeatures::arm_version() == ARMv7) {
|
|
Format(instr, "movt'cond 'rd, #'imm4_12");
|
|
} else {
|
|
Unknown(instr);
|
|
}
|
|
break;
|
|
}
|
|
default: {
|
|
Unknown(instr); // Not used.
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
// extra load/store instructions
|
|
switch (instr->PUField()) {
|
|
case 0: {
|
|
if (instr->Bit(22) == 0) {
|
|
Format(instr, "'memop'cond'x 'rd2, ['rn], -'rm");
|
|
} else {
|
|
Format(instr, "'memop'cond'x 'rd2, ['rn], #-'off8");
|
|
}
|
|
break;
|
|
}
|
|
case 1: {
|
|
if (instr->Bit(22) == 0) {
|
|
Format(instr, "'memop'cond'x 'rd2, ['rn], +'rm");
|
|
} else {
|
|
Format(instr, "'memop'cond'x 'rd2, ['rn], #+'off8");
|
|
}
|
|
break;
|
|
}
|
|
case 2: {
|
|
if (instr->Bit(22) == 0) {
|
|
Format(instr, "'memop'cond'x 'rd2, ['rn, -'rm]'w");
|
|
} else {
|
|
Format(instr, "'memop'cond'x 'rd2, ['rn, #-'off8]'w");
|
|
}
|
|
break;
|
|
}
|
|
case 3: {
|
|
if (instr->Bit(22) == 0) {
|
|
Format(instr, "'memop'cond'x 'rd2, ['rn, +'rm]'w");
|
|
} else {
|
|
Format(instr, "'memop'cond'x 'rd2, ['rn, #+'off8]'w");
|
|
}
|
|
break;
|
|
}
|
|
default: {
|
|
// The PU field is a 2-bit field.
|
|
UNREACHABLE();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
switch (instr->OpcodeField()) {
|
|
case AND: {
|
|
Format(instr, "and'cond's 'rd, 'rn, 'shift_op");
|
|
break;
|
|
}
|
|
case EOR: {
|
|
Format(instr, "eor'cond's 'rd, 'rn, 'shift_op");
|
|
break;
|
|
}
|
|
case SUB: {
|
|
Format(instr, "sub'cond's 'rd, 'rn, 'shift_op");
|
|
break;
|
|
}
|
|
case RSB: {
|
|
Format(instr, "rsb'cond's 'rd, 'rn, 'shift_op");
|
|
break;
|
|
}
|
|
case ADD: {
|
|
Format(instr, "add'cond's 'rd, 'rn, 'shift_op");
|
|
break;
|
|
}
|
|
case ADC: {
|
|
Format(instr, "adc'cond's 'rd, 'rn, 'shift_op");
|
|
break;
|
|
}
|
|
case SBC: {
|
|
Format(instr, "sbc'cond's 'rd, 'rn, 'shift_op");
|
|
break;
|
|
}
|
|
case RSC: {
|
|
Format(instr, "rsc'cond's 'rd, 'rn, 'shift_op");
|
|
break;
|
|
}
|
|
case TST: {
|
|
if (instr->HasS()) {
|
|
Format(instr, "tst'cond 'rn, 'shift_op");
|
|
} else {
|
|
Unknown(instr); // Not used.
|
|
}
|
|
break;
|
|
}
|
|
case TEQ: {
|
|
if (instr->HasS()) {
|
|
Format(instr, "teq'cond 'rn, 'shift_op");
|
|
} else {
|
|
Unknown(instr); // Not used.
|
|
}
|
|
break;
|
|
}
|
|
case CMP: {
|
|
if (instr->HasS()) {
|
|
Format(instr, "cmp'cond 'rn, 'shift_op");
|
|
} else {
|
|
Unknown(instr); // Not used.
|
|
}
|
|
break;
|
|
}
|
|
case CMN: {
|
|
if (instr->HasS()) {
|
|
Format(instr, "cmn'cond 'rn, 'shift_op");
|
|
} else {
|
|
Unknown(instr); // Not used.
|
|
}
|
|
break;
|
|
}
|
|
case ORR: {
|
|
Format(instr, "orr'cond's 'rd, 'rn, 'shift_op");
|
|
break;
|
|
}
|
|
case MOV: {
|
|
Format(instr, "mov'cond's 'rd, 'shift_op");
|
|
break;
|
|
}
|
|
case BIC: {
|
|
Format(instr, "bic'cond's 'rd, 'rn, 'shift_op");
|
|
break;
|
|
}
|
|
case MVN: {
|
|
Format(instr, "mvn'cond's 'rd, 'shift_op");
|
|
break;
|
|
}
|
|
default: {
|
|
// The Opcode field is a 4-bit field.
|
|
UNREACHABLE();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void ARMDecoder::DecodeType2(Instr* instr) {
|
|
switch (instr->PUField()) {
|
|
case 0: {
|
|
if (instr->HasW()) {
|
|
Unknown(instr); // Not used.
|
|
} else {
|
|
Format(instr, "'memop'cond'b 'rd, ['rn], #-'off12");
|
|
}
|
|
break;
|
|
}
|
|
case 1: {
|
|
if (instr->HasW()) {
|
|
Unknown(instr); // Not used.
|
|
} else {
|
|
Format(instr, "'memop'cond'b 'rd, ['rn], #+'off12");
|
|
}
|
|
break;
|
|
}
|
|
case 2: {
|
|
Format(instr, "'memop'cond'b 'rd, ['rn, #-'off12]'w");
|
|
break;
|
|
}
|
|
case 3: {
|
|
Format(instr, "'memop'cond'b 'rd, ['rn, #+'off12]'w");
|
|
break;
|
|
}
|
|
default: {
|
|
// The PU field is a 2-bit field.
|
|
UNREACHABLE();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void ARMDecoder::DecodeType3(Instr* instr) {
|
|
if (instr->IsDivision()) {
|
|
if (!TargetCPUFeatures::integer_division_supported()) {
|
|
Unknown(instr);
|
|
return;
|
|
}
|
|
if (instr->Bit(21)) {
|
|
Format(instr, "udiv'cond 'rn, 'rs, 'rm");
|
|
} else {
|
|
Format(instr, "sdiv'cond 'rn, 'rs, 'rm");
|
|
}
|
|
return;
|
|
}
|
|
switch (instr->PUField()) {
|
|
case 0: {
|
|
if (instr->HasW()) {
|
|
Unknown(instr);
|
|
} else {
|
|
Format(instr, "'memop'cond'b 'rd, ['rn], -'shift_rm");
|
|
}
|
|
break;
|
|
}
|
|
case 1: {
|
|
if (instr->HasW()) {
|
|
Unknown(instr);
|
|
} else {
|
|
Format(instr, "'memop'cond'b 'rd, ['rn], +'shift_rm");
|
|
}
|
|
break;
|
|
}
|
|
case 2: {
|
|
Format(instr, "'memop'cond'b 'rd, ['rn, -'shift_rm]'w");
|
|
break;
|
|
}
|
|
case 3: {
|
|
Format(instr, "'memop'cond'b 'rd, ['rn, +'shift_rm]'w");
|
|
break;
|
|
}
|
|
default: {
|
|
// The PU field is a 2-bit field.
|
|
UNREACHABLE();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void ARMDecoder::DecodeType4(Instr* instr) {
|
|
if (instr->Bit(22) == 1) {
|
|
Unknown(instr); // Privileged mode currently not supported.
|
|
} else if (instr->HasL()) {
|
|
Format(instr, "ldm'cond'pu 'rn'w, 'rlist");
|
|
} else {
|
|
Format(instr, "stm'cond'pu 'rn'w, 'rlist");
|
|
}
|
|
}
|
|
|
|
|
|
void ARMDecoder::DecodeType5(Instr* instr) {
|
|
Format(instr, "b'l'cond 'target ; 'dest");
|
|
}
|
|
|
|
|
|
void ARMDecoder::DecodeType6(Instr* instr) {
|
|
if (instr->IsVFPDoubleTransfer()) {
|
|
if (instr->Bit(8) == 0) {
|
|
if (instr->Bit(20) == 1) {
|
|
Format(instr, "vmovrrs'cond 'rd, 'rn, {'sm, 'sm1}");
|
|
} else {
|
|
Format(instr, "vmovsrr'cond {'sm, 'sm1}, 'rd, 'rn");
|
|
}
|
|
} else {
|
|
if (instr->Bit(20) == 1) {
|
|
Format(instr, "vmovrrd'cond 'rd, 'rn, 'dm");
|
|
} else {
|
|
Format(instr, "vmovdrr'cond 'dm, 'rd, 'rn");
|
|
}
|
|
}
|
|
} else if (instr->IsVFPLoadStore()) {
|
|
if (instr->Bit(8) == 0) {
|
|
if (instr->Bit(20) == 1) { // vldrs
|
|
if (instr->Bit(23) == 1) {
|
|
Format(instr, "vldrs'cond 'sd, ['rn, #+'off10]");
|
|
} else {
|
|
Format(instr, "vldrs'cond 'sd, ['rn, #-'off10]");
|
|
}
|
|
} else { // vstrs
|
|
if (instr->Bit(23) == 1) {
|
|
Format(instr, "vstrs'cond 'sd, ['rn, #+'off10]");
|
|
} else {
|
|
Format(instr, "vstrs'cond 'sd, ['rn, #-'off10]");
|
|
}
|
|
}
|
|
} else {
|
|
if (instr->Bit(20) == 1) { // vldrd
|
|
if (instr->Bit(23) == 1) {
|
|
Format(instr, "vldrd'cond 'dd, ['rn, #+'off10]");
|
|
} else {
|
|
Format(instr, "vldrd'cond 'dd, ['rn, #-'off10]");
|
|
}
|
|
} else { // vstrd
|
|
if (instr->Bit(23) == 1) {
|
|
Format(instr, "vstrd'cond 'dd, ['rn, #+'off10]");
|
|
} else {
|
|
Format(instr, "vstrd'cond 'dd, ['rn, #-'off10]");
|
|
}
|
|
}
|
|
}
|
|
} else if (instr->IsVFPMultipleLoadStore()) {
|
|
if (instr->HasL()) { // vldm
|
|
if (instr->Bit(8)) { // vldmd
|
|
Format(instr, "vldmd'cond'pu 'rn'w, 'dlist");
|
|
} else { // vldms
|
|
Format(instr, "vldms'cond'pu 'rn'w, 'slist");
|
|
}
|
|
} else { // vstm
|
|
if (instr->Bit(8)) { // vstmd
|
|
Format(instr, "vstmd'cond'pu 'rn'w, 'dlist");
|
|
} else { // vstms
|
|
Format(instr, "vstms'cond'pu 'rn'w, 'slist");
|
|
}
|
|
}
|
|
} else {
|
|
Unknown(instr);
|
|
}
|
|
}
|
|
|
|
|
|
void ARMDecoder::DecodeType7(Instr* instr) {
|
|
if (instr->Bit(24) == 1) {
|
|
Format(instr, "svc'cond #'svc");
|
|
} else if (instr->IsVFPDataProcessingOrSingleTransfer()) {
|
|
if (instr->Bit(4) == 0) {
|
|
// VFP Data Processing
|
|
switch (instr->Bits(20, 4) & 0xb) {
|
|
case 0: { // vmla, vmls floating-point
|
|
if (instr->Bit(8) == 0) {
|
|
if (instr->Bit(6) == 0) {
|
|
Format(instr, "vmlas'cond 'sd, 'sn, 'sm");
|
|
} else {
|
|
Format(instr, "vmlss'cond 'sd, 'sn, 'sm");
|
|
}
|
|
} else {
|
|
if (instr->Bit(6) == 0) {
|
|
Format(instr, "vmlad'cond 'dd, 'dn, 'dm");
|
|
} else {
|
|
Format(instr, "vmlsd'cond 'dd, 'dn, 'dm");
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case 1: // vnmla, vnmls, vnmul
|
|
default: {
|
|
Unknown(instr);
|
|
break;
|
|
}
|
|
case 2: { // vmul
|
|
if (instr->Bit(8) == 0) {
|
|
Format(instr, "vmuls'cond 'sd, 'sn, 'sm");
|
|
} else {
|
|
Format(instr, "vmuld'cond 'dd, 'dn, 'dm");
|
|
}
|
|
break;
|
|
}
|
|
case 8: { // vdiv
|
|
if (instr->Bit(8) == 0) {
|
|
Format(instr, "vdivs'cond 'sd, 'sn, 'sm");
|
|
} else {
|
|
Format(instr, "vdivd'cond 'dd, 'dn, 'dm");
|
|
}
|
|
break;
|
|
}
|
|
case 3: { // vadd, vsub floating-point
|
|
if (instr->Bit(8) == 0) {
|
|
if (instr->Bit(6) == 0) {
|
|
Format(instr, "vadds'cond 'sd, 'sn, 'sm");
|
|
} else {
|
|
Format(instr, "vsubs'cond 'sd, 'sn, 'sm");
|
|
}
|
|
} else {
|
|
if (instr->Bit(6) == 0) {
|
|
Format(instr, "vaddd'cond 'dd, 'dn, 'dm");
|
|
} else {
|
|
Format(instr, "vsubd'cond 'dd, 'dn, 'dm");
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case 0xb: { // Other VFP data-processing instructions
|
|
if (instr->Bit(6) == 0) { // vmov immediate
|
|
if (instr->Bit(8) == 0) {
|
|
Format(instr, "vmovs'cond 'sd, #'immf");
|
|
} else {
|
|
Format(instr, "vmovd'cond 'dd, #'immd");
|
|
}
|
|
break;
|
|
}
|
|
switch (instr->Bits(16, 4)) {
|
|
case 0: { // vmov register, vabs
|
|
switch (instr->Bits(6, 2)) {
|
|
case 1: { // vmov register
|
|
if (instr->Bit(8) == 0) {
|
|
Format(instr, "vmovs'cond 'sd, 'sm");
|
|
} else {
|
|
Format(instr, "vmovd'cond 'dd, 'dm");
|
|
}
|
|
break;
|
|
}
|
|
case 3: { // vabs
|
|
if (instr->Bit(8) == 0) {
|
|
Format(instr, "vabss'cond 'sd, 'sm");
|
|
} else {
|
|
Format(instr, "vabsd'cond 'dd, 'dm");
|
|
}
|
|
break;
|
|
}
|
|
default: {
|
|
Unknown(instr);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case 1: { // vneg, vsqrt
|
|
switch (instr->Bits(6, 2)) {
|
|
case 1: { // vneg
|
|
if (instr->Bit(8) == 0) {
|
|
Format(instr, "vnegs'cond 'sd, 'sm");
|
|
} else {
|
|
Format(instr, "vnegd'cond 'dd, 'dm");
|
|
}
|
|
break;
|
|
}
|
|
case 3: { // vsqrt
|
|
if (instr->Bit(8) == 0) {
|
|
Format(instr, "vsqrts'cond 'sd, 'sm");
|
|
} else {
|
|
Format(instr, "vsqrtd'cond 'dd, 'dm");
|
|
}
|
|
break;
|
|
}
|
|
default: {
|
|
Unknown(instr);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case 4: // vcmp, vcmpe
|
|
case 5: { // vcmp #0.0, vcmpe #0.0
|
|
if (instr->Bit(7) == 1) { // vcmpe
|
|
Unknown(instr);
|
|
} else {
|
|
if (instr->Bit(8) == 0) { // vcmps
|
|
if (instr->Bit(16) == 0) {
|
|
Format(instr, "vcmps'cond 'sd, 'sm");
|
|
} else {
|
|
Format(instr, "vcmps'cond 'sd, #0.0");
|
|
}
|
|
} else { // vcmpd
|
|
if (instr->Bit(16) == 0) {
|
|
Format(instr, "vcmpd'cond 'dd, 'dm");
|
|
} else {
|
|
Format(instr, "vcmpd'cond 'dd, #0.0");
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case 7: { // vcvt between double-precision and single-precision
|
|
if (instr->Bit(8) == 0) {
|
|
Format(instr, "vcvtds'cond 'dd, 'sm");
|
|
} else {
|
|
Format(instr, "vcvtsd'cond 'sd, 'dm");
|
|
}
|
|
break;
|
|
}
|
|
case 8: { // vcvt, vcvtr between floating-point and integer
|
|
if (instr->Bit(8) == 0) {
|
|
if (instr->Bit(7) == 0) {
|
|
Format(instr, "vcvtsu'cond 'sd, 'sm");
|
|
} else {
|
|
Format(instr, "vcvtsi'cond 'sd, 'sm");
|
|
}
|
|
} else {
|
|
if (instr->Bit(7) == 0) {
|
|
Format(instr, "vcvtdu'cond 'dd, 'sm");
|
|
} else {
|
|
Format(instr, "vcvtdi'cond 'dd, 'sm");
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case 12:
|
|
case 13: { // vcvt, vcvtr between floating-point and integer
|
|
if (instr->Bit(7) == 0) {
|
|
// We only support round-to-zero mode
|
|
Unknown(instr);
|
|
break;
|
|
}
|
|
if (instr->Bit(8) == 0) {
|
|
if (instr->Bit(16) == 0) {
|
|
Format(instr, "vcvtus'cond 'sd, 'sm");
|
|
} else {
|
|
Format(instr, "vcvtis'cond 'sd, 'sm");
|
|
}
|
|
} else {
|
|
if (instr->Bit(16) == 0) {
|
|
Format(instr, "vcvtud'cond 'sd, 'dm");
|
|
} else {
|
|
Format(instr, "vcvtid'cond 'sd, 'dm");
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case 2: // vcvtb, vcvtt
|
|
case 3: // vcvtb, vcvtt
|
|
case 9: // undefined
|
|
case 10: // vcvt between floating-point and fixed-point
|
|
case 11: // vcvt between floating-point and fixed-point
|
|
case 14: // vcvt between floating-point and fixed-point
|
|
case 15: // vcvt between floating-point and fixed-point
|
|
default: {
|
|
Unknown(instr);
|
|
break;
|
|
}
|
|
}
|
|
} break;
|
|
}
|
|
} else {
|
|
// 8, 16, or 32-bit Transfer between ARM Core and VFP
|
|
if ((instr->Bits(21, 3) == 0) && (instr->Bit(8) == 0)) {
|
|
if (instr->Bit(20) == 0) {
|
|
Format(instr, "vmovs'cond 'sn, 'rd");
|
|
} else {
|
|
Format(instr, "vmovr'cond 'rd, 'sn");
|
|
}
|
|
} else if ((instr->Bits(22, 3) == 0) && (instr->Bit(20) == 0) &&
|
|
(instr->Bit(8) == 1) && (instr->Bits(5, 2) == 0)) {
|
|
if (instr->Bit(21) == 0) {
|
|
Format(instr, "vmovd'cond 'dn[0], 'rd");
|
|
} else {
|
|
Format(instr, "vmovd'cond 'dn[1], 'rd");
|
|
}
|
|
} else if ((instr->Bits(20, 4) == 0xf) && (instr->Bit(8) == 0)) {
|
|
if (instr->Bits(12, 4) == 0xf) {
|
|
Format(instr, "vmrs'cond APSR, FPSCR");
|
|
} else {
|
|
Format(instr, "vmrs'cond 'rd, FPSCR");
|
|
}
|
|
} else {
|
|
Unknown(instr);
|
|
}
|
|
}
|
|
} else {
|
|
Unknown(instr);
|
|
}
|
|
}
|
|
|
|
|
|
void ARMDecoder::DecodeSIMDDataProcessing(Instr* instr) {
|
|
ASSERT(instr->ConditionField() == kSpecialCondition);
|
|
if (instr->Bit(6) == 1) {
|
|
if ((instr->Bits(8, 4) == 8) && (instr->Bit(4) == 0) &&
|
|
(instr->Bits(23, 2) == 0)) {
|
|
Format(instr, "vaddq'sz 'qd, 'qn, 'qm");
|
|
} else if ((instr->Bits(8, 4) == 13) && (instr->Bit(4) == 0) &&
|
|
(instr->Bits(23, 2) == 0) && (instr->Bit(21) == 0)) {
|
|
Format(instr, "vaddqs 'qd, 'qn, 'qm");
|
|
} else if ((instr->Bits(8, 4) == 8) && (instr->Bit(4) == 0) &&
|
|
(instr->Bits(23, 2) == 2)) {
|
|
Format(instr, "vsubq'sz 'qd, 'qn, 'qm");
|
|
} else if ((instr->Bits(8, 4) == 13) && (instr->Bit(4) == 0) &&
|
|
(instr->Bits(23, 2) == 0) && (instr->Bit(21) == 1)) {
|
|
Format(instr, "vsubqs 'qd, 'qn, 'qm");
|
|
} else if ((instr->Bits(8, 4) == 9) && (instr->Bit(4) == 1) &&
|
|
(instr->Bits(23, 2) == 0)) {
|
|
Format(instr, "vmulq'sz 'qd, 'qn, 'qm");
|
|
} else if ((instr->Bits(8, 4) == 13) && (instr->Bit(4) == 1) &&
|
|
(instr->Bits(23, 2) == 2) && (instr->Bit(21) == 0)) {
|
|
Format(instr, "vmulqs 'qd, 'qn, 'qm");
|
|
} else if ((instr->Bits(8, 4) == 4) && (instr->Bit(4) == 0) &&
|
|
(instr->Bits(23, 5) == 4)) {
|
|
Format(instr, "vshlqi'sz 'qd, 'qm, 'qn");
|
|
} else if ((instr->Bits(8, 4) == 4) && (instr->Bit(4) == 0) &&
|
|
(instr->Bits(23, 5) == 6)) {
|
|
Format(instr, "vshlqu'sz 'qd, 'qm, 'qn");
|
|
} else if ((instr->Bits(8, 4) == 1) && (instr->Bit(4) == 1) &&
|
|
(instr->Bits(20, 2) == 0) && (instr->Bits(23, 2) == 2)) {
|
|
Format(instr, "veorq 'qd, 'qn, 'qm");
|
|
} else if ((instr->Bits(8, 4) == 1) && (instr->Bit(4) == 1) &&
|
|
(instr->Bits(20, 2) == 3) && (instr->Bits(23, 2) == 0)) {
|
|
Format(instr, "vornq 'qd, 'qn, 'qm");
|
|
} else if ((instr->Bits(8, 4) == 1) && (instr->Bit(4) == 1) &&
|
|
(instr->Bits(20, 2) == 2) && (instr->Bits(23, 2) == 0)) {
|
|
if (instr->QmField() == instr->QnField()) {
|
|
Format(instr, "vmovq 'qd, 'qm");
|
|
} else {
|
|
Format(instr, "vorrq 'qd, 'qm");
|
|
}
|
|
} else if ((instr->Bits(8, 4) == 1) && (instr->Bit(4) == 1) &&
|
|
(instr->Bits(20, 2) == 0) && (instr->Bits(23, 2) == 0)) {
|
|
Format(instr, "vandq 'qd, 'qn, 'qm");
|
|
} else if ((instr->Bits(7, 5) == 11) && (instr->Bit(4) == 0) &&
|
|
(instr->Bits(20, 2) == 3) && (instr->Bits(23, 5) == 7) &&
|
|
(instr->Bits(16, 4) == 0)) {
|
|
Format(instr, "vmvnq 'qd, 'qm");
|
|
} else if ((instr->Bits(8, 4) == 15) && (instr->Bit(4) == 0) &&
|
|
(instr->Bits(20, 2) == 2) && (instr->Bits(23, 2) == 0)) {
|
|
Format(instr, "vminqs 'qd, 'qn, 'qm");
|
|
} else if ((instr->Bits(8, 4) == 15) && (instr->Bit(4) == 0) &&
|
|
(instr->Bits(20, 2) == 0) && (instr->Bits(23, 2) == 0)) {
|
|
Format(instr, "vmaxqs 'qd, 'qn, 'qm");
|
|
} else if ((instr->Bits(8, 4) == 7) && (instr->Bit(4) == 0) &&
|
|
(instr->Bits(20, 2) == 3) && (instr->Bits(23, 2) == 3) &&
|
|
(instr->Bit(7) == 0) && (instr->Bits(16, 4) == 9)) {
|
|
Format(instr, "vabsqs 'qd, 'qm");
|
|
} else if ((instr->Bits(8, 4) == 7) && (instr->Bit(4) == 0) &&
|
|
(instr->Bits(20, 2) == 3) && (instr->Bits(23, 2) == 3) &&
|
|
(instr->Bit(7) == 1) && (instr->Bits(16, 4) == 9)) {
|
|
Format(instr, "vnegqs 'qd, 'qm");
|
|
} else if ((instr->Bits(7, 5) == 10) && (instr->Bit(4) == 0) &&
|
|
(instr->Bits(20, 2) == 3) && (instr->Bits(23, 2) == 3) &&
|
|
(instr->Bits(16, 4) == 11)) {
|
|
Format(instr, "vrecpeqs 'qd, 'qm");
|
|
} else if ((instr->Bits(8, 4) == 15) && (instr->Bit(4) == 1) &&
|
|
(instr->Bits(20, 2) == 0) && (instr->Bits(23, 2) == 0)) {
|
|
Format(instr, "vrecpsqs 'qd, 'qn, 'qm");
|
|
} else if ((instr->Bits(8, 4) == 5) && (instr->Bit(4) == 0) &&
|
|
(instr->Bits(20, 2) == 3) && (instr->Bits(23, 2) == 3) &&
|
|
(instr->Bit(7) == 1) && (instr->Bits(16, 4) == 11)) {
|
|
Format(instr, "vrsqrteqs 'qd, 'qm");
|
|
} else if ((instr->Bits(8, 4) == 15) && (instr->Bit(4) == 1) &&
|
|
(instr->Bits(20, 2) == 2) && (instr->Bits(23, 2) == 0)) {
|
|
Format(instr, "vrsqrtsqs 'qd, 'qn, 'qm");
|
|
} else if ((instr->Bits(8, 4) == 12) && (instr->Bit(4) == 0) &&
|
|
(instr->Bits(20, 2) == 3) && (instr->Bits(23, 2) == 3) &&
|
|
(instr->Bit(7) == 0)) {
|
|
int32_t imm4 = instr->Bits(16, 4);
|
|
if (imm4 & 1) {
|
|
Format(instr, "vdupb 'qd, 'dm['imm4_vdup]");
|
|
} else if (imm4 & 2) {
|
|
Format(instr, "vduph 'qd, 'dm['imm4_vdup]");
|
|
} else if (imm4 & 4) {
|
|
Format(instr, "vdupw 'qd, 'dm['imm4_vdup]");
|
|
} else {
|
|
Unknown(instr);
|
|
}
|
|
} else if ((instr->Bits(8, 4) == 1) && (instr->Bit(4) == 0) &&
|
|
(instr->Bits(20, 2) == 3) && (instr->Bits(23, 2) == 3) &&
|
|
(instr->Bit(7) == 1) && (instr->Bits(16, 4) == 10)) {
|
|
Format(instr, "vzipqw 'qd, 'qm");
|
|
} else if ((instr->Bits(8, 4) == 8) && (instr->Bit(4) == 1) &&
|
|
(instr->Bits(23, 2) == 2)) {
|
|
Format(instr, "vceqq'sz 'qd, 'qn, 'qm");
|
|
} else if ((instr->Bits(8, 4) == 14) && (instr->Bit(4) == 0) &&
|
|
(instr->Bits(20, 2) == 0) && (instr->Bits(23, 2) == 0)) {
|
|
Format(instr, "vceqqs 'qd, 'qn, 'qm");
|
|
} else if ((instr->Bits(8, 4) == 3) && (instr->Bit(4) == 1) &&
|
|
(instr->Bits(23, 2) == 0)) {
|
|
Format(instr, "vcgeq'sz 'qd, 'qn, 'qm");
|
|
} else if ((instr->Bits(8, 4) == 3) && (instr->Bit(4) == 1) &&
|
|
(instr->Bits(23, 2) == 2)) {
|
|
Format(instr, "vcugeq'sz 'qd, 'qn, 'qm");
|
|
} else if ((instr->Bits(8, 4) == 14) && (instr->Bit(4) == 0) &&
|
|
(instr->Bits(20, 2) == 0) && (instr->Bits(23, 2) == 2)) {
|
|
Format(instr, "vcgeqs 'qd, 'qn, 'qm");
|
|
} else if ((instr->Bits(8, 4) == 3) && (instr->Bit(4) == 0) &&
|
|
(instr->Bits(23, 2) == 0)) {
|
|
Format(instr, "vcgtq'sz 'qd, 'qn, 'qm");
|
|
} else if ((instr->Bits(8, 4) == 3) && (instr->Bit(4) == 0) &&
|
|
(instr->Bits(23, 2) == 2)) {
|
|
Format(instr, "vcugtq'sz 'qd, 'qn, 'qm");
|
|
} else if ((instr->Bits(8, 4) == 14) && (instr->Bit(4) == 0) &&
|
|
(instr->Bits(20, 2) == 2) && (instr->Bits(23, 2) == 2)) {
|
|
Format(instr, "vcgtqs 'qd, 'qn, 'qm");
|
|
} else {
|
|
Unknown(instr);
|
|
}
|
|
} else {
|
|
if ((instr->Bits(23, 2) == 3) && (instr->Bits(20, 2) == 3) &&
|
|
(instr->Bits(10, 2) == 2) && (instr->Bit(4) == 0)) {
|
|
Format(instr, "vtbl 'dd, 'dtbllist, 'dm");
|
|
} else {
|
|
Unknown(instr);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void ARMDecoder::InstructionDecode(uword pc) {
|
|
Instr* instr = Instr::At(pc);
|
|
|
|
if (instr->ConditionField() == kSpecialCondition) {
|
|
if ((instr->InstructionBits() == static_cast<int32_t>(0xf57ff01f)) &&
|
|
(TargetCPUFeatures::arm_version() != ARMv5TE)) {
|
|
Format(instr, "clrex");
|
|
} else {
|
|
if (instr->IsSIMDDataProcessing()) {
|
|
DecodeSIMDDataProcessing(instr);
|
|
} else {
|
|
Unknown(instr);
|
|
}
|
|
}
|
|
} else {
|
|
switch (instr->TypeField()) {
|
|
case 0:
|
|
case 1: {
|
|
DecodeType01(instr);
|
|
break;
|
|
}
|
|
case 2: {
|
|
DecodeType2(instr);
|
|
break;
|
|
}
|
|
case 3: {
|
|
DecodeType3(instr);
|
|
break;
|
|
}
|
|
case 4: {
|
|
DecodeType4(instr);
|
|
break;
|
|
}
|
|
case 5: {
|
|
DecodeType5(instr);
|
|
break;
|
|
}
|
|
case 6: {
|
|
DecodeType6(instr);
|
|
break;
|
|
}
|
|
case 7: {
|
|
DecodeType7(instr);
|
|
break;
|
|
}
|
|
default: {
|
|
// The type field is 3-bits in the ARM encoding.
|
|
UNREACHABLE();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void Disassembler::DecodeInstruction(char* hex_buffer,
|
|
intptr_t hex_size,
|
|
char* human_buffer,
|
|
intptr_t human_size,
|
|
int* out_instr_size,
|
|
const Code& code,
|
|
Object** object,
|
|
uword pc) {
|
|
ARMDecoder decoder(human_buffer, human_size);
|
|
decoder.InstructionDecode(pc);
|
|
int32_t instruction_bits = Instr::At(pc)->InstructionBits();
|
|
OS::SNPrint(hex_buffer, hex_size, "%08x", instruction_bits);
|
|
if (out_instr_size) {
|
|
*out_instr_size = Instr::kInstrSize;
|
|
}
|
|
|
|
*object = NULL;
|
|
if (!code.IsNull()) {
|
|
*object = &Object::Handle();
|
|
if (!DecodeLoadObjectFromPoolOrThread(pc, code, *object)) {
|
|
*object = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif // !PRODUCT
|
|
|
|
} // namespace dart
|
|
|
|
#endif // defined TARGET_ARCH_ARM
|