/* * Copyright (C) 2012 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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. */ #define __STDC_FORMAT_MACROS #include "config.h" #if USE(ARM64_DISASSEMBLER) #include "A64DOpcode.h" #include #include #include namespace JSC { namespace ARM64Disassembler { A64DOpcode::OpcodeGroup* A64DOpcode::opcodeTable[32]; const char* const A64DOpcode::s_conditionNames[16] = { "eq", "ne", "hs", "lo", "mi", "pl", "vs", "vc", "hi", "ls", "ge", "lt", "gt", "le", "al", "ne" }; const char* const A64DOpcode::s_optionName[8] = { "uxtb", "uxth", "uxtw", "uxtx", "sxtb", "sxth", "sxtw", "sxtx" }; const char* const A64DOpcode::s_shiftNames[4] = { "lsl", "lsr", "asl", "ror" }; const char A64DOpcode::s_FPRegisterPrefix[5] = { 'b', 'h', 's', 'd', 'q' }; struct OpcodeGroupInitializer { unsigned m_opcodeGroupNumber; uint32_t m_mask; uint32_t m_pattern; const char* (*m_format)(A64DOpcode*); }; #define OPCODE_GROUP_ENTRY(groupIndex, groupClass) \ { groupIndex, groupClass::mask, groupClass::pattern, groupClass::format } static OpcodeGroupInitializer opcodeGroupList[] = { OPCODE_GROUP_ENTRY(0x08, A64DOpcodeLoadStoreRegisterPair), OPCODE_GROUP_ENTRY(0x09, A64DOpcodeLoadStoreRegisterPair), OPCODE_GROUP_ENTRY(0x0a, A64DOpcodeLogicalShiftedRegister), OPCODE_GROUP_ENTRY(0x0b, A64DOpcodeAddSubtractExtendedRegister), OPCODE_GROUP_ENTRY(0x0b, A64DOpcodeAddSubtractShiftedRegister), OPCODE_GROUP_ENTRY(0x11, A64DOpcodeAddSubtractImmediate), OPCODE_GROUP_ENTRY(0x12, A64DOpcodeMoveWide), OPCODE_GROUP_ENTRY(0x12, A64DOpcodeLogicalImmediate), OPCODE_GROUP_ENTRY(0x13, A64DOpcodeBitfield), OPCODE_GROUP_ENTRY(0x13, A64DOpcodeExtract), OPCODE_GROUP_ENTRY(0x14, A64DOpcodeUnconditionalBranchImmediate), OPCODE_GROUP_ENTRY(0x14, A64DOpcodeConditionalBranchImmediate), OPCODE_GROUP_ENTRY(0x14, A64DOpcodeCompareAndBranchImmediate), OPCODE_GROUP_ENTRY(0x14, A64OpcodeExceptionGeneration), OPCODE_GROUP_ENTRY(0x15, A64DOpcodeUnconditionalBranchImmediate), OPCODE_GROUP_ENTRY(0x15, A64DOpcodeConditionalBranchImmediate), OPCODE_GROUP_ENTRY(0x15, A64DOpcodeCompareAndBranchImmediate), OPCODE_GROUP_ENTRY(0x15, A64DOpcodeHint), OPCODE_GROUP_ENTRY(0x16, A64DOpcodeUnconditionalBranchImmediate), OPCODE_GROUP_ENTRY(0x16, A64DOpcodeUnconditionalBranchRegister), OPCODE_GROUP_ENTRY(0x16, A64DOpcodeTestAndBranchImmediate), OPCODE_GROUP_ENTRY(0x17, A64DOpcodeUnconditionalBranchImmediate), OPCODE_GROUP_ENTRY(0x17, A64DOpcodeUnconditionalBranchRegister), OPCODE_GROUP_ENTRY(0x17, A64DOpcodeTestAndBranchImmediate), OPCODE_GROUP_ENTRY(0x18, A64DOpcodeLoadStoreImmediate), OPCODE_GROUP_ENTRY(0x18, A64DOpcodeLoadStoreRegisterOffset), OPCODE_GROUP_ENTRY(0x19, A64DOpcodeLoadStoreUnsignedImmediate), OPCODE_GROUP_ENTRY(0x1a, A64DOpcodeConditionalSelect), OPCODE_GROUP_ENTRY(0x1a, A64DOpcodeDataProcessing2Source), OPCODE_GROUP_ENTRY(0x1b, A64DOpcodeDataProcessing3Source), OPCODE_GROUP_ENTRY(0x1c, A64DOpcodeLoadStoreImmediate), OPCODE_GROUP_ENTRY(0x1c, A64DOpcodeLoadStoreRegisterOffset), OPCODE_GROUP_ENTRY(0x1d, A64DOpcodeLoadStoreUnsignedImmediate), OPCODE_GROUP_ENTRY(0x1e, A64DOpcodeFloatingPointCompare), OPCODE_GROUP_ENTRY(0x1e, A64DOpcodeFloatingPointDataProcessing2Source), OPCODE_GROUP_ENTRY(0x1e, A64DOpcodeFloatingPointDataProcessing1Source), OPCODE_GROUP_ENTRY(0x1e, A64DOpcodeFloatingFixedPointConversions), OPCODE_GROUP_ENTRY(0x1e, A64DOpcodeFloatingPointIntegerConversions), }; bool A64DOpcode::s_initialized = false; void A64DOpcode::init() { if (s_initialized) return; OpcodeGroup* lastGroups[32]; for (unsigned i = 0; i < 32; i++) { opcodeTable[i] = 0; lastGroups[i] = 0; } for (unsigned i = 0; i < sizeof(opcodeGroupList) / sizeof(struct OpcodeGroupInitializer); i++) { OpcodeGroup* newOpcodeGroup = new OpcodeGroup(opcodeGroupList[i].m_mask, opcodeGroupList[i].m_pattern, opcodeGroupList[i].m_format); uint32_t opcodeGroupNumber = opcodeGroupList[i].m_opcodeGroupNumber; if (!opcodeTable[opcodeGroupNumber]) opcodeTable[opcodeGroupNumber] = newOpcodeGroup; else lastGroups[opcodeGroupNumber]->setNext(newOpcodeGroup); lastGroups[opcodeGroupNumber] = newOpcodeGroup; } s_initialized = true; } void A64DOpcode::setPCAndOpcode(uint32_t* newPC, uint32_t newOpcode) { m_currentPC = newPC; m_opcode = newOpcode; m_bufferOffset = 0; m_formatBuffer[0] = '\0'; } const char* A64DOpcode::disassemble(uint32_t* currentPC) { setPCAndOpcode(currentPC, *currentPC); OpcodeGroup* opGroup = opcodeTable[opcodeGroupNumber(m_opcode)]; while (opGroup) { if (opGroup->matches(m_opcode)) return opGroup->format(this); opGroup = opGroup->next(); } return A64DOpcode::format(); } void A64DOpcode::bufferPrintf(const char* format, ...) { if (m_bufferOffset >= bufferSize) return; va_list argList; va_start(argList, format); m_bufferOffset += vsnprintf(m_formatBuffer + m_bufferOffset, bufferSize - m_bufferOffset, format, argList); va_end(argList); } const char* A64DOpcode::format() { bufferPrintf(" .long %08x", m_opcode); return m_formatBuffer; } void A64DOpcode::appendRegisterName(unsigned registerNumber, bool is64Bit) { if (registerNumber == 29) { bufferPrintf(is64Bit ? "fp" : "wfp"); return; } if (registerNumber == 30) { bufferPrintf(is64Bit ? "lr" : "wlr"); return; } bufferPrintf("%c%u", is64Bit ? 'x' : 'w', registerNumber); } void A64DOpcode::appendFPRegisterName(unsigned registerNumber, unsigned registerSize) { bufferPrintf("%c%u", FPRegisterPrefix(registerSize), registerNumber); } const char* const A64DOpcodeAddSubtract::s_opNames[4] = { "add", "adds", "sub", "subs" }; const char* A64DOpcodeAddSubtractImmediate::format() { if (isCMP()) appendInstructionName(cmpName()); else { if (isMovSP()) appendInstructionName("mov"); else appendInstructionName(opName()); appendSPOrRegisterName(rd(), is64Bit()); appendSeparator(); } appendSPOrRegisterName(rn(), is64Bit()); if (!isMovSP()) { appendSeparator(); appendUnsignedImmediate(immed12()); if (shift()) { appendSeparator(); appendString(shift() == 1 ? "lsl" : "reserved"); } } return m_formatBuffer; } const char* A64DOpcodeAddSubtractExtendedRegister::format() { if (immediate3() > 4) return A64DOpcode::format(); if (isCMP()) appendInstructionName(cmpName()); else { appendInstructionName(opName()); appendSPOrRegisterName(rd(), is64Bit()); appendSeparator(); } appendSPOrRegisterName(rn(), is64Bit()); appendSeparator(); appendZROrRegisterName(rm(), is64Bit() && ((option() & 0x3) == 0x3)); appendSeparator(); if (option() == 0x2 && ((rd() == 31) || (rn() == 31))) appendString("lsl"); else appendString(optionName()); if (immediate3()) { appendCharacter(' '); appendUnsignedImmediate(immediate3()); } return m_formatBuffer; } const char* A64DOpcodeAddSubtractShiftedRegister::format() { if (!is64Bit() && immediate6() & 0x20) return A64DOpcode::format(); if (shift() == 0x3) return A64DOpcode::format(); if (isCMP()) appendInstructionName(cmpName()); else { if (isNeg()) appendInstructionName(cmpName()); else appendInstructionName(opName()); appendSPOrRegisterName(rd(), is64Bit()); appendSeparator(); } if (!isNeg()) { appendRegisterName(rn(), is64Bit()); appendSeparator(); } appendZROrRegisterName(rm(), is64Bit()); if (immediate6()) { appendSeparator(); appendShiftType(shift()); appendUnsignedImmediate(immediate6()); } return m_formatBuffer; } const char* const A64DOpcodeBitfield::s_opNames[3] = { "sbfm", "bfm", "ubfm" }; const char* const A64DOpcodeBitfield::s_extendPseudoOpNames[3][3] = { { "sxtb", "sxth", "sxtw" }, { 0, 0, 0} , { "uxtb", "uxth", "uxtw" } }; const char* const A64DOpcodeBitfield::s_insertOpNames[3] = { "sbfiz", "bfi", "ubfiz" }; const char* const A64DOpcodeBitfield::s_extractOpNames[3] = { "sbfx", "bf", "ubfx" }; const char* A64DOpcodeBitfield::format() { if (opc() == 0x3) return A64DOpcode::format(); if (is64Bit() != nBit()) return A64DOpcode::format(); if (!is64Bit() && ((immediateR() & 0x20) || (immediateS() & 0x20))) return A64DOpcode::format(); if (!(opc() & 0x1) && !immediateR()) { // [un]signed {btye,half-word,word} extend bool isSTXType = false; if (immediateS() == 7) { appendInstructionName(extendPseudoOpNames(0)); isSTXType = true; } else if (immediateS() == 15) { appendInstructionName(extendPseudoOpNames(1)); isSTXType = true; } else if (immediateS() == 31 && is64Bit()) { appendInstructionName(extendPseudoOpNames(2)); isSTXType = true; } if (isSTXType) { appendRegisterName(rd(), is64Bit()); appendSeparator(); appendRegisterName(rn(), false); return m_formatBuffer; } } if (opc() == 0x2 && immediateS() == (immediateR() + 1)) { // lsl appendInstructionName("lsl"); appendRegisterName(rd(), is64Bit()); appendSeparator(); appendRegisterName(rn(), is64Bit()); appendSeparator(); appendUnsignedImmediate((is64Bit() ? 63u : 31u) - immediateR()); return m_formatBuffer; } else if (!(opc() & 0x1) && ((immediateS() & 0x1f) == 0x1f) && (is64Bit() == (immediateS() >> 5))) { // asr/lsr appendInstructionName(!opc() ? "ars" : "lsr"); appendRegisterName(rd(), is64Bit()); appendSeparator(); appendRegisterName(rn(), is64Bit()); appendSeparator(); appendUnsignedImmediate(immediateR()); return m_formatBuffer; } else if (immediateS() < immediateR()) { // bit field insert appendInstructionName(insertOpNames()); appendRegisterName(rd(), is64Bit()); appendSeparator(); appendRegisterName(rn(), is64Bit()); appendSeparator(); appendUnsignedImmediate((is64Bit() ? 64u : 32u) - immediateR()); appendSeparator(); appendUnsignedImmediate(immediateS() + 1); return m_formatBuffer; } else { // bit field extract appendInstructionName(extractOpNames()); appendRegisterName(rd(), is64Bit()); appendSeparator(); appendRegisterName(rn(), is64Bit()); appendSeparator(); appendUnsignedImmediate(immediateR()); appendSeparator(); appendUnsignedImmediate(immediateS() - immediateR() + 1); return m_formatBuffer; } appendInstructionName(opName()); appendRegisterName(rd(), is64Bit()); appendSeparator(); appendRegisterName(rn(), is64Bit()); appendSeparator(); appendUnsignedImmediate(immediateR()); appendSeparator(); appendUnsignedImmediate(immediateS()); return m_formatBuffer; } const char* A64DOpcodeCompareAndBranchImmediate::format() { appendInstructionName(opBit() ? "cbnz" : "cbz"); appendRegisterName(rt(), is64Bit()); appendSeparator(); appendPCRelativeOffset(m_currentPC, static_cast(immediate19())); return m_formatBuffer; } const char* A64DOpcodeConditionalBranchImmediate::format() { bufferPrintf(" b.%-5.5s", conditionName(condition())); appendPCRelativeOffset(m_currentPC, static_cast(immediate19())); return m_formatBuffer; } const char* const A64DOpcodeConditionalSelect::s_opNames[4] = { "csel", "csinc", "csinv", "csneg" }; const char* A64DOpcodeConditionalSelect::format() { if (sBit()) return A64DOpcode::format(); if (op2() & 0x2) return A64DOpcode::format(); if (rn() == rm() && (opNum() == 1 || opNum() == 2)) { if (rn() == 31) { appendInstructionName((opNum() == 1) ? "cset" : "csetm"); appendRegisterName(rd(), is64Bit()); } else { appendInstructionName((opNum() == 1) ? "cinc" : "cinv"); appendRegisterName(rd(), is64Bit()); appendSeparator(); appendZROrRegisterName(rn(), is64Bit()); } appendSeparator(); appendString(conditionName(condition() ^ 0x1)); return m_formatBuffer; } appendInstructionName(opName()); appendRegisterName(rd(), is64Bit()); appendSeparator(); appendZROrRegisterName(rn(), is64Bit()); appendSeparator(); appendZROrRegisterName(rm(), is64Bit()); appendSeparator(); appendString(conditionName(condition())); return m_formatBuffer; } const char* const A64DOpcodeDataProcessing2Source::s_opNames[8] = { 0, 0, "udiv", "sdiv", "lsl", "lsr", "asr", "ror" // We use the pseudo-op names for the shift/rotate instructions }; const char* A64DOpcodeDataProcessing2Source::format() { if (sBit()) return A64DOpcode::format(); if (!(opCode() & 0x3e)) return A64DOpcode::format(); if (opCode() & 0x30) return A64DOpcode::format(); if ((opCode() & 0x34) == 0x4) return A64DOpcode::format(); appendInstructionName(opName()); appendRegisterName(rd(), is64Bit()); appendSeparator(); appendRegisterName(rn(), is64Bit()); appendSeparator(); appendRegisterName(rm(), is64Bit()); return m_formatBuffer; } const char* const A64DOpcodeDataProcessing3Source::s_opNames[16] = { "madd", "msub", "smaddl", "smsubl", "smulh", 0, 0, 0, 0, 0, "umaddl", "umsubl", "umulh", 0, 0, 0 }; const char* const A64DOpcodeDataProcessing3Source::s_pseudoOpNames[16] = { "mul", "mneg", "smull", "smnegl", "smulh", 0, 0, 0, 0, 0, "umull", "umnegl", "umulh", 0, 0, 0 }; const char* A64DOpcodeDataProcessing3Source::format() { if (op54()) return A64DOpcode::format(); if (opNum() > 12) return A64DOpcode::format(); if (!is64Bit() && opNum() > 1) return A64DOpcode::format(); if (!opName()) return A64DOpcode::format(); appendInstructionName(opName()); appendRegisterName(rd(), is64Bit()); appendSeparator(); bool srcOneAndTwoAre64Bit = is64Bit() & !(opNum() & 0x2); appendRegisterName(rn(), srcOneAndTwoAre64Bit); appendSeparator(); appendRegisterName(rm(), srcOneAndTwoAre64Bit); if ((ra() != 31) || !(opNum() & 0x4)) { appendSeparator(); appendRegisterName(ra(), is64Bit()); } return m_formatBuffer; } const char* A64OpcodeExceptionGeneration::format() { const char* opname = 0; if (!op2()) { switch (opc()) { case 0x0: // SVC, HVC & SMC switch (ll()) { case 0x1: opname = "svc"; break; case 0x2: opname = "hvc"; break; case 0x3: opname = "smc"; break; } break; case 0x1: // BRK if (!ll()) opname = "brk"; break; case 0x2: // HLT if (!ll()) opname = "hlt"; break; case 0x5: // DPCS1-3 switch (ll()) { case 0x1: opname = "dpcs1"; break; case 0x2: opname = "dpcs2"; break; case 0x3: opname = "dpcs3"; break; } break; } } if (!opname) return A64DOpcode::format(); appendInstructionName(opname); appendUnsignedImmediate(immediate16()); return m_formatBuffer; } const char* A64DOpcodeExtract::format() { if (!op21() || !o0Bit()) return A64DOpcode::format(); if (is64Bit() != nBit()) return A64DOpcode::format(); if (is64Bit() && (immediateS() & 0x20)) return A64DOpcode::format(); const char* opName = (rn() == rm()) ? "ror" : "extr"; appendInstructionName(opName); appendRegisterName(rd(), is64Bit()); appendSeparator(); appendRegisterName(rn(), is64Bit()); appendSeparator(); appendRegisterName(rm(), is64Bit()); appendSeparator(); appendUnsignedImmediate(immediateS()); return m_formatBuffer; } const char* A64DOpcodeFloatingPointCompare::format() { if (mBit()) return A64DOpcode::format(); if (sBit()) return A64DOpcode::format(); if (type() & 0x2) return A64DOpcode::format(); if (op()) return A64DOpcode::format(); if (opCode2() & 0x7) return A64DOpcode::format(); appendInstructionName(opName()); unsigned registerSize = type() + 2; appendFPRegisterName(rn(), registerSize); appendSeparator(); if (opCode2() & 0x8) bufferPrintf("#0.0"); else appendFPRegisterName(rm(), registerSize); return m_formatBuffer; } const char* const A64DOpcodeFloatingPointDataProcessing1Source::s_opNames[16] = { "fmov", "fabs", "fneg", "fsqrt", "fcvt", "fcvt", 0, "fcvt", "frintn", "frintp", "frintm", "frintz", "frinta", 0, "frintx", "frinti" }; const char* A64DOpcodeFloatingPointDataProcessing1Source::format() { if (mBit()) return A64DOpcode::format(); if (sBit()) return A64DOpcode::format(); if (opNum() > 16) return A64DOpcode::format(); switch (type()) { case 0: if ((opNum() == 0x4) || (opNum() == 0x6) || (opNum() == 0xd)) return A64DOpcode::format(); break; case 1: if ((opNum() == 0x5) || (opNum() == 0x6) || (opNum() == 0xd)) return A64DOpcode::format(); break; case 2: return A64DOpcode::format(); case 3: if ((opNum() < 0x4) || (opNum() > 0x5)) return A64DOpcode::format(); break; } appendInstructionName(opName()); if ((opNum() >= 0x4) && (opNum() <= 0x7)) { unsigned srcRegisterSize = type() ^ 0x2; // 0:s, 1:d & 3:h unsigned destRegisterSize = (opNum() & 0x3) ^ 0x2; appendFPRegisterName(rd(), destRegisterSize); appendSeparator(); appendFPRegisterName(rn(), srcRegisterSize); } else { unsigned registerSize = type() + 2; appendFPRegisterName(rd(), registerSize); appendSeparator(); appendFPRegisterName(rn(), registerSize); } return m_formatBuffer; } const char* const A64DOpcodeFloatingPointDataProcessing2Source::s_opNames[16] = { "fmul", "fdiv", "fadd", "fsub", "fmax", "fmin", "fmaxnm", "fminnm", "fnmul" }; const char* A64DOpcodeFloatingPointDataProcessing2Source::format() { if (mBit()) return A64DOpcode::format(); if (sBit()) return A64DOpcode::format(); if (type() & 0x2) return A64DOpcode::format(); if (opNum() > 8) return A64DOpcode::format(); appendInstructionName(opName()); unsigned registerSize = type() + 2; appendFPRegisterName(rd(), registerSize); appendSeparator(); appendFPRegisterName(rn(), registerSize); appendSeparator(); appendFPRegisterName(rm(), registerSize); return m_formatBuffer; } const char* const A64DOpcodeFloatingFixedPointConversions::s_opNames[4] = { "fcvtzs", "fcvtzu", "scvtf", "ucvtf" }; const char* A64DOpcodeFloatingFixedPointConversions::format() { if (sBit()) return A64DOpcode::format(); if (type() & 0x2) return A64DOpcode::format(); if (opcode() & 0x4) return A64DOpcode::format(); if (!(rmode() & 0x1) && !(opcode() & 0x6)) return A64DOpcode::format(); if ((rmode() & 0x1) && (opcode() & 0x6) == 0x2) return A64DOpcode::format(); if (!(rmode() & 0x2) && !(opcode() & 0x6)) return A64DOpcode::format(); if ((rmode() & 0x2) && (opcode() & 0x6) == 0x2) return A64DOpcode::format(); if (!is64Bit() && scale() >= 32) return A64DOpcode::format(); appendInstructionName(opName()); unsigned FPRegisterSize = type() + 2; bool destIsFP = !rmode(); if (destIsFP) { appendFPRegisterName(rd(), FPRegisterSize); appendSeparator(); appendRegisterName(rn(), is64Bit()); } else { appendRegisterName(rd(), is64Bit()); appendSeparator(); appendFPRegisterName(rn(), FPRegisterSize); } appendSeparator(); appendUnsignedImmediate(64 - scale()); return m_formatBuffer; } const char* const A64DOpcodeFloatingPointIntegerConversions::s_opNames[32] = { "fcvtns", "fcvtnu", "scvtf", "ucvtf", "fcvtas", "fcvtau", "fmov", "fmov", "fcvtps", "fcvtpu", 0, 0, 0, 0, "fmov", "fmov", "fcvtms", "fcvtmu", 0, 0, 0, 0, 0, 0, "fcvtzs", "fcvtzu", 0, 0, 0, 0, 0, 0 }; const char* A64DOpcodeFloatingPointIntegerConversions::format() { if (sBit()) return A64DOpcode::format(); if (type() == 0x3) return A64DOpcode::format(); if (((rmode() & 0x1) || (rmode() & 0x2)) && (((opcode() & 0x6) == 0x2) || ((opcode() & 0x6) == 0x4))) return A64DOpcode::format(); if ((type() == 0x2) && (!(opcode() & 0x4) || ((opcode() & 0x6) == 0x4))) return A64DOpcode::format(); if (!type() && (rmode() & 0x1) && ((opcode() & 0x6) == 0x6)) return A64DOpcode::format(); if (is64Bit() && type() == 0x2 && ((opNum() & 0xe) == 0x6)) return A64DOpcode::format(); if (!opName()) return A64DOpcode::format(); if ((opNum() & 0x1e) == 0xe) { // Handle fmov to/from upper half of quad separately if (!is64Bit() || (type() != 0x2)) return A64DOpcode::format(); appendInstructionName(opName()); if (opcode() & 0x1) { // fmov Vd.D[1], Xn bufferPrintf("V%u.D[1]", rd()); appendSeparator(); appendRegisterName(rn()); } else { // fmov Xd, Vn.D[1] appendRegisterName(rd()); appendSeparator(); bufferPrintf("V%u.D[1]", rn()); } return m_formatBuffer; } appendInstructionName(opName()); unsigned FPRegisterSize = type() + 2; bool destIsFP = ((opNum() == 2) || (opNum() == 3) || (opNum() == 7)); if (destIsFP) { appendFPRegisterName(rd(), FPRegisterSize); appendSeparator(); appendRegisterName(rn(), is64Bit()); } else { appendRegisterName(rd(), is64Bit()); appendSeparator(); appendFPRegisterName(rn(), FPRegisterSize); } return m_formatBuffer; } const char* const A64DOpcodeHint::s_opNames[6] = { "nop", "yield", "wfe", "wfi", "sev", "sevl" }; const char* A64DOpcodeHint::format() { appendInstructionName(opName()); if (immediate7() > 5) appendUnsignedImmediate(immediate7()); return m_formatBuffer; } // A zero in an entry of the table means the instruction is Unallocated const char* const A64DOpcodeLoadStore::s_opNames[32] = { "strb", "ldrb", "ldrsb", "ldrsb", "str", "ldr", "str", "ldr", "strh", "ldrh", "ldrsh", "ldrsh", "str", "ldr", 0, 0, "str", "ldr", "ldrsw", 0, "str", "ldr", 0, 0, "str", "ldr", 0, 0, "str", "ldr", 0, 0 }; // A zero in an entry of the table means the instruction is Unallocated const char* const A64DOpcodeLoadStoreImmediate::s_unprivilegedOpNames[32] = { "sttrb", "ldtrb", "ldtrsb", "ldtrsb", 0, 0, 0, 0, "sttrh", "ldtrh", "ldtrsh", "ldtrsh", 0, 0, 0, 0, "sttr", "ldtr", "ldtrsw", 0, 0, 0, 0, 0, "sttr", "ldtr", 0, 0, 0, 0, 0, 0 }; // A zero in an entry of the table means the instruction is Unallocated const char* const A64DOpcodeLoadStoreImmediate::s_unscaledOpNames[32] = { "sturb", "ldurb", "ldursb", "ldursb", "stur", "ldur", "stur", "ldur", "sturh", "ldurh", "ldursh", "ldursh", "stur", "ldur", 0, 0, "stur", "ldur", "ldursw", 0, "stur", "ldur", 0, 0, "stur", "ldur", "prfum", 0, "stur", "ldur", 0, 0 }; const char* A64DOpcodeLoadStoreImmediate::format() { const char* thisOpName; if (type() & 0x1) thisOpName = opName(); else if (!type()) thisOpName = unscaledOpName(); else thisOpName = unprivilegedOpName(); if (!thisOpName) return A64DOpcode::format(); appendInstructionName(thisOpName); if (vBit()) appendFPRegisterName(rt(), size()); else appendRegisterName(rt(), is64BitRT()); appendSeparator(); appendCharacter('['); appendSPOrRegisterName(rn()); switch (type()) { case 0: // Unscaled Immediate if (immediate9()) { appendSeparator(); appendSignedImmediate(immediate9()); } appendCharacter(']'); break; case 1: // Immediate Post-Indexed appendCharacter(']'); if (immediate9()) { appendSeparator(); appendSignedImmediate(immediate9()); } break; case 2: // Unprivileged if (immediate9()) { appendSeparator(); appendSignedImmediate(immediate9()); } appendCharacter(']'); break; case 3: // Immediate Pre-Indexed if (immediate9()) { appendSeparator(); appendSignedImmediate(immediate9()); } appendCharacter(']'); appendCharacter('!'); break; } return m_formatBuffer; } const char* A64DOpcodeLoadStoreRegisterOffset::format() { const char* thisOpName = opName(); if (!thisOpName) return A64DOpcode::format(); if (!(option() & 0x2)) return A64DOpcode::format(); appendInstructionName(thisOpName); unsigned scale; if (vBit()) { appendFPRegisterName(rt(), size()); scale = ((opc() & 2)<<1) | size(); } else { appendRegisterName(rt(), is64BitRT()); scale = size(); } appendSeparator(); appendCharacter('['); appendSPOrRegisterName(rn()); appendSeparator(); appendZROrRegisterName(rm(), (option() & 0x3) == 0x3); unsigned shift = sBit() ? scale : 0; if (option() == 0x3) { if (shift) { appendSeparator(); appendString("lsl "); appendUnsignedImmediate(shift); } } else { appendSeparator(); appendString(optionName()); if (shift) appendUnsignedImmediate(shift); } appendCharacter(']'); return m_formatBuffer; } const char* A64DOpcodeLoadStoreRegisterPair::opName() { if (!vBit() && lBit() && size() == 0x1) return "ldpsw"; if (lBit()) return "ldp"; return "stp"; } const char* A64DOpcodeLoadStoreRegisterPair::format() { const char* thisOpName = opName(); if (size() == 0x3) return A64DOpcode::format(); if ((offsetMode() < 0x1) || (offsetMode() > 0x3)) return A64DOpcode::format(); if ((offsetMode() == 0x1) && !vBit() && !lBit()) return A64DOpcode::format(); appendInstructionName(thisOpName); unsigned offsetShift; if (vBit()) { appendFPRegisterName(rt(), size()); appendSeparator(); appendFPRegisterName(rt2(), size()); offsetShift = size() + 2; } else { appendRegisterName(rt(), is64Bit()); appendSeparator(); appendRegisterName(rt2(), is64Bit()); offsetShift = (size() >> 1) + 2; } appendSeparator(); appendCharacter('['); appendSPOrRegisterName(rn()); int offset = immediate7() << offsetShift; if (offsetMode() == 1) { appendCharacter(']'); appendSeparator(); appendSignedImmediate(offset); } else { appendSeparator(); appendSignedImmediate(offset); appendCharacter(']'); if (offsetMode() == 0x3) appendCharacter('!'); } return m_formatBuffer; } const char* A64DOpcodeLoadStoreUnsignedImmediate::format() { const char* thisOpName = opName(); if (!thisOpName) return A64DOpcode::format(); appendInstructionName(thisOpName); unsigned scale; if (vBit()) { appendFPRegisterName(rt(), size()); scale = ((opc() & 2)<<1) | size(); } else { appendRegisterName(rt(), is64BitRT()); scale = size(); } appendSeparator(); appendCharacter('['); appendSPOrRegisterName(rn()); if (immediate12()) { appendSeparator(); appendUnsignedImmediate(immediate12() << scale); } appendCharacter(']'); return m_formatBuffer; } // A zero in an entry of the table means the instruction is Unallocated const char* const A64DOpcodeLogical::s_opNames[8] = { "and", "bic", "orr", "orn", "eor", "eon", "ands", "bics" }; const char* A64DOpcodeLogicalShiftedRegister::format() { if (!is64Bit() && immediate6() & 0x20) return A64DOpcode::format(); if (isTst()) appendInstructionName("tst"); else { if (isMov()) appendInstructionName("mov"); else appendInstructionName(opName(opNumber())); appendSPOrRegisterName(rd(), is64Bit()); appendSeparator(); } if (!isMov()) { appendRegisterName(rn(), is64Bit()); appendSeparator(); } appendZROrRegisterName(rm(), is64Bit()); if (immediate6()) { appendSeparator(); appendShiftType(shift()); appendUnsignedImmediate(immediate6()); } return m_formatBuffer; } static unsigned highestBitSet(unsigned value) { unsigned result = 0; while (value >>= 1) result++; return result; } static uint64_t rotateRight(uint64_t value, unsigned width, unsigned shift) { uint64_t result = value; if (shift) result = (value >> (shift % width)) | (value << (width - shift)); return result; } static uint64_t replicate(uint64_t value, unsigned width) { uint64_t result = 0; for (unsigned totalBits = 0; totalBits < 64; totalBits += width) result = (result << width) | value; return result; } const char* A64DOpcodeLogicalImmediate::format() { if (!is64Bit() && nBit()) return A64DOpcode::format(); unsigned len = highestBitSet(nBit() << 6 | (immediateS() ^ 0x3f)); unsigned levels = (1 << len) - 1; // len number of 1 bits starting at LSB if ((immediateS() & levels) == levels) return A64DOpcode::format(); unsigned r = immediateR() & levels; unsigned s = immediateS() & levels; unsigned eSize = 1 << len; uint64_t pattern = rotateRight((1ull << (s + 1)) - 1, eSize, r); uint64_t immediate = replicate(pattern, eSize); if (!is64Bit()) immediate &= 0xffffffffull; if (isTst()) appendInstructionName("tst"); else { if (isMov()) appendInstructionName("mov"); else appendInstructionName(opName(opNumber())); appendRegisterName(rd(), is64Bit()); appendSeparator(); } if (!isMov()) { appendRegisterName(rn(), is64Bit()); appendSeparator(); } appendUnsignedImmediate64(immediate); return m_formatBuffer; } const char* const A64DOpcodeMoveWide::s_opNames[4] = { "movn", "", "movz", "movk" }; const char* A64DOpcodeMoveWide::format() { if (opc() == 1) return A64DOpcode::format(); if (!size() && hw() >= 2) return A64DOpcode::format(); appendInstructionName(opName()); appendRegisterName(rd(), is64Bit()); appendSeparator(); appendUnsignedImmediate(immediate16()); if (hw()) { appendSeparator(); appendShiftAmount(hw()); } return m_formatBuffer; } const char* A64DOpcodeTestAndBranchImmediate::format() { appendInstructionName(opBit() ? "tbnz" : "tbz"); appendRegisterName(rt()); appendSeparator(); appendUnsignedImmediate(bitNumber()); appendSeparator(); appendPCRelativeOffset(m_currentPC, static_cast(immediate14())); return m_formatBuffer; } const char* A64DOpcodeUnconditionalBranchImmediate::format() { appendInstructionName(op() ? "bl" : "b"); appendPCRelativeOffset(m_currentPC, static_cast(immediate26())); return m_formatBuffer; } const char* const A64DOpcodeUnconditionalBranchRegister::s_opNames[8] = { "br", "blr", "ret", "", "eret", "drps", "", "" }; const char* A64DOpcodeUnconditionalBranchRegister::format() { unsigned opcValue = opc(); if (opcValue == 3 || opcValue > 5) return A64DOpcode::format(); if (((opcValue & 0xe) == 0x4) && rn() != 0x1f) return A64DOpcode::format(); appendInstructionName(opName()); if (opcValue <= 2) appendRegisterName(rn()); return m_formatBuffer; } } } // namespace JSC::ARM64Disassembler #endif // USE(ARM64_DISASSEMBLER)