// 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/globals.h" // Needed here to get TARGET_ARCH_ARM. #if defined(TARGET_ARCH_ARM) #include "vm/compiler/assembler/disassembler.h" #include "platform/assert.h" #include "vm/cpu.h" #include "vm/instructions.h" namespace dart { #if !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER) 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 ". 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()]); } // Print the register name according to the active name converter. void ARMDecoder::PrintRegister(int reg) { ASSERT(0 <= reg); ASSERT(reg < kNumberOfCpuRegisters); Print(cpu_reg_names[reg]); } void ARMDecoder::PrintSRegister(int reg) { ASSERT(0 <= reg); ASSERT(reg < kNumberOfSRegisters); buffer_pos_ += Utils::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_ += Utils::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_ += Utils::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 ". 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_ += Utils::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_ += Utils::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) { uint8_t rotate = instr->RotateField() * 2; int32_t immed8 = instr->Immed8Field(); int32_t imm = Utils::RotateRight(immed8, rotate); buffer_pos_ += Utils::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")); const int32_t off = (static_cast(instr->SImmed24Field()) << 2) + 8; if (FLAG_disassemble_relative) { buffer_pos_ += Utils::SNPrint(current_position_in_buffer(), remaining_size_in_buffer(), "%+" Pd32 "", off); } else { uword destination = reinterpret_cast(instr) + off; buffer_pos_ += Utils::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_ += Utils::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_ += Utils::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_ += Utils::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_ += Utils::SNPrint(current_position_in_buffer(), remaining_size_in_buffer(), "0x%x", immed16); return 7; } case 'l': { if (format[1] == 's') { ASSERT(STRING_STARTS_WITH(format, "lsb")); buffer_pos_ += Utils::SNPrint(current_position_in_buffer(), remaining_size_in_buffer(), "%u", instr->BitFieldExtractLSBField()); return 3; } else { // '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_ += Utils::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_ += Utils::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_ += Utils::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_ += Utils::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_ += Utils::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")); int32_t off = (static_cast(instr->SImmed24Field()) << 2) + 8; buffer_pos_ += Utils::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': { if (format[1] == 'i') { ASSERT(STRING_STARTS_WITH(format, "width")); // 'width: width field of bit field extract instructions // (field value in encoding is 1 less than in mnemonic) buffer_pos_ += Utils::SNPrint(current_position_in_buffer(), remaining_size_in_buffer(), "%u", instr->BitFieldExtractWidthField() + 1); return 5; } else { // '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"); } 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: { // 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: { // 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 { // 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: { Format(instr, "movw'cond 'rd, #'imm4_12"); 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: { Format(instr, "movt'cond 'rd, #'imm4_12"); 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->IsMedia()) { if (instr->IsDivision()) { if (!TargetCPUFeatures::integer_division_supported()) { Unknown(instr); return; } // Check differences between A8.8.{165,248} and FormatRegister. static_assert(kDivRdShift == kRnShift, "div 'rd does not correspond to 'rn"); static_assert(kDivRmShift == kRsShift, "div 'rm does not correspond to 'rs"); static_assert(kDivRnShift == kRmShift, "div 'rn does not correspond to 'rm"); if (instr->IsDivUnsigned()) { Format(instr, "udiv'cond 'rn, 'rm, 'rs"); } else { Format(instr, "sdiv'cond 'rn, 'rm, 'rs"); } } else if (instr->IsRbit()) { Format(instr, "rbit'cond 'rd, 'rm"); } else if (instr->IsBitFieldExtract()) { // Check differences between A8.8.{164,246} and FormatRegister. static_assert(kBitFieldExtractRnShift == kRmShift, "bfx 'rn does not correspond to 'rm"); if (instr->IsBitFieldExtractSignExtended()) { Format(instr, "sbfx'cond 'rd, 'rm, 'lsb, 'width"); } else { Format(instr, "ubfx'cond 'rd, 'rm, 'lsb, 'width"); } } else { UNREACHABLE(); } 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) != 0) { // 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) != 0) { // 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) != 0) { Format(instr, "vdupb 'qd, 'dm['imm4_vdup]"); } else if ((imm4 & 2) != 0) { Format(instr, "vduph 'qd, 'dm['imm4_vdup]"); } else if ((imm4 & 4) != 0) { 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(0xf57ff01f)) { Format(instr, "clrex"); } else if (instr->InstructionBits() == static_cast(kDMB_ISH)) { Format(instr, "dmb ish"); } else if (instr->InstructionBits() == static_cast(kDMB_ISHST)) { Format(instr, "dmb ishst"); } 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(); Utils::SNPrint(hex_buffer, hex_size, "%08x", instruction_bits); if (out_instr_size != nullptr) { *out_instr_size = Instr::kInstrSize; } *object = nullptr; // TODO(36839): Make DecodeLoadObjectFromPoolOrThread work on simarm_x64. #if !defined(IS_SIMARM_HOST64) if (!code.IsNull()) { *object = &Object::Handle(); if (!DecodeLoadObjectFromPoolOrThread(pc, code, *object)) { *object = nullptr; } } #endif // !defined(IS_SIMARM_HOST64) } #endif // !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER) } // namespace dart #endif // defined(TARGET_ARCH_ARM)