Index: src/a64/disasm-a64.cc |
diff --git a/src/a64/disasm-a64.cc b/src/a64/disasm-a64.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..5ef75d55e2061c5d1ab40525a489cfb237913e5f |
--- /dev/null |
+++ b/src/a64/disasm-a64.cc |
@@ -0,0 +1,1854 @@ |
+// Copyright 2013 the V8 project authors. All rights reserved. |
+// Redistribution and use in source and binary forms, with or without |
+// modification, are permitted provided that the following conditions are |
+// met: |
+// |
+// * Redistributions of source code must retain the above copyright |
+// notice, this list of conditions and the following disclaimer. |
+// * Redistributions in binary form must reproduce the above |
+// copyright notice, this list of conditions and the following |
+// disclaimer in the documentation and/or other materials provided |
+// with the distribution. |
+// * Neither the name of Google Inc. nor the names of its |
+// contributors may be used to endorse or promote products derived |
+// from this software without specific prior written permission. |
+// |
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
+ |
+#include <assert.h> |
+#include <stdio.h> |
+#include <stdarg.h> |
+#include <string.h> |
+ |
+#include "v8.h" |
+ |
+#if V8_TARGET_ARCH_A64 |
+ |
+#include "disasm.h" |
+#include "a64/disasm-a64.h" |
+#include "macro-assembler.h" |
+#include "platform.h" |
+ |
+namespace v8 { |
+namespace internal { |
+ |
+ |
+Disassembler::Disassembler() { |
+ buffer_size_ = 256; |
+ buffer_ = reinterpret_cast<char*>(malloc(buffer_size_)); |
+ buffer_pos_ = 0; |
+ own_buffer_ = true; |
+} |
+ |
+ |
+Disassembler::Disassembler(char* text_buffer, int buffer_size) { |
+ buffer_size_ = buffer_size; |
+ buffer_ = text_buffer; |
+ buffer_pos_ = 0; |
+ own_buffer_ = false; |
+} |
+ |
+ |
+Disassembler::~Disassembler() { |
+ if (own_buffer_) { |
+ free(buffer_); |
+ } |
+} |
+ |
+ |
+char* Disassembler::GetOutput() { |
+ return buffer_; |
+} |
+ |
+ |
+void Disassembler::VisitAddSubImmediate(Instruction* instr) { |
+ bool rd_is_zr = RdIsZROrSP(instr); |
+ bool stack_op = (rd_is_zr || RnIsZROrSP(instr)) && |
+ (instr->ImmAddSub() == 0) ? true : false; |
+ const char *mnemonic = ""; |
+ const char *form = "'Rds, 'Rns, 'IAddSub"; |
+ const char *form_cmp = "'Rns, 'IAddSub"; |
+ const char *form_mov = "'Rds, 'Rns"; |
+ |
+ switch (instr->Mask(AddSubImmediateMask)) { |
+ case ADD_w_imm: |
+ case ADD_x_imm: { |
+ mnemonic = "add"; |
+ if (stack_op) { |
+ mnemonic = "mov"; |
+ form = form_mov; |
+ } |
+ break; |
+ } |
+ case ADDS_w_imm: |
+ case ADDS_x_imm: { |
+ mnemonic = "adds"; |
+ if (rd_is_zr) { |
+ mnemonic = "cmn"; |
+ form = form_cmp; |
+ } |
+ break; |
+ } |
+ case SUB_w_imm: |
+ case SUB_x_imm: mnemonic = "sub"; break; |
+ case SUBS_w_imm: |
+ case SUBS_x_imm: { |
+ mnemonic = "subs"; |
+ if (rd_is_zr) { |
+ mnemonic = "cmp"; |
+ form = form_cmp; |
+ } |
+ break; |
+ } |
+ default: UNREACHABLE(); |
+ } |
+ Format(instr, mnemonic, form); |
+} |
+ |
+ |
+void Disassembler::VisitAddSubShifted(Instruction* instr) { |
+ bool rd_is_zr = RdIsZROrSP(instr); |
+ bool rn_is_zr = RnIsZROrSP(instr); |
+ const char *mnemonic = ""; |
+ const char *form = "'Rd, 'Rn, 'Rm'HDP"; |
+ const char *form_cmp = "'Rn, 'Rm'HDP"; |
+ const char *form_neg = "'Rd, 'Rm'HDP"; |
+ |
+ switch (instr->Mask(AddSubShiftedMask)) { |
+ case ADD_w_shift: |
+ case ADD_x_shift: mnemonic = "add"; break; |
+ case ADDS_w_shift: |
+ case ADDS_x_shift: { |
+ mnemonic = "adds"; |
+ if (rd_is_zr) { |
+ mnemonic = "cmn"; |
+ form = form_cmp; |
+ } |
+ break; |
+ } |
+ case SUB_w_shift: |
+ case SUB_x_shift: { |
+ mnemonic = "sub"; |
+ if (rn_is_zr) { |
+ mnemonic = "neg"; |
+ form = form_neg; |
+ } |
+ break; |
+ } |
+ case SUBS_w_shift: |
+ case SUBS_x_shift: { |
+ mnemonic = "subs"; |
+ if (rd_is_zr) { |
+ mnemonic = "cmp"; |
+ form = form_cmp; |
+ } else if (rn_is_zr) { |
+ mnemonic = "negs"; |
+ form = form_neg; |
+ } |
+ break; |
+ } |
+ default: UNREACHABLE(); |
+ } |
+ Format(instr, mnemonic, form); |
+} |
+ |
+ |
+void Disassembler::VisitAddSubExtended(Instruction* instr) { |
+ bool rd_is_zr = RdIsZROrSP(instr); |
+ const char *mnemonic = ""; |
+ Extend mode = static_cast<Extend>(instr->ExtendMode()); |
+ const char *form = ((mode == UXTX) || (mode == SXTX)) ? |
+ "'Rds, 'Rns, 'Xm'Ext" : "'Rds, 'Rns, 'Wm'Ext"; |
+ const char *form_cmp = ((mode == UXTX) || (mode == SXTX)) ? |
+ "'Rns, 'Xm'Ext" : "'Rns, 'Wm'Ext"; |
+ |
+ switch (instr->Mask(AddSubExtendedMask)) { |
+ case ADD_w_ext: |
+ case ADD_x_ext: mnemonic = "add"; break; |
+ case ADDS_w_ext: |
+ case ADDS_x_ext: { |
+ mnemonic = "adds"; |
+ if (rd_is_zr) { |
+ mnemonic = "cmn"; |
+ form = form_cmp; |
+ } |
+ break; |
+ } |
+ case SUB_w_ext: |
+ case SUB_x_ext: mnemonic = "sub"; break; |
+ case SUBS_w_ext: |
+ case SUBS_x_ext: { |
+ mnemonic = "subs"; |
+ if (rd_is_zr) { |
+ mnemonic = "cmp"; |
+ form = form_cmp; |
+ } |
+ break; |
+ } |
+ default: UNREACHABLE(); |
+ } |
+ Format(instr, mnemonic, form); |
+} |
+ |
+ |
+void Disassembler::VisitAddSubWithCarry(Instruction* instr) { |
+ bool rn_is_zr = RnIsZROrSP(instr); |
+ const char *mnemonic = ""; |
+ const char *form = "'Rd, 'Rn, 'Rm"; |
+ const char *form_neg = "'Rd, 'Rm"; |
+ |
+ switch (instr->Mask(AddSubWithCarryMask)) { |
+ case ADC_w: |
+ case ADC_x: mnemonic = "adc"; break; |
+ case ADCS_w: |
+ case ADCS_x: mnemonic = "adcs"; break; |
+ case SBC_w: |
+ case SBC_x: { |
+ mnemonic = "sbc"; |
+ if (rn_is_zr) { |
+ mnemonic = "ngc"; |
+ form = form_neg; |
+ } |
+ break; |
+ } |
+ case SBCS_w: |
+ case SBCS_x: { |
+ mnemonic = "sbcs"; |
+ if (rn_is_zr) { |
+ mnemonic = "ngcs"; |
+ form = form_neg; |
+ } |
+ break; |
+ } |
+ default: UNREACHABLE(); |
+ } |
+ Format(instr, mnemonic, form); |
+} |
+ |
+ |
+void Disassembler::VisitLogicalImmediate(Instruction* instr) { |
+ bool rd_is_zr = RdIsZROrSP(instr); |
+ bool rn_is_zr = RnIsZROrSP(instr); |
+ const char *mnemonic = ""; |
+ const char *form = "'Rds, 'Rn, 'ITri"; |
+ |
+ if (instr->ImmLogical() == 0) { |
+ // The immediate encoded in the instruction is not in the expected format. |
+ Format(instr, "unallocated", "(LogicalImmediate)"); |
+ return; |
+ } |
+ |
+ switch (instr->Mask(LogicalImmediateMask)) { |
+ case AND_w_imm: |
+ case AND_x_imm: mnemonic = "and"; break; |
+ case ORR_w_imm: |
+ case ORR_x_imm: { |
+ mnemonic = "orr"; |
+ unsigned reg_size = (instr->SixtyFourBits() == 1) ? kXRegSize |
+ : kWRegSize; |
+ if (rn_is_zr && !IsMovzMovnImm(reg_size, instr->ImmLogical())) { |
+ mnemonic = "mov"; |
+ form = "'Rds, 'ITri"; |
+ } |
+ break; |
+ } |
+ case EOR_w_imm: |
+ case EOR_x_imm: mnemonic = "eor"; break; |
+ case ANDS_w_imm: |
+ case ANDS_x_imm: { |
+ mnemonic = "ands"; |
+ if (rd_is_zr) { |
+ mnemonic = "tst"; |
+ form = "'Rn, 'ITri"; |
+ } |
+ break; |
+ } |
+ default: UNREACHABLE(); |
+ } |
+ Format(instr, mnemonic, form); |
+} |
+ |
+ |
+bool Disassembler::IsMovzMovnImm(unsigned reg_size, uint64_t value) { |
+ ASSERT((reg_size == kXRegSize) || |
+ ((reg_size == kWRegSize) && (value <= 0xffffffff))); |
+ |
+ // Test for movz: 16-bits set at positions 0, 16, 32 or 48. |
+ if (((value & 0xffffffffffff0000UL) == 0UL) || |
+ ((value & 0xffffffff0000ffffUL) == 0UL) || |
+ ((value & 0xffff0000ffffffffUL) == 0UL) || |
+ ((value & 0x0000ffffffffffffUL) == 0UL)) { |
+ return true; |
+ } |
+ |
+ // Test for movn: NOT(16-bits set at positions 0, 16, 32 or 48). |
+ if ((reg_size == kXRegSize) && |
+ (((value & 0xffffffffffff0000UL) == 0xffffffffffff0000UL) || |
+ ((value & 0xffffffff0000ffffUL) == 0xffffffff0000ffffUL) || |
+ ((value & 0xffff0000ffffffffUL) == 0xffff0000ffffffffUL) || |
+ ((value & 0x0000ffffffffffffUL) == 0x0000ffffffffffffUL))) { |
+ return true; |
+ } |
+ if ((reg_size == kWRegSize) && |
+ (((value & 0xffff0000) == 0xffff0000) || |
+ ((value & 0x0000ffff) == 0x0000ffff))) { |
+ return true; |
+ } |
+ return false; |
+} |
+ |
+ |
+void Disassembler::VisitLogicalShifted(Instruction* instr) { |
+ bool rd_is_zr = RdIsZROrSP(instr); |
+ bool rn_is_zr = RnIsZROrSP(instr); |
+ const char *mnemonic = ""; |
+ const char *form = "'Rd, 'Rn, 'Rm'HLo"; |
+ |
+ switch (instr->Mask(LogicalShiftedMask)) { |
+ case AND_w: |
+ case AND_x: mnemonic = "and"; break; |
+ case BIC_w: |
+ case BIC_x: mnemonic = "bic"; break; |
+ case EOR_w: |
+ case EOR_x: mnemonic = "eor"; break; |
+ case EON_w: |
+ case EON_x: mnemonic = "eon"; break; |
+ case BICS_w: |
+ case BICS_x: mnemonic = "bics"; break; |
+ case ANDS_w: |
+ case ANDS_x: { |
+ mnemonic = "ands"; |
+ if (rd_is_zr) { |
+ mnemonic = "tst"; |
+ form = "'Rn, 'Rm'HLo"; |
+ } |
+ break; |
+ } |
+ case ORR_w: |
+ case ORR_x: { |
+ mnemonic = "orr"; |
+ if (rn_is_zr && (instr->ImmDPShift() == 0) && (instr->ShiftDP() == LSL)) { |
+ mnemonic = "mov"; |
+ form = "'Rd, 'Rm"; |
+ } |
+ break; |
+ } |
+ case ORN_w: |
+ case ORN_x: { |
+ mnemonic = "orn"; |
+ if (rn_is_zr) { |
+ mnemonic = "mvn"; |
+ form = "'Rd, 'Rm'HLo"; |
+ } |
+ break; |
+ } |
+ default: UNREACHABLE(); |
+ } |
+ |
+ Format(instr, mnemonic, form); |
+} |
+ |
+ |
+void Disassembler::VisitConditionalCompareRegister(Instruction* instr) { |
+ const char *mnemonic = ""; |
+ const char *form = "'Rn, 'Rm, 'INzcv, 'Cond"; |
+ |
+ switch (instr->Mask(ConditionalCompareRegisterMask)) { |
+ case CCMN_w: |
+ case CCMN_x: mnemonic = "ccmn"; break; |
+ case CCMP_w: |
+ case CCMP_x: mnemonic = "ccmp"; break; |
+ default: UNREACHABLE(); |
+ } |
+ Format(instr, mnemonic, form); |
+} |
+ |
+ |
+void Disassembler::VisitConditionalCompareImmediate(Instruction* instr) { |
+ const char *mnemonic = ""; |
+ const char *form = "'Rn, 'IP, 'INzcv, 'Cond"; |
+ |
+ switch (instr->Mask(ConditionalCompareImmediateMask)) { |
+ case CCMN_w_imm: |
+ case CCMN_x_imm: mnemonic = "ccmn"; break; |
+ case CCMP_w_imm: |
+ case CCMP_x_imm: mnemonic = "ccmp"; break; |
+ default: UNREACHABLE(); |
+ } |
+ Format(instr, mnemonic, form); |
+} |
+ |
+ |
+void Disassembler::VisitConditionalSelect(Instruction* instr) { |
+ bool rnm_is_zr = (RnIsZROrSP(instr) && RmIsZROrSP(instr)); |
+ bool rn_is_rm = (instr->Rn() == instr->Rm()); |
+ const char *mnemonic = ""; |
+ const char *form = "'Rd, 'Rn, 'Rm, 'Cond"; |
+ const char *form_test = "'Rd, 'CInv"; |
+ const char *form_update = "'Rd, 'Rn, 'CInv"; |
+ |
+ Condition cond = static_cast<Condition>(instr->Condition()); |
+ bool invertible_cond = (cond != al) && (cond != nv); |
+ |
+ switch (instr->Mask(ConditionalSelectMask)) { |
+ case CSEL_w: |
+ case CSEL_x: mnemonic = "csel"; break; |
+ case CSINC_w: |
+ case CSINC_x: { |
+ mnemonic = "csinc"; |
+ if (rnm_is_zr && invertible_cond) { |
+ mnemonic = "cset"; |
+ form = form_test; |
+ } else if (rn_is_rm && invertible_cond) { |
+ mnemonic = "cinc"; |
+ form = form_update; |
+ } |
+ break; |
+ } |
+ case CSINV_w: |
+ case CSINV_x: { |
+ mnemonic = "csinv"; |
+ if (rnm_is_zr && invertible_cond) { |
+ mnemonic = "csetm"; |
+ form = form_test; |
+ } else if (rn_is_rm && invertible_cond) { |
+ mnemonic = "cinv"; |
+ form = form_update; |
+ } |
+ break; |
+ } |
+ case CSNEG_w: |
+ case CSNEG_x: { |
+ mnemonic = "csneg"; |
+ if (rn_is_rm && invertible_cond) { |
+ mnemonic = "cneg"; |
+ form = form_update; |
+ } |
+ break; |
+ } |
+ default: UNREACHABLE(); |
+ } |
+ Format(instr, mnemonic, form); |
+} |
+ |
+ |
+void Disassembler::VisitBitfield(Instruction* instr) { |
+ unsigned s = instr->ImmS(); |
+ unsigned r = instr->ImmR(); |
+ unsigned rd_size_minus_1 = |
+ ((instr->SixtyFourBits() == 1) ? kXRegSize : kWRegSize) - 1; |
+ const char *mnemonic = ""; |
+ const char *form = ""; |
+ const char *form_shift_right = "'Rd, 'Rn, 'IBr"; |
+ const char *form_extend = "'Rd, 'Wn"; |
+ const char *form_bfiz = "'Rd, 'Rn, 'IBZ-r, 'IBs+1"; |
+ const char *form_bfx = "'Rd, 'Rn, 'IBr, 'IBs-r+1"; |
+ const char *form_lsl = "'Rd, 'Rn, 'IBZ-r"; |
+ |
+ switch (instr->Mask(BitfieldMask)) { |
+ case SBFM_w: |
+ case SBFM_x: { |
+ mnemonic = "sbfx"; |
+ form = form_bfx; |
+ if (r == 0) { |
+ form = form_extend; |
+ if (s == 7) { |
+ mnemonic = "sxtb"; |
+ } else if (s == 15) { |
+ mnemonic = "sxth"; |
+ } else if ((s == 31) && (instr->SixtyFourBits() == 1)) { |
+ mnemonic = "sxtw"; |
+ } else { |
+ form = form_bfx; |
+ } |
+ } else if (s == rd_size_minus_1) { |
+ mnemonic = "asr"; |
+ form = form_shift_right; |
+ } else if (s < r) { |
+ mnemonic = "sbfiz"; |
+ form = form_bfiz; |
+ } |
+ break; |
+ } |
+ case UBFM_w: |
+ case UBFM_x: { |
+ mnemonic = "ubfx"; |
+ form = form_bfx; |
+ if (r == 0) { |
+ form = form_extend; |
+ if (s == 7) { |
+ mnemonic = "uxtb"; |
+ } else if (s == 15) { |
+ mnemonic = "uxth"; |
+ } else { |
+ form = form_bfx; |
+ } |
+ } |
+ if (s == rd_size_minus_1) { |
+ mnemonic = "lsr"; |
+ form = form_shift_right; |
+ } else if (r == s + 1) { |
+ mnemonic = "lsl"; |
+ form = form_lsl; |
+ } else if (s < r) { |
+ mnemonic = "ubfiz"; |
+ form = form_bfiz; |
+ } |
+ break; |
+ } |
+ case BFM_w: |
+ case BFM_x: { |
+ mnemonic = "bfxil"; |
+ form = form_bfx; |
+ if (s < r) { |
+ mnemonic = "bfi"; |
+ form = form_bfiz; |
+ } |
+ } |
+ } |
+ Format(instr, mnemonic, form); |
+} |
+ |
+ |
+void Disassembler::VisitExtract(Instruction* instr) { |
+ const char *mnemonic = ""; |
+ const char *form = "'Rd, 'Rn, 'Rm, 'IExtract"; |
+ |
+ switch (instr->Mask(ExtractMask)) { |
+ case EXTR_w: |
+ case EXTR_x: { |
+ if (instr->Rn() == instr->Rm()) { |
+ mnemonic = "ror"; |
+ form = "'Rd, 'Rn, 'IExtract"; |
+ } else { |
+ mnemonic = "extr"; |
+ } |
+ break; |
+ } |
+ default: UNREACHABLE(); |
+ } |
+ Format(instr, mnemonic, form); |
+} |
+ |
+ |
+void Disassembler::VisitPCRelAddressing(Instruction* instr) { |
+ switch (instr->Mask(PCRelAddressingMask)) { |
+ case ADR: Format(instr, "adr", "'Xd, 'AddrPCRelByte"); break; |
+ // ADRP is not implemented. |
+ default: Format(instr, "unimplemented", "(PCRelAddressing)"); |
+ } |
+} |
+ |
+ |
+void Disassembler::VisitConditionalBranch(Instruction* instr) { |
+ switch (instr->Mask(ConditionalBranchMask)) { |
+ case B_cond: Format(instr, "b.'CBrn", "'BImmCond"); break; |
+ default: UNREACHABLE(); |
+ } |
+} |
+ |
+ |
+void Disassembler::VisitUnconditionalBranchToRegister(Instruction* instr) { |
+ const char *mnemonic = "unimplemented"; |
+ const char *form = "'Xn"; |
+ |
+ switch (instr->Mask(UnconditionalBranchToRegisterMask)) { |
+ case BR: mnemonic = "br"; break; |
+ case BLR: mnemonic = "blr"; break; |
+ case RET: { |
+ mnemonic = "ret"; |
+ if (instr->Rn() == kLinkRegCode) { |
+ form = NULL; |
+ } |
+ break; |
+ } |
+ default: form = "(UnconditionalBranchToRegister)"; |
+ } |
+ Format(instr, mnemonic, form); |
+} |
+ |
+ |
+void Disassembler::VisitUnconditionalBranch(Instruction* instr) { |
+ const char *mnemonic = ""; |
+ const char *form = "'BImmUncn"; |
+ |
+ switch (instr->Mask(UnconditionalBranchMask)) { |
+ case B: mnemonic = "b"; break; |
+ case BL: mnemonic = "bl"; break; |
+ default: UNREACHABLE(); |
+ } |
+ Format(instr, mnemonic, form); |
+} |
+ |
+ |
+void Disassembler::VisitDataProcessing1Source(Instruction* instr) { |
+ const char *mnemonic = ""; |
+ const char *form = "'Rd, 'Rn"; |
+ |
+ switch (instr->Mask(DataProcessing1SourceMask)) { |
+ #define FORMAT(A, B) \ |
+ case A##_w: \ |
+ case A##_x: mnemonic = B; break; |
+ FORMAT(RBIT, "rbit"); |
+ FORMAT(REV16, "rev16"); |
+ FORMAT(REV, "rev"); |
+ FORMAT(CLZ, "clz"); |
+ FORMAT(CLS, "cls"); |
+ #undef FORMAT |
+ case REV32_x: mnemonic = "rev32"; break; |
+ default: UNREACHABLE(); |
+ } |
+ Format(instr, mnemonic, form); |
+} |
+ |
+ |
+void Disassembler::VisitDataProcessing2Source(Instruction* instr) { |
+ const char *mnemonic = "unimplemented"; |
+ const char *form = "'Rd, 'Rn, 'Rm"; |
+ |
+ switch (instr->Mask(DataProcessing2SourceMask)) { |
+ #define FORMAT(A, B) \ |
+ case A##_w: \ |
+ case A##_x: mnemonic = B; break; |
+ FORMAT(UDIV, "udiv"); |
+ FORMAT(SDIV, "sdiv"); |
+ FORMAT(LSLV, "lsl"); |
+ FORMAT(LSRV, "lsr"); |
+ FORMAT(ASRV, "asr"); |
+ FORMAT(RORV, "ror"); |
+ #undef FORMAT |
+ default: form = "(DataProcessing2Source)"; |
+ } |
+ Format(instr, mnemonic, form); |
+} |
+ |
+ |
+void Disassembler::VisitDataProcessing3Source(Instruction* instr) { |
+ bool ra_is_zr = RaIsZROrSP(instr); |
+ const char *mnemonic = ""; |
+ const char *form = "'Xd, 'Wn, 'Wm, 'Xa"; |
+ const char *form_rrr = "'Rd, 'Rn, 'Rm"; |
+ const char *form_rrrr = "'Rd, 'Rn, 'Rm, 'Ra"; |
+ const char *form_xww = "'Xd, 'Wn, 'Wm"; |
+ const char *form_xxx = "'Xd, 'Xn, 'Xm"; |
+ |
+ switch (instr->Mask(DataProcessing3SourceMask)) { |
+ case MADD_w: |
+ case MADD_x: { |
+ mnemonic = "madd"; |
+ form = form_rrrr; |
+ if (ra_is_zr) { |
+ mnemonic = "mul"; |
+ form = form_rrr; |
+ } |
+ break; |
+ } |
+ case MSUB_w: |
+ case MSUB_x: { |
+ mnemonic = "msub"; |
+ form = form_rrrr; |
+ if (ra_is_zr) { |
+ mnemonic = "mneg"; |
+ form = form_rrr; |
+ } |
+ break; |
+ } |
+ case SMADDL_x: { |
+ mnemonic = "smaddl"; |
+ if (ra_is_zr) { |
+ mnemonic = "smull"; |
+ form = form_xww; |
+ } |
+ break; |
+ } |
+ case SMSUBL_x: { |
+ mnemonic = "smsubl"; |
+ if (ra_is_zr) { |
+ mnemonic = "smnegl"; |
+ form = form_xww; |
+ } |
+ break; |
+ } |
+ case UMADDL_x: { |
+ mnemonic = "umaddl"; |
+ if (ra_is_zr) { |
+ mnemonic = "umull"; |
+ form = form_xww; |
+ } |
+ break; |
+ } |
+ case UMSUBL_x: { |
+ mnemonic = "umsubl"; |
+ if (ra_is_zr) { |
+ mnemonic = "umnegl"; |
+ form = form_xww; |
+ } |
+ break; |
+ } |
+ case SMULH_x: { |
+ mnemonic = "smulh"; |
+ form = form_xxx; |
+ break; |
+ } |
+ case UMULH_x: { |
+ mnemonic = "umulh"; |
+ form = form_xxx; |
+ break; |
+ } |
+ default: UNREACHABLE(); |
+ } |
+ Format(instr, mnemonic, form); |
+} |
+ |
+ |
+void Disassembler::VisitCompareBranch(Instruction* instr) { |
+ const char *mnemonic = ""; |
+ const char *form = "'Rt, 'BImmCmpa"; |
+ |
+ switch (instr->Mask(CompareBranchMask)) { |
+ case CBZ_w: |
+ case CBZ_x: mnemonic = "cbz"; break; |
+ case CBNZ_w: |
+ case CBNZ_x: mnemonic = "cbnz"; break; |
+ default: UNREACHABLE(); |
+ } |
+ Format(instr, mnemonic, form); |
+} |
+ |
+ |
+void Disassembler::VisitTestBranch(Instruction* instr) { |
+ const char *mnemonic = ""; |
+ // If the top bit of the immediate is clear, the tested register is |
+ // disassembled as Wt, otherwise Xt. As the top bit of the immediate is |
+ // encoded in bit 31 of the instruction, we can reuse the Rt form, which |
+ // uses bit 31 (normally "sf") to choose the register size. |
+ const char *form = "'Rt, 'IS, 'BImmTest"; |
+ |
+ switch (instr->Mask(TestBranchMask)) { |
+ case TBZ: mnemonic = "tbz"; break; |
+ case TBNZ: mnemonic = "tbnz"; break; |
+ default: UNREACHABLE(); |
+ } |
+ Format(instr, mnemonic, form); |
+} |
+ |
+ |
+void Disassembler::VisitMoveWideImmediate(Instruction* instr) { |
+ const char *mnemonic = ""; |
+ const char *form = "'Rd, 'IMoveImm"; |
+ |
+ // Print the shift separately for movk, to make it clear which half word will |
+ // be overwritten. Movn and movz print the computed immediate, which includes |
+ // shift calculation. |
+ switch (instr->Mask(MoveWideImmediateMask)) { |
+ case MOVN_w: |
+ case MOVN_x: mnemonic = "movn"; break; |
+ case MOVZ_w: |
+ case MOVZ_x: mnemonic = "movz"; break; |
+ case MOVK_w: |
+ case MOVK_x: mnemonic = "movk"; form = "'Rd, 'IMoveLSL"; break; |
+ default: UNREACHABLE(); |
+ } |
+ Format(instr, mnemonic, form); |
+} |
+ |
+ |
+#define LOAD_STORE_LIST(V) \ |
+ V(STRB_w, "strb", "'Wt") \ |
+ V(STRH_w, "strh", "'Wt") \ |
+ V(STR_w, "str", "'Wt") \ |
+ V(STR_x, "str", "'Xt") \ |
+ V(LDRB_w, "ldrb", "'Wt") \ |
+ V(LDRH_w, "ldrh", "'Wt") \ |
+ V(LDR_w, "ldr", "'Wt") \ |
+ V(LDR_x, "ldr", "'Xt") \ |
+ V(LDRSB_x, "ldrsb", "'Xt") \ |
+ V(LDRSH_x, "ldrsh", "'Xt") \ |
+ V(LDRSW_x, "ldrsw", "'Xt") \ |
+ V(LDRSB_w, "ldrsb", "'Wt") \ |
+ V(LDRSH_w, "ldrsh", "'Wt") \ |
+ V(STR_s, "str", "'St") \ |
+ V(STR_d, "str", "'Dt") \ |
+ V(LDR_s, "ldr", "'St") \ |
+ V(LDR_d, "ldr", "'Dt") |
+ |
+void Disassembler::VisitLoadStorePreIndex(Instruction* instr) { |
+ const char *mnemonic = "unimplemented"; |
+ const char *form = "(LoadStorePreIndex)"; |
+ |
+ switch (instr->Mask(LoadStorePreIndexMask)) { |
+ #define LS_PREINDEX(A, B, C) \ |
+ case A##_pre: mnemonic = B; form = C ", ['Xns'ILS]!"; break; |
+ LOAD_STORE_LIST(LS_PREINDEX) |
+ #undef LS_PREINDEX |
+ } |
+ Format(instr, mnemonic, form); |
+} |
+ |
+ |
+void Disassembler::VisitLoadStorePostIndex(Instruction* instr) { |
+ const char *mnemonic = "unimplemented"; |
+ const char *form = "(LoadStorePostIndex)"; |
+ |
+ switch (instr->Mask(LoadStorePostIndexMask)) { |
+ #define LS_POSTINDEX(A, B, C) \ |
+ case A##_post: mnemonic = B; form = C ", ['Xns]'ILS"; break; |
+ LOAD_STORE_LIST(LS_POSTINDEX) |
+ #undef LS_POSTINDEX |
+ } |
+ Format(instr, mnemonic, form); |
+} |
+ |
+ |
+void Disassembler::VisitLoadStoreUnsignedOffset(Instruction* instr) { |
+ const char *mnemonic = "unimplemented"; |
+ const char *form = "(LoadStoreUnsignedOffset)"; |
+ |
+ switch (instr->Mask(LoadStoreUnsignedOffsetMask)) { |
+ #define LS_UNSIGNEDOFFSET(A, B, C) \ |
+ case A##_unsigned: mnemonic = B; form = C ", ['Xns'ILU]"; break; |
+ LOAD_STORE_LIST(LS_UNSIGNEDOFFSET) |
+ #undef LS_UNSIGNEDOFFSET |
+ case PRFM_unsigned: mnemonic = "prfm"; form = "'PrefOp, ['Xn'ILU]"; |
+ } |
+ Format(instr, mnemonic, form); |
+} |
+ |
+ |
+void Disassembler::VisitLoadStoreRegisterOffset(Instruction* instr) { |
+ const char *mnemonic = "unimplemented"; |
+ const char *form = "(LoadStoreRegisterOffset)"; |
+ |
+ switch (instr->Mask(LoadStoreRegisterOffsetMask)) { |
+ #define LS_REGISTEROFFSET(A, B, C) \ |
+ case A##_reg: mnemonic = B; form = C ", ['Xns, 'Offsetreg]"; break; |
+ LOAD_STORE_LIST(LS_REGISTEROFFSET) |
+ #undef LS_REGISTEROFFSET |
+ case PRFM_reg: mnemonic = "prfm"; form = "'PrefOp, ['Xns, 'Offsetreg]"; |
+ } |
+ Format(instr, mnemonic, form); |
+} |
+ |
+ |
+void Disassembler::VisitLoadStoreUnscaledOffset(Instruction* instr) { |
+ const char *mnemonic = "unimplemented"; |
+ const char *form = "'Wt, ['Xns'ILS]"; |
+ const char *form_x = "'Xt, ['Xns'ILS]"; |
+ const char *form_s = "'St, ['Xns'ILS]"; |
+ const char *form_d = "'Dt, ['Xns'ILS]"; |
+ |
+ switch (instr->Mask(LoadStoreUnscaledOffsetMask)) { |
+ case STURB_w: mnemonic = "sturb"; break; |
+ case STURH_w: mnemonic = "sturh"; break; |
+ case STUR_w: mnemonic = "stur"; break; |
+ case STUR_x: mnemonic = "stur"; form = form_x; break; |
+ case STUR_s: mnemonic = "stur"; form = form_s; break; |
+ case STUR_d: mnemonic = "stur"; form = form_d; break; |
+ case LDURB_w: mnemonic = "ldurb"; break; |
+ case LDURH_w: mnemonic = "ldurh"; break; |
+ case LDUR_w: mnemonic = "ldur"; break; |
+ case LDUR_x: mnemonic = "ldur"; form = form_x; break; |
+ case LDUR_s: mnemonic = "ldur"; form = form_s; break; |
+ case LDUR_d: mnemonic = "ldur"; form = form_d; break; |
+ case LDURSB_x: form = form_x; // Fall through. |
+ case LDURSB_w: mnemonic = "ldursb"; break; |
+ case LDURSH_x: form = form_x; // Fall through. |
+ case LDURSH_w: mnemonic = "ldursh"; break; |
+ case LDURSW_x: mnemonic = "ldursw"; form = form_x; break; |
+ default: form = "(LoadStoreUnscaledOffset)"; |
+ } |
+ Format(instr, mnemonic, form); |
+} |
+ |
+ |
+void Disassembler::VisitLoadLiteral(Instruction* instr) { |
+ const char *mnemonic = "ldr"; |
+ const char *form = "(LoadLiteral)"; |
+ |
+ switch (instr->Mask(LoadLiteralMask)) { |
+ case LDR_w_lit: form = "'Wt, 'ILLiteral 'LValue"; break; |
+ case LDR_x_lit: form = "'Xt, 'ILLiteral 'LValue"; break; |
+ case LDR_s_lit: form = "'St, 'ILLiteral 'LValue"; break; |
+ case LDR_d_lit: form = "'Dt, 'ILLiteral 'LValue"; break; |
+ default: mnemonic = "unimplemented"; |
+ } |
+ Format(instr, mnemonic, form); |
+} |
+ |
+ |
+#define LOAD_STORE_PAIR_LIST(V) \ |
+ V(STP_w, "stp", "'Wt, 'Wt2", "4") \ |
+ V(LDP_w, "ldp", "'Wt, 'Wt2", "4") \ |
+ V(LDPSW_x, "ldpsw", "'Xt, 'Xt2", "4") \ |
+ V(STP_x, "stp", "'Xt, 'Xt2", "8") \ |
+ V(LDP_x, "ldp", "'Xt, 'Xt2", "8") \ |
+ V(STP_s, "stp", "'St, 'St2", "4") \ |
+ V(LDP_s, "ldp", "'St, 'St2", "4") \ |
+ V(STP_d, "stp", "'Dt, 'Dt2", "8") \ |
+ V(LDP_d, "ldp", "'Dt, 'Dt2", "8") |
+ |
+void Disassembler::VisitLoadStorePairPostIndex(Instruction* instr) { |
+ const char *mnemonic = "unimplemented"; |
+ const char *form = "(LoadStorePairPostIndex)"; |
+ |
+ switch (instr->Mask(LoadStorePairPostIndexMask)) { |
+ #define LSP_POSTINDEX(A, B, C, D) \ |
+ case A##_post: mnemonic = B; form = C ", ['Xns]'ILP" D; break; |
+ LOAD_STORE_PAIR_LIST(LSP_POSTINDEX) |
+ #undef LSP_POSTINDEX |
+ } |
+ Format(instr, mnemonic, form); |
+} |
+ |
+ |
+void Disassembler::VisitLoadStorePairPreIndex(Instruction* instr) { |
+ const char *mnemonic = "unimplemented"; |
+ const char *form = "(LoadStorePairPreIndex)"; |
+ |
+ switch (instr->Mask(LoadStorePairPreIndexMask)) { |
+ #define LSP_PREINDEX(A, B, C, D) \ |
+ case A##_pre: mnemonic = B; form = C ", ['Xns'ILP" D "]!"; break; |
+ LOAD_STORE_PAIR_LIST(LSP_PREINDEX) |
+ #undef LSP_PREINDEX |
+ } |
+ Format(instr, mnemonic, form); |
+} |
+ |
+ |
+void Disassembler::VisitLoadStorePairOffset(Instruction* instr) { |
+ const char *mnemonic = "unimplemented"; |
+ const char *form = "(LoadStorePairOffset)"; |
+ |
+ switch (instr->Mask(LoadStorePairOffsetMask)) { |
+ #define LSP_OFFSET(A, B, C, D) \ |
+ case A##_off: mnemonic = B; form = C ", ['Xns'ILP" D "]"; break; |
+ LOAD_STORE_PAIR_LIST(LSP_OFFSET) |
+ #undef LSP_OFFSET |
+ } |
+ Format(instr, mnemonic, form); |
+} |
+ |
+ |
+void Disassembler::VisitLoadStorePairNonTemporal(Instruction* instr) { |
+ const char *mnemonic = "unimplemented"; |
+ const char *form; |
+ |
+ switch (instr->Mask(LoadStorePairNonTemporalMask)) { |
+ case STNP_w: mnemonic = "stnp"; form = "'Wt, 'Wt2, ['Xns'ILP4]"; break; |
+ case LDNP_w: mnemonic = "ldnp"; form = "'Wt, 'Wt2, ['Xns'ILP4]"; break; |
+ case STNP_x: mnemonic = "stnp"; form = "'Xt, 'Xt2, ['Xns'ILP8]"; break; |
+ case LDNP_x: mnemonic = "ldnp"; form = "'Xt, 'Xt2, ['Xns'ILP8]"; break; |
+ case STNP_s: mnemonic = "stnp"; form = "'St, 'St2, ['Xns'ILP4]"; break; |
+ case LDNP_s: mnemonic = "ldnp"; form = "'St, 'St2, ['Xns'ILP4]"; break; |
+ case STNP_d: mnemonic = "stnp"; form = "'Dt, 'Dt2, ['Xns'ILP8]"; break; |
+ case LDNP_d: mnemonic = "ldnp"; form = "'Dt, 'Dt2, ['Xns'ILP8]"; break; |
+ default: form = "(LoadStorePairNonTemporal)"; |
+ } |
+ Format(instr, mnemonic, form); |
+} |
+ |
+ |
+void Disassembler::VisitFPCompare(Instruction* instr) { |
+ const char *mnemonic = "unimplemented"; |
+ const char *form = "'Fn, 'Fm"; |
+ const char *form_zero = "'Fn, #0.0"; |
+ |
+ switch (instr->Mask(FPCompareMask)) { |
+ case FCMP_s_zero: |
+ case FCMP_d_zero: form = form_zero; // Fall through. |
+ case FCMP_s: |
+ case FCMP_d: mnemonic = "fcmp"; break; |
+ default: form = "(FPCompare)"; |
+ } |
+ Format(instr, mnemonic, form); |
+} |
+ |
+ |
+void Disassembler::VisitFPConditionalCompare(Instruction* instr) { |
+ const char *mnemonic = "unimplemented"; |
+ const char *form = "'Fn, 'Fm, 'INzcv, 'Cond"; |
+ |
+ switch (instr->Mask(FPConditionalCompareMask)) { |
+ case FCCMP_s: |
+ case FCCMP_d: mnemonic = "fccmp"; break; |
+ case FCCMPE_s: |
+ case FCCMPE_d: mnemonic = "fccmpe"; break; |
+ default: form = "(FPConditionalCompare)"; |
+ } |
+ Format(instr, mnemonic, form); |
+} |
+ |
+ |
+void Disassembler::VisitFPConditionalSelect(Instruction* instr) { |
+ const char *mnemonic = ""; |
+ const char *form = "'Fd, 'Fn, 'Fm, 'Cond"; |
+ |
+ switch (instr->Mask(FPConditionalSelectMask)) { |
+ case FCSEL_s: |
+ case FCSEL_d: mnemonic = "fcsel"; break; |
+ default: UNREACHABLE(); |
+ } |
+ Format(instr, mnemonic, form); |
+} |
+ |
+ |
+void Disassembler::VisitFPDataProcessing1Source(Instruction* instr) { |
+ const char *mnemonic = "unimplemented"; |
+ const char *form = "'Fd, 'Fn"; |
+ |
+ switch (instr->Mask(FPDataProcessing1SourceMask)) { |
+ #define FORMAT(A, B) \ |
+ case A##_s: \ |
+ case A##_d: mnemonic = B; break; |
+ FORMAT(FMOV, "fmov"); |
+ FORMAT(FABS, "fabs"); |
+ FORMAT(FNEG, "fneg"); |
+ FORMAT(FSQRT, "fsqrt"); |
+ FORMAT(FRINTN, "frintn"); |
+ FORMAT(FRINTP, "frintp"); |
+ FORMAT(FRINTM, "frintm"); |
+ FORMAT(FRINTZ, "frintz"); |
+ FORMAT(FRINTA, "frinta"); |
+ FORMAT(FRINTX, "frintx"); |
+ FORMAT(FRINTI, "frinti"); |
+ #undef FORMAT |
+ case FCVT_ds: mnemonic = "fcvt"; form = "'Dd, 'Sn"; break; |
+ case FCVT_sd: mnemonic = "fcvt"; form = "'Sd, 'Dn"; break; |
+ default: form = "(FPDataProcessing1Source)"; |
+ } |
+ Format(instr, mnemonic, form); |
+} |
+ |
+ |
+void Disassembler::VisitFPDataProcessing2Source(Instruction* instr) { |
+ const char *mnemonic = ""; |
+ const char *form = "'Fd, 'Fn, 'Fm"; |
+ |
+ switch (instr->Mask(FPDataProcessing2SourceMask)) { |
+ #define FORMAT(A, B) \ |
+ case A##_s: \ |
+ case A##_d: mnemonic = B; break; |
+ FORMAT(FMUL, "fmul"); |
+ FORMAT(FDIV, "fdiv"); |
+ FORMAT(FADD, "fadd"); |
+ FORMAT(FSUB, "fsub"); |
+ FORMAT(FMAX, "fmax"); |
+ FORMAT(FMIN, "fmin"); |
+ FORMAT(FMAXNM, "fmaxnm"); |
+ FORMAT(FMINNM, "fminnm"); |
+ FORMAT(FNMUL, "fnmul"); |
+ #undef FORMAT |
+ default: UNREACHABLE(); |
+ } |
+ Format(instr, mnemonic, form); |
+} |
+ |
+ |
+void Disassembler::VisitFPDataProcessing3Source(Instruction* instr) { |
+ const char *mnemonic = ""; |
+ const char *form = "'Fd, 'Fn, 'Fm, 'Fa"; |
+ |
+ switch (instr->Mask(FPDataProcessing3SourceMask)) { |
+ #define FORMAT(A, B) \ |
+ case A##_s: \ |
+ case A##_d: mnemonic = B; break; |
+ FORMAT(FMADD, "fmadd"); |
+ FORMAT(FMSUB, "fmsub"); |
+ FORMAT(FNMADD, "fnmadd"); |
+ FORMAT(FNMSUB, "fnmsub"); |
+ #undef FORMAT |
+ default: UNREACHABLE(); |
+ } |
+ Format(instr, mnemonic, form); |
+} |
+ |
+ |
+void Disassembler::VisitFPImmediate(Instruction* instr) { |
+ const char *mnemonic = ""; |
+ const char *form = "(FPImmediate)"; |
+ |
+ switch (instr->Mask(FPImmediateMask)) { |
+ case FMOV_s_imm: mnemonic = "fmov"; form = "'Sd, 'IFPSingle"; break; |
+ case FMOV_d_imm: mnemonic = "fmov"; form = "'Dd, 'IFPDouble"; break; |
+ default: UNREACHABLE(); |
+ } |
+ Format(instr, mnemonic, form); |
+} |
+ |
+ |
+void Disassembler::VisitFPIntegerConvert(Instruction* instr) { |
+ const char *mnemonic = "unimplemented"; |
+ const char *form = "(FPIntegerConvert)"; |
+ const char *form_rf = "'Rd, 'Fn"; |
+ const char *form_fr = "'Fd, 'Rn"; |
+ |
+ switch (instr->Mask(FPIntegerConvertMask)) { |
+ case FMOV_ws: |
+ case FMOV_xd: mnemonic = "fmov"; form = form_rf; break; |
+ case FMOV_sw: |
+ case FMOV_dx: mnemonic = "fmov"; form = form_fr; break; |
+ case FCVTAS_ws: |
+ case FCVTAS_xs: |
+ case FCVTAS_wd: |
+ case FCVTAS_xd: mnemonic = "fcvtas"; form = form_rf; break; |
+ case FCVTAU_ws: |
+ case FCVTAU_xs: |
+ case FCVTAU_wd: |
+ case FCVTAU_xd: mnemonic = "fcvtau"; form = form_rf; break; |
+ case FCVTMS_ws: |
+ case FCVTMS_xs: |
+ case FCVTMS_wd: |
+ case FCVTMS_xd: mnemonic = "fcvtms"; form = form_rf; break; |
+ case FCVTMU_ws: |
+ case FCVTMU_xs: |
+ case FCVTMU_wd: |
+ case FCVTMU_xd: mnemonic = "fcvtmu"; form = form_rf; break; |
+ case FCVTNS_ws: |
+ case FCVTNS_xs: |
+ case FCVTNS_wd: |
+ case FCVTNS_xd: mnemonic = "fcvtns"; form = form_rf; break; |
+ case FCVTNU_ws: |
+ case FCVTNU_xs: |
+ case FCVTNU_wd: |
+ case FCVTNU_xd: mnemonic = "fcvtnu"; form = form_rf; break; |
+ case FCVTZU_xd: |
+ case FCVTZU_ws: |
+ case FCVTZU_wd: |
+ case FCVTZU_xs: mnemonic = "fcvtzu"; form = form_rf; break; |
+ case FCVTZS_xd: |
+ case FCVTZS_wd: |
+ case FCVTZS_xs: |
+ case FCVTZS_ws: mnemonic = "fcvtzs"; form = form_rf; break; |
+ case SCVTF_sw: |
+ case SCVTF_sx: |
+ case SCVTF_dw: |
+ case SCVTF_dx: mnemonic = "scvtf"; form = form_fr; break; |
+ case UCVTF_sw: |
+ case UCVTF_sx: |
+ case UCVTF_dw: |
+ case UCVTF_dx: mnemonic = "ucvtf"; form = form_fr; break; |
+ } |
+ Format(instr, mnemonic, form); |
+} |
+ |
+ |
+void Disassembler::VisitFPFixedPointConvert(Instruction* instr) { |
+ const char *mnemonic = ""; |
+ const char *form = "'Rd, 'Fn, 'IFPFBits"; |
+ const char *form_fr = "'Fd, 'Rn, 'IFPFBits"; |
+ |
+ switch (instr->Mask(FPFixedPointConvertMask)) { |
+ case FCVTZS_ws_fixed: |
+ case FCVTZS_xs_fixed: |
+ case FCVTZS_wd_fixed: |
+ case FCVTZS_xd_fixed: mnemonic = "fcvtzs"; break; |
+ case FCVTZU_ws_fixed: |
+ case FCVTZU_xs_fixed: |
+ case FCVTZU_wd_fixed: |
+ case FCVTZU_xd_fixed: mnemonic = "fcvtzu"; break; |
+ case SCVTF_sw_fixed: |
+ case SCVTF_sx_fixed: |
+ case SCVTF_dw_fixed: |
+ case SCVTF_dx_fixed: mnemonic = "scvtf"; form = form_fr; break; |
+ case UCVTF_sw_fixed: |
+ case UCVTF_sx_fixed: |
+ case UCVTF_dw_fixed: |
+ case UCVTF_dx_fixed: mnemonic = "ucvtf"; form = form_fr; break; |
+ } |
+ Format(instr, mnemonic, form); |
+} |
+ |
+ |
+void Disassembler::VisitSystem(Instruction* instr) { |
+ // Some system instructions hijack their Op and Cp fields to represent a |
+ // range of immediates instead of indicating a different instruction. This |
+ // makes the decoding tricky. |
+ const char *mnemonic = "unimplemented"; |
+ const char *form = "(System)"; |
+ |
+ if (instr->Mask(SystemSysRegFMask) == SystemSysRegFixed) { |
+ switch (instr->Mask(SystemSysRegMask)) { |
+ case MRS: { |
+ mnemonic = "mrs"; |
+ switch (instr->ImmSystemRegister()) { |
+ case NZCV: form = "'Xt, nzcv"; break; |
+ case FPCR: form = "'Xt, fpcr"; break; |
+ default: form = "'Xt, (unknown)"; break; |
+ } |
+ break; |
+ } |
+ case MSR: { |
+ mnemonic = "msr"; |
+ switch (instr->ImmSystemRegister()) { |
+ case NZCV: form = "nzcv, 'Xt"; break; |
+ case FPCR: form = "fpcr, 'Xt"; break; |
+ default: form = "(unknown), 'Xt"; break; |
+ } |
+ break; |
+ } |
+ } |
+ } else if (instr->Mask(SystemHintFMask) == SystemHintFixed) { |
+ ASSERT(instr->Mask(SystemHintMask) == HINT); |
+ switch (instr->ImmHint()) { |
+ case NOP: { |
+ mnemonic = "nop"; |
+ form = NULL; |
+ break; |
+ } |
+ } |
+ } else if (instr->Mask(MemBarrierFMask) == MemBarrierFixed) { |
+ switch (instr->Mask(MemBarrierMask)) { |
+ case DMB: { |
+ mnemonic = "dmb"; |
+ form = "'M"; |
+ break; |
+ } |
+ case DSB: { |
+ mnemonic = "dsb"; |
+ form = "'M"; |
+ break; |
+ } |
+ case ISB: { |
+ mnemonic = "isb"; |
+ form = NULL; |
+ break; |
+ } |
+ } |
+ } |
+ |
+ Format(instr, mnemonic, form); |
+} |
+ |
+ |
+void Disassembler::VisitException(Instruction* instr) { |
+ const char *mnemonic = "unimplemented"; |
+ const char *form = "'IDebug"; |
+ |
+ switch (instr->Mask(ExceptionMask)) { |
+ case HLT: mnemonic = "hlt"; break; |
+ case BRK: mnemonic = "brk"; break; |
+ case SVC: mnemonic = "svc"; break; |
+ case HVC: mnemonic = "hvc"; break; |
+ case SMC: mnemonic = "smc"; break; |
+ case DCPS1: mnemonic = "dcps1"; form = "{'IDebug}"; break; |
+ case DCPS2: mnemonic = "dcps2"; form = "{'IDebug}"; break; |
+ case DCPS3: mnemonic = "dcps3"; form = "{'IDebug}"; break; |
+ default: form = "(Exception)"; |
+ } |
+ Format(instr, mnemonic, form); |
+} |
+ |
+ |
+void Disassembler::VisitUnimplemented(Instruction* instr) { |
+ Format(instr, "unimplemented", "(Unimplemented)"); |
+} |
+ |
+ |
+void Disassembler::VisitUnallocated(Instruction* instr) { |
+ Format(instr, "unallocated", "(Unallocated)"); |
+} |
+ |
+ |
+void Disassembler::ProcessOutput(Instruction* /*instr*/) { |
+ // The base disasm does nothing more than disassembling into a buffer. |
+} |
+ |
+ |
+void Disassembler::Format(Instruction* instr, const char* mnemonic, |
+ const char* format) { |
+ // TODO(mcapewel) don't think I can use the instr address here - there needs |
+ // to be a base address too |
+ ASSERT(mnemonic != NULL); |
+ ResetOutput(); |
+ Substitute(instr, mnemonic); |
+ if (format != NULL) { |
+ buffer_[buffer_pos_++] = ' '; |
+ Substitute(instr, format); |
+ } |
+ buffer_[buffer_pos_] = 0; |
+ ProcessOutput(instr); |
+} |
+ |
+ |
+void Disassembler::Substitute(Instruction* instr, const char* string) { |
+ char chr = *string++; |
+ while (chr != '\0') { |
+ if (chr == '\'') { |
+ string += SubstituteField(instr, string); |
+ } else { |
+ buffer_[buffer_pos_++] = chr; |
+ } |
+ chr = *string++; |
+ } |
+} |
+ |
+ |
+int Disassembler::SubstituteField(Instruction* instr, const char* format) { |
+ switch (format[0]) { |
+ case 'R': // Register. X or W, selected by sf bit. |
+ case 'F': // FP Register. S or D, selected by type field. |
+ case 'W': |
+ case 'X': |
+ case 'S': |
+ case 'D': return SubstituteRegisterField(instr, format); |
+ case 'I': return SubstituteImmediateField(instr, format); |
+ case 'L': return SubstituteLiteralField(instr, format); |
+ case 'H': return SubstituteShiftField(instr, format); |
+ case 'P': return SubstitutePrefetchField(instr, format); |
+ case 'C': return SubstituteConditionField(instr, format); |
+ case 'E': return SubstituteExtendField(instr, format); |
+ case 'A': return SubstitutePCRelAddressField(instr, format); |
+ case 'B': return SubstituteBranchTargetField(instr, format); |
+ case 'O': return SubstituteLSRegOffsetField(instr, format); |
+ case 'M': return SubstituteBarrierField(instr, format); |
+ default: { |
+ UNREACHABLE(); |
+ return 1; |
+ } |
+ } |
+} |
+ |
+ |
+int Disassembler::SubstituteRegisterField(Instruction* instr, |
+ const char* format) { |
+ unsigned reg_num = 0; |
+ unsigned field_len = 2; |
+ switch (format[1]) { |
+ case 'd': reg_num = instr->Rd(); break; |
+ case 'n': reg_num = instr->Rn(); break; |
+ case 'm': reg_num = instr->Rm(); break; |
+ case 'a': reg_num = instr->Ra(); break; |
+ case 't': { |
+ if (format[2] == '2') { |
+ reg_num = instr->Rt2(); |
+ field_len = 3; |
+ } else { |
+ reg_num = instr->Rt(); |
+ } |
+ break; |
+ } |
+ default: UNREACHABLE(); |
+ } |
+ |
+ // Increase field length for registers tagged as stack. |
+ if (format[2] == 's') { |
+ field_len = 3; |
+ } |
+ |
+ char reg_type; |
+ if (format[0] == 'R') { |
+ // Register type is R: use sf bit to choose X and W. |
+ reg_type = instr->SixtyFourBits() ? 'x' : 'w'; |
+ } else if (format[0] == 'F') { |
+ // Floating-point register: use type field to choose S or D. |
+ reg_type = ((instr->FPType() & 1) == 0) ? 's' : 'd'; |
+ } else { |
+ // Register type is specified. Make it lower case. |
+ reg_type = format[0] + 0x20; |
+ } |
+ |
+ if ((reg_num != kZeroRegCode) || (reg_type == 's') || (reg_type == 'd')) { |
+ // A normal register: w0 - w30, x0 - x30, s0 - s31, d0 - d31. |
+ |
+ // Filter special registers |
+ if ((reg_type == 'x') && (reg_num == 27)) { |
+ AppendToOutput("cp"); |
+ } else if ((reg_type == 'x') && (reg_num == 28)) { |
+ AppendToOutput("jssp"); |
+ } else if ((reg_type == 'x') && (reg_num == 29)) { |
+ AppendToOutput("fp"); |
+ } else if ((reg_type == 'x') && (reg_num == 30)) { |
+ AppendToOutput("lr"); |
+ } else { |
+ AppendToOutput("%c%d", reg_type, reg_num); |
+ } |
+ } else if (format[2] == 's') { |
+ // Disassemble w31/x31 as stack pointer wcsp/csp. |
+ AppendToOutput("%s", (reg_type == 'w') ? "wcsp" : "csp"); |
+ } else { |
+ // Disassemble w31/x31 as zero register wzr/xzr. |
+ AppendToOutput("%czr", reg_type); |
+ } |
+ |
+ return field_len; |
+} |
+ |
+ |
+int Disassembler::SubstituteImmediateField(Instruction* instr, |
+ const char* format) { |
+ ASSERT(format[0] == 'I'); |
+ |
+ switch (format[1]) { |
+ case 'M': { // IMoveImm or IMoveLSL. |
+ if (format[5] == 'I') { |
+ uint64_t imm = instr->ImmMoveWide() << (16 * instr->ShiftMoveWide()); |
+ AppendToOutput("#0x%" PRIx64, imm); |
+ } else { |
+ ASSERT(format[5] == 'L'); |
+ AppendToOutput("#0x%" PRIx64, instr->ImmMoveWide()); |
+ if (instr->ShiftMoveWide() > 0) { |
+ AppendToOutput(", lsl #%d", 16 * instr->ShiftMoveWide()); |
+ } |
+ } |
+ return 8; |
+ } |
+ case 'L': { |
+ switch (format[2]) { |
+ case 'L': { // ILLiteral - Immediate Load Literal. |
+ AppendToOutput("pc%+" PRId64, |
+ instr->ImmLLiteral() << kLiteralEntrySizeLog2); |
+ return 9; |
+ } |
+ case 'S': { // ILS - Immediate Load/Store. |
+ if (instr->ImmLS() != 0) { |
+ AppendToOutput(", #%" PRId64, instr->ImmLS()); |
+ } |
+ return 3; |
+ } |
+ case 'P': { // ILPx - Immediate Load/Store Pair, x = access size. |
+ if (instr->ImmLSPair() != 0) { |
+ // format[3] is the scale value. Convert to a number. |
+ int scale = format[3] - 0x30; |
+ AppendToOutput(", #%" PRId64, instr->ImmLSPair() * scale); |
+ } |
+ return 4; |
+ } |
+ case 'U': { // ILU - Immediate Load/Store Unsigned. |
+ if (instr->ImmLSUnsigned() != 0) { |
+ AppendToOutput(", #%" PRIu64, |
+ instr->ImmLSUnsigned() << instr->SizeLS()); |
+ } |
+ return 3; |
+ } |
+ } |
+ } |
+ case 'C': { // ICondB - Immediate Conditional Branch. |
+ int64_t offset = instr->ImmCondBranch() << 2; |
+ char sign = (offset >= 0) ? '+' : '-'; |
+ AppendToOutput("#%c0x%" PRIx64, sign, offset); |
+ return 6; |
+ } |
+ case 'A': { // IAddSub. |
+ ASSERT(instr->ShiftAddSub() <= 1); |
+ int64_t imm = instr->ImmAddSub() << (12 * instr->ShiftAddSub()); |
+ AppendToOutput("#0x%" PRIx64 " (%" PRId64 ")", imm, imm); |
+ return 7; |
+ } |
+ case 'F': { // IFPSingle, IFPDouble or IFPFBits. |
+ if (format[3] == 'F') { // IFPFBits. |
+ AppendToOutput("#%d", 64 - instr->FPScale()); |
+ return 8; |
+ } else { |
+ AppendToOutput("#0x%" PRIx64 " (%.4f)", instr->ImmFP(), |
+ format[3] == 'S' ? instr->ImmFP32() : instr->ImmFP64()); |
+ return 9; |
+ } |
+ } |
+ case 'T': { // ITri - Immediate Triangular Encoded. |
+ AppendToOutput("#0x%" PRIx64, instr->ImmLogical()); |
+ return 4; |
+ } |
+ case 'N': { // INzcv. |
+ int nzcv = (instr->Nzcv() << Flags_offset); |
+ AppendToOutput("#%c%c%c%c", ((nzcv & NFlag) == 0) ? 'n' : 'N', |
+ ((nzcv & ZFlag) == 0) ? 'z' : 'Z', |
+ ((nzcv & CFlag) == 0) ? 'c' : 'C', |
+ ((nzcv & VFlag) == 0) ? 'v' : 'V'); |
+ return 5; |
+ } |
+ case 'P': { // IP - Conditional compare. |
+ AppendToOutput("#%d", instr->ImmCondCmp()); |
+ return 2; |
+ } |
+ case 'B': { // Bitfields. |
+ return SubstituteBitfieldImmediateField(instr, format); |
+ } |
+ case 'E': { // IExtract. |
+ AppendToOutput("#%d", instr->ImmS()); |
+ return 8; |
+ } |
+ case 'S': { // IS - Test and branch bit. |
+ AppendToOutput("#%d", (instr->ImmTestBranchBit5() << 5) | |
+ instr->ImmTestBranchBit40()); |
+ return 2; |
+ } |
+ case 'D': { // IDebug - HLT and BRK instructions. |
+ AppendToOutput("#0x%x", instr->ImmException()); |
+ return 6; |
+ } |
+ default: { |
+ UNIMPLEMENTED(); |
+ return 0; |
+ } |
+ } |
+} |
+ |
+ |
+int Disassembler::SubstituteBitfieldImmediateField(Instruction* instr, |
+ const char* format) { |
+ ASSERT((format[0] == 'I') && (format[1] == 'B')); |
+ unsigned r = instr->ImmR(); |
+ unsigned s = instr->ImmS(); |
+ |
+ switch (format[2]) { |
+ case 'r': { // IBr. |
+ AppendToOutput("#%d", r); |
+ return 3; |
+ } |
+ case 's': { // IBs+1 or IBs-r+1. |
+ if (format[3] == '+') { |
+ AppendToOutput("#%d", s + 1); |
+ return 5; |
+ } else { |
+ ASSERT(format[3] == '-'); |
+ AppendToOutput("#%d", s - r + 1); |
+ return 7; |
+ } |
+ } |
+ case 'Z': { // IBZ-r. |
+ ASSERT((format[3] == '-') && (format[4] == 'r')); |
+ unsigned reg_size = (instr->SixtyFourBits() == 1) ? kXRegSize : kWRegSize; |
+ AppendToOutput("#%d", reg_size - r); |
+ return 5; |
+ } |
+ default: { |
+ UNREACHABLE(); |
+ return 0; |
+ } |
+ } |
+} |
+ |
+ |
+int Disassembler::SubstituteLiteralField(Instruction* instr, |
+ const char* format) { |
+ ASSERT(strncmp(format, "LValue", 6) == 0); |
+ USE(format); |
+ |
+ switch (instr->Mask(LoadLiteralMask)) { |
+ case LDR_w_lit: |
+ case LDR_x_lit: |
+ case LDR_s_lit: |
+ case LDR_d_lit: AppendToOutput("(addr %p)", instr->LiteralAddress()); break; |
+ default: UNREACHABLE(); |
+ } |
+ |
+ return 6; |
+} |
+ |
+ |
+int Disassembler::SubstituteShiftField(Instruction* instr, const char* format) { |
+ ASSERT(format[0] == 'H'); |
+ ASSERT(instr->ShiftDP() <= 0x3); |
+ |
+ switch (format[1]) { |
+ case 'D': { // HDP. |
+ ASSERT(instr->ShiftDP() != ROR); |
+ } // Fall through. |
+ case 'L': { // HLo. |
+ if (instr->ImmDPShift() != 0) { |
+ const char* shift_type[] = {"lsl", "lsr", "asr", "ror"}; |
+ AppendToOutput(", %s #%" PRId64, shift_type[instr->ShiftDP()], |
+ instr->ImmDPShift()); |
+ } |
+ return 3; |
+ } |
+ default: |
+ UNIMPLEMENTED(); |
+ return 0; |
+ } |
+} |
+ |
+ |
+int Disassembler::SubstituteConditionField(Instruction* instr, |
+ const char* format) { |
+ ASSERT(format[0] == 'C'); |
+ const char* condition_code[] = { "eq", "ne", "hs", "lo", |
+ "mi", "pl", "vs", "vc", |
+ "hi", "ls", "ge", "lt", |
+ "gt", "le", "al", "nv" }; |
+ int cond; |
+ switch (format[1]) { |
+ case 'B': cond = instr->ConditionBranch(); break; |
+ case 'I': { |
+ cond = InvertCondition(static_cast<Condition>(instr->Condition())); |
+ break; |
+ } |
+ default: cond = instr->Condition(); |
+ } |
+ AppendToOutput("%s", condition_code[cond]); |
+ return 4; |
+} |
+ |
+ |
+int Disassembler::SubstitutePCRelAddressField(Instruction* instr, |
+ const char* format) { |
+ USE(format); |
+ ASSERT(strncmp(format, "AddrPCRel", 9) == 0); |
+ |
+ int offset = instr->ImmPCRel(); |
+ |
+ // Only ADR (AddrPCRelByte) is supported. |
+ ASSERT(strcmp(format, "AddrPCRelByte") == 0); |
+ |
+ char sign = '+'; |
+ if (offset < 0) { |
+ offset = -offset; |
+ sign = '-'; |
+ } |
+ // TODO(jbramley): Can we print the target address here? |
+ AppendToOutput("#%c0x%x", sign, offset); |
+ return 13; |
+} |
+ |
+ |
+int Disassembler::SubstituteBranchTargetField(Instruction* instr, |
+ const char* format) { |
+ ASSERT(strncmp(format, "BImm", 4) == 0); |
+ |
+ int64_t offset = 0; |
+ switch (format[5]) { |
+ // BImmUncn - unconditional branch immediate. |
+ case 'n': offset = instr->ImmUncondBranch(); break; |
+ // BImmCond - conditional branch immediate. |
+ case 'o': offset = instr->ImmCondBranch(); break; |
+ // BImmCmpa - compare and branch immediate. |
+ case 'm': offset = instr->ImmCmpBranch(); break; |
+ // BImmTest - test and branch immediate. |
+ case 'e': offset = instr->ImmTestBranch(); break; |
+ default: UNIMPLEMENTED(); |
+ } |
+ offset <<= kInstructionSizeLog2; |
+ char sign = '+'; |
+ if (offset < 0) { |
+ offset = -offset; |
+ sign = '-'; |
+ } |
+ // TODO(mcapewel): look up pc + offset in label table. |
+ AppendToOutput("#%c0x%" PRIx64, sign, offset); |
+ return 8; |
+} |
+ |
+ |
+int Disassembler::SubstituteExtendField(Instruction* instr, |
+ const char* format) { |
+ ASSERT(strncmp(format, "Ext", 3) == 0); |
+ ASSERT(instr->ExtendMode() <= 7); |
+ USE(format); |
+ |
+ const char* extend_mode[] = { "uxtb", "uxth", "uxtw", "uxtx", |
+ "sxtb", "sxth", "sxtw", "sxtx" }; |
+ |
+ // If rd or rn is SP, uxtw on 32-bit registers and uxtx on 64-bit |
+ // registers becomes lsl. |
+ if (((instr->Rd() == kZeroRegCode) || (instr->Rn() == kZeroRegCode)) && |
+ (((instr->ExtendMode() == UXTW) && (instr->SixtyFourBits() == 0)) || |
+ (instr->ExtendMode() == UXTX))) { |
+ if (instr->ImmExtendShift() > 0) { |
+ AppendToOutput(", lsl #%d", instr->ImmExtendShift()); |
+ } |
+ } else { |
+ AppendToOutput(", %s", extend_mode[instr->ExtendMode()]); |
+ if (instr->ImmExtendShift() > 0) { |
+ AppendToOutput(" #%d", instr->ImmExtendShift()); |
+ } |
+ } |
+ return 3; |
+} |
+ |
+ |
+int Disassembler::SubstituteLSRegOffsetField(Instruction* instr, |
+ const char* format) { |
+ ASSERT(strncmp(format, "Offsetreg", 9) == 0); |
+ const char* extend_mode[] = { "undefined", "undefined", "uxtw", "lsl", |
+ "undefined", "undefined", "sxtw", "sxtx" }; |
+ USE(format); |
+ |
+ unsigned shift = instr->ImmShiftLS(); |
+ Extend ext = static_cast<Extend>(instr->ExtendMode()); |
+ char reg_type = ((ext == UXTW) || (ext == SXTW)) ? 'w' : 'x'; |
+ |
+ unsigned rm = instr->Rm(); |
+ if (rm == kZeroRegCode) { |
+ AppendToOutput("%czr", reg_type); |
+ } else { |
+ AppendToOutput("%c%d", reg_type, rm); |
+ } |
+ |
+ // Extend mode UXTX is an alias for shift mode LSL here. |
+ if (!((ext == UXTX) && (shift == 0))) { |
+ AppendToOutput(", %s", extend_mode[ext]); |
+ if (shift != 0) { |
+ AppendToOutput(" #%d", instr->SizeLS()); |
+ } |
+ } |
+ return 9; |
+} |
+ |
+ |
+int Disassembler::SubstitutePrefetchField(Instruction* instr, |
+ const char* format) { |
+ ASSERT(format[0] == 'P'); |
+ USE(format); |
+ |
+ int prefetch_mode = instr->PrefetchMode(); |
+ |
+ const char* ls = (prefetch_mode & 0x10) ? "st" : "ld"; |
+ int level = (prefetch_mode >> 1) + 1; |
+ const char* ks = (prefetch_mode & 1) ? "strm" : "keep"; |
+ |
+ AppendToOutput("p%sl%d%s", ls, level, ks); |
+ return 6; |
+} |
+ |
+int Disassembler::SubstituteBarrierField(Instruction* instr, |
+ const char* format) { |
+ ASSERT(format[0] == 'M'); |
+ USE(format); |
+ |
+ static const char* options[4][4] = { |
+ { "sy (0b0000)", "oshld", "oshst", "osh" }, |
+ { "sy (0b0100)", "nshld", "nshst", "nsh" }, |
+ { "sy (0b1000)", "ishld", "ishst", "ish" }, |
+ { "sy (0b1100)", "ld", "st", "sy" } |
+ }; |
+ int domain = instr->ImmBarrierDomain(); |
+ int type = instr->ImmBarrierType(); |
+ |
+ AppendToOutput("%s", options[domain][type]); |
+ return 1; |
+} |
+ |
+ |
+void Disassembler::ResetOutput() { |
+ buffer_pos_ = 0; |
+ buffer_[buffer_pos_] = 0; |
+} |
+ |
+ |
+void Disassembler::AppendToOutput(const char* format, ...) { |
+ va_list args; |
+ va_start(args, format); |
+ buffer_pos_ += vsnprintf(&buffer_[buffer_pos_], buffer_size_, format, args); |
+ va_end(args); |
+} |
+ |
+ |
+void PrintDisassembler::ProcessOutput(Instruction* instr) { |
+ fprintf(stream_, "0x%016" PRIx64 " %08" PRIx32 "\t\t%s\n", |
+ reinterpret_cast<uint64_t>(instr), instr->InstructionBits(), |
+ GetOutput()); |
+} |
+ |
+} } // namespace v8::internal |
+ |
+ |
+namespace disasm { |
+ |
+ |
+const char* NameConverter::NameOfAddress(byte* addr) const { |
+ v8::internal::OS::SNPrintF(tmp_buffer_, "%p", addr); |
+ return tmp_buffer_.start(); |
+} |
+ |
+ |
+const char* NameConverter::NameOfConstant(byte* addr) const { |
+ return NameOfAddress(addr); |
+} |
+ |
+ |
+const char* NameConverter::NameOfCPURegister(int reg) const { |
+ unsigned ureg = reg; // Avoid warnings about signed/unsigned comparisons. |
+ if (ureg >= v8::internal::kNumberOfRegisters) { |
+ return "noreg"; |
+ } |
+ if (ureg == v8::internal::kZeroRegCode) { |
+ return "xzr"; |
+ } |
+ v8::internal::OS::SNPrintF(tmp_buffer_, "x%u", ureg); |
+ return tmp_buffer_.start(); |
+} |
+ |
+ |
+const char* NameConverter::NameOfByteCPURegister(int reg) const { |
+ UNREACHABLE(); // A64 does not have the concept of a byte register |
+ return "nobytereg"; |
+} |
+ |
+ |
+const char* NameConverter::NameOfXMMRegister(int reg) const { |
+ UNREACHABLE(); // A64 does not have any XMM registers |
+ return "noxmmreg"; |
+} |
+ |
+ |
+const char* NameConverter::NameInCode(byte* addr) const { |
+ // The default name converter is called for unknown code, so we will not try |
+ // to access any memory. |
+ return ""; |
+} |
+ |
+ |
+//------------------------------------------------------------------------------ |
+ |
+class BufferDisassembler : public v8::internal::Disassembler { |
+ public: |
+ explicit BufferDisassembler(v8::internal::Vector<char> out_buffer) |
+ : out_buffer_(out_buffer) { } |
+ |
+ ~BufferDisassembler() { } |
+ |
+ virtual void ProcessOutput(v8::internal::Instruction* instr) { |
+ v8::internal::OS::SNPrintF(out_buffer_, "%s", GetOutput()); |
+ } |
+ |
+ private: |
+ v8::internal::Vector<char> out_buffer_; |
+}; |
+ |
+Disassembler::Disassembler(const NameConverter& converter) |
+ : converter_(converter) {} |
+ |
+ |
+Disassembler::~Disassembler() {} |
+ |
+ |
+int Disassembler::InstructionDecode(v8::internal::Vector<char> buffer, |
+ byte* instr) { |
+ v8::internal::Decoder decoder; |
+ BufferDisassembler disasm(buffer); |
+ decoder.AppendVisitor(&disasm); |
+ |
+ decoder.Decode(reinterpret_cast<v8::internal::Instruction*>(instr)); |
+ return v8::internal::kInstructionSize; |
+} |
+ |
+ |
+int Disassembler::ConstantPoolSizeAt(byte* instr) { |
+ return v8::internal::Assembler::ConstantPoolSizeAt( |
+ reinterpret_cast<v8::internal::Instruction*>(instr)); |
+} |
+ |
+ |
+void Disassembler::Disassemble(FILE* file, byte* start, byte* end) { |
+ v8::internal::Decoder decoder; |
+ v8::internal::PrintDisassembler disasm(file); |
+ decoder.AppendVisitor(&disasm); |
+ |
+ for (byte* pc = start; pc < end; pc += v8::internal::kInstructionSize) { |
+ decoder.Decode(reinterpret_cast<v8::internal::Instruction*>(pc)); |
+ } |
+} |
+ |
+} // namespace disasm |
+ |
+#endif // V8_TARGET_ARCH_A64 |