| /* |
| * Copyright (C) 2012-2021 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. |
| */ |
| |
| #pragma once |
| |
| #include <wtf/Assertions.h> |
| #include <stdint.h> |
| |
| namespace JSC { namespace ARM64Disassembler { |
| |
| class A64DOpcode { |
| private: |
| class OpcodeGroup { |
| WTF_MAKE_FAST_ALLOCATED; |
| public: |
| OpcodeGroup(uint32_t opcodeMask, uint32_t opcodePattern, const char* (*format)(A64DOpcode*)) |
| : m_opcodeMask(opcodeMask) |
| , m_opcodePattern(opcodePattern) |
| , m_format(format) |
| , m_next(0) |
| { |
| } |
| |
| void setNext(OpcodeGroup* next) |
| { |
| m_next = next; |
| } |
| |
| OpcodeGroup* next() |
| { |
| return m_next; |
| } |
| |
| bool matches(uint32_t opcode) |
| { |
| return (opcode & m_opcodeMask) == m_opcodePattern; |
| } |
| |
| const char* format(A64DOpcode* thisObj) |
| { |
| return m_format(thisObj); |
| } |
| |
| private: |
| uint32_t m_opcodeMask; |
| uint32_t m_opcodePattern; |
| const char* (*m_format)(A64DOpcode*); |
| OpcodeGroup* m_next; |
| }; |
| |
| public: |
| static void init(); |
| |
| A64DOpcode() |
| : m_opcode(0) |
| , m_bufferOffset(0) |
| { |
| init(); |
| m_formatBuffer[0] = '\0'; |
| } |
| |
| const char* disassemble(uint32_t* currentPC); |
| |
| protected: |
| void setPCAndOpcode(uint32_t*, uint32_t); |
| const char* format(); |
| |
| static const char* const s_conditionNames[16]; |
| static const char* const s_shiftNames[4]; |
| static const char* const s_optionName[8]; |
| static const char s_FPRegisterPrefix[5]; |
| |
| static const char* conditionName(unsigned condition) { return s_conditionNames[condition & 0xf]; } |
| static const char* shiftName(unsigned shiftValue) { return s_shiftNames[shiftValue & 0x3]; } |
| const char* optionName() { return s_optionName[option()]; } |
| static char FPRegisterPrefix(unsigned FPRegisterSize) |
| { |
| if (FPRegisterSize > 4) |
| FPRegisterSize = 4; |
| return s_FPRegisterPrefix[FPRegisterSize]; |
| } |
| |
| unsigned opcodeGroupNumber(uint32_t opcode) { return (opcode >> 24) & 0x1f; } |
| |
| bool is64Bit() { return m_opcode & 0x80000000; } |
| unsigned size() { return m_opcode >> 30; } |
| unsigned option() { return (m_opcode >> 13) & 0x7; } |
| unsigned rd() { return m_opcode & 0x1f; } |
| unsigned rt() { return m_opcode & 0x1f; } |
| unsigned rn() { return (m_opcode >> 5) & 0x1f; } |
| unsigned rm() { return (m_opcode >> 16) & 0x1f; } |
| |
| void bufferPrintf(const char* format, ...) WTF_ATTRIBUTE_PRINTF(2, 3); |
| |
| void appendInstructionName(const char* instructionName) |
| { |
| bufferPrintf(" %-9.9s", instructionName); |
| } |
| |
| void appendRegisterName(unsigned registerNumber, bool is64Bit = true); |
| void appendSPOrRegisterName(unsigned registerNumber, bool is64Bit = true) |
| { |
| if (registerNumber == 31) { |
| bufferPrintf(is64Bit ? "sp" : "wsp"); |
| return; |
| } |
| appendRegisterName(registerNumber, is64Bit); |
| } |
| |
| void appendZROrRegisterName(unsigned registerNumber, bool is64Bit = true) |
| { |
| if (registerNumber == 31) { |
| bufferPrintf(is64Bit ? "xzr" : "wzr"); |
| return; |
| } |
| appendRegisterName(registerNumber, is64Bit); |
| } |
| |
| void appendFPRegisterName(unsigned registerNumber, unsigned registerSize); |
| |
| void appendSeparator() |
| { |
| bufferPrintf(", "); |
| } |
| |
| void appendCharacter(const char c) |
| { |
| bufferPrintf("%c", c); |
| } |
| |
| void appendString(const char* string) |
| { |
| bufferPrintf("%s", string); |
| } |
| |
| void appendShiftType(unsigned shiftValue) |
| { |
| bufferPrintf("%s ", shiftName(shiftValue)); |
| } |
| |
| void appendSignedImmediate(int immediate) |
| { |
| bufferPrintf("#%d", immediate); |
| } |
| |
| void appendSignedImmediate64(int64_t immediate) |
| { |
| bufferPrintf("#%" PRIi64, immediate); |
| } |
| |
| void appendUnsignedImmediate(unsigned immediate) |
| { |
| bufferPrintf("#%u", immediate); |
| } |
| |
| void appendUnsignedHexImmediate(unsigned immediate) |
| { |
| bufferPrintf("#0x%x", immediate); |
| } |
| |
| void appendUnsignedImmediate64(uint64_t immediate) |
| { |
| bufferPrintf("#0x%" PRIx64, immediate); |
| } |
| |
| void appendPCRelativeOffset(uint32_t* pc, int32_t immediate) |
| { |
| bufferPrintf("0x%" PRIxPTR, bitwise_cast<uintptr_t>(pc + immediate)); |
| } |
| |
| void appendShiftAmount(unsigned amount) |
| { |
| bufferPrintf("lsl #%u", 16 * amount); |
| } |
| |
| static constexpr int bufferSize = 81; |
| |
| char m_formatBuffer[bufferSize]; |
| uint32_t* m_currentPC; |
| uint32_t m_opcode; |
| int m_bufferOffset; |
| |
| private: |
| static OpcodeGroup* opcodeTable[32]; |
| |
| static bool s_initialized; |
| }; |
| |
| #define DEFINE_STATIC_FORMAT(klass, thisObj) \ |
| static const char* format(A64DOpcode* thisObj) { return reinterpret_cast< klass *>(thisObj)->format(); } |
| |
| class A64DOpcodeAddSubtract : public A64DOpcode { |
| private: |
| static const char* const s_opNames[4]; |
| |
| public: |
| const char* opName() { return s_opNames[opAndS()]; } |
| const char* cmpName() { return op() ? "cmp" : "cmn"; } |
| |
| bool isCMP() { return (sBit() && rd() == 31); } |
| unsigned op() { return (m_opcode >> 30) & 0x1; } |
| unsigned sBit() { return (m_opcode >> 29) & 0x1; } |
| unsigned opAndS() { return (m_opcode >> 29) & 0x3; } |
| }; |
| |
| class A64DOpcodeAddSubtractImmediate : public A64DOpcodeAddSubtract { |
| public: |
| static constexpr uint32_t mask = 0x1f000000; |
| static constexpr uint32_t pattern = 0x11000000; |
| |
| DEFINE_STATIC_FORMAT(A64DOpcodeAddSubtractImmediate, thisObj); |
| |
| const char* format(); |
| |
| bool isMovSP() { return (!opAndS() && !immed12() && ((rd() == 31) || rn() == 31)); } |
| unsigned shift() { return (m_opcode >> 22) & 0x3; } |
| unsigned immed12() { return (m_opcode >> 10) & 0xfff; } |
| }; |
| |
| class A64DOpcodeAddSubtractExtendedRegister : public A64DOpcodeAddSubtract { |
| public: |
| static constexpr uint32_t mask = 0x1fe00000; |
| static constexpr uint32_t pattern = 0x0b200000; |
| |
| DEFINE_STATIC_FORMAT(A64DOpcodeAddSubtractExtendedRegister, thisObj); |
| |
| const char* format(); |
| |
| unsigned immediate3() { return (m_opcode >> 10) & 0x7; } |
| }; |
| |
| class A64DOpcodeAddSubtractShiftedRegister : public A64DOpcodeAddSubtract { |
| public: |
| static constexpr uint32_t mask = 0x1f200000; |
| static constexpr uint32_t pattern = 0x0b000000; |
| |
| DEFINE_STATIC_FORMAT(A64DOpcodeAddSubtractShiftedRegister, thisObj); |
| |
| const char* format(); |
| |
| bool isNeg() { return (op() && rn() == 31); } |
| const char* negName() { return sBit() ? "negs" : "neg"; } |
| unsigned shift() { return (m_opcode >> 22) & 0x3; } |
| int immediate6() { return (static_cast<uint32_t>((m_opcode >> 10) & 0x3f) << 26) >> 26; } |
| }; |
| |
| class A64DOpcodeBitfield : public A64DOpcode { |
| private: |
| static const char* const s_opNames[3]; |
| static const char* const s_extendPseudoOpNames[3][3]; |
| static const char* const s_insertOpNames[3]; |
| static const char* const s_extractOpNames[3]; |
| |
| public: |
| static constexpr uint32_t mask = 0x1f800000; |
| static constexpr uint32_t pattern = 0x13000000; |
| |
| DEFINE_STATIC_FORMAT(A64DOpcodeBitfield, thisObj); |
| |
| const char* format(); |
| |
| const char* opName() { return s_opNames[opc()]; } |
| const char* extendPseudoOpNames(unsigned opSize) { return s_extendPseudoOpNames[opc()][opSize]; } |
| const char* insertOpNames() { return s_insertOpNames[opc()]; } |
| const char* extractOpNames() { return s_extractOpNames[opc()]; } |
| |
| unsigned opc() { return (m_opcode >> 29) & 0x3; } |
| unsigned nBit() { return (m_opcode >> 22) & 0x1; } |
| unsigned immediateR() { return (m_opcode >> 16) & 0x3f; } |
| unsigned immediateS() { return (m_opcode >> 10) & 0x3f; } |
| }; |
| |
| class A64DOpcodeCompareAndBranchImmediate : public A64DOpcode { |
| public: |
| static constexpr uint32_t mask = 0x7e000000; |
| static constexpr uint32_t pattern = 0x34000000; |
| |
| DEFINE_STATIC_FORMAT(A64DOpcodeCompareAndBranchImmediate, thisObj); |
| |
| const char* format(); |
| |
| unsigned opBit() { return (m_opcode >> 24) & 0x1; } |
| int immediate19() { return (static_cast<int>((m_opcode >> 5) & 0x7ffff) << 13) >> 13; } |
| }; |
| |
| class A64DOpcodeConditionalBranchImmediate : public A64DOpcode { |
| public: |
| static constexpr uint32_t mask = 0xff000010; |
| static constexpr uint32_t pattern = 0x54000000; |
| |
| DEFINE_STATIC_FORMAT(A64DOpcodeConditionalBranchImmediate, thisObj); |
| |
| const char* format(); |
| |
| unsigned condition() { return m_opcode & 0xf; } |
| int immediate19() { return (static_cast<int>((m_opcode >> 5) & 0x7ffff) << 13) >> 13; } |
| }; |
| |
| class A64DOpcodeConditionalSelect : public A64DOpcode { |
| private: |
| static const char* const s_opNames[4]; |
| |
| public: |
| static constexpr uint32_t mask = 0x1fe00000; |
| static constexpr uint32_t pattern = 0x1a800000; |
| |
| DEFINE_STATIC_FORMAT(A64DOpcodeConditionalSelect, thisObj); |
| |
| const char* format(); |
| |
| const char* opName() { return s_opNames[opNum()]; } |
| unsigned opNum() { return (op() << 1 | (op2() & 0x1)); } |
| unsigned op() { return (m_opcode >> 30) & 0x1; } |
| unsigned sBit() { return (m_opcode >> 29) & 0x1; } |
| unsigned condition() { return (m_opcode >> 12) & 0xf; } |
| unsigned op2() { return (m_opcode >> 10) & 0x3; } |
| }; |
| |
| class A64DOpcodeDataProcessing1Source : public A64DOpcode { |
| private: |
| static const char* const s_opNames[8]; |
| static const char* const s_pacAutOpNames[18]; |
| |
| public: |
| static constexpr uint32_t mask = 0x5fe00000; |
| static constexpr uint32_t pattern = 0x5ac00000; |
| |
| DEFINE_STATIC_FORMAT(A64DOpcodeDataProcessing1Source, thisObj); |
| |
| const char* format(); |
| |
| const char* opName() { return s_opNames[opNameIndex()]; } |
| unsigned sBit() { return (m_opcode >> 29) & 0x1; } |
| unsigned opCode() { return (m_opcode >> 10) & 0x3f; } |
| unsigned opCode2() { return (m_opcode >> 16) & 0x1f; } |
| unsigned opNameIndex() { return (opCode() & 0x7); } |
| }; |
| |
| class A64DOpcodeDataProcessing2Source : public A64DOpcode { |
| private: |
| static const char* const s_opNames[16]; |
| |
| public: |
| static constexpr uint32_t mask = 0x5fe00000; |
| static constexpr uint32_t pattern = 0x1ac00000; |
| |
| DEFINE_STATIC_FORMAT(A64DOpcodeDataProcessing2Source, thisObj); |
| |
| const char* format(); |
| |
| const char* opName() { return s_opNames[opNameIndex()]; } |
| unsigned sBit() { return (m_opcode >> 29) & 0x1; } |
| unsigned opCode() { return (m_opcode >> 10) & 0x3f; } |
| unsigned opNameIndex() { return (m_opcode >> 10) & 0xf; } |
| }; |
| |
| class A64DOpcodeDataProcessing3Source : public A64DOpcode { |
| private: |
| static const char* const s_opNames[16]; |
| static const char* const s_pseudoOpNames[16]; |
| |
| public: |
| static constexpr uint32_t mask = 0x1f000000; |
| static constexpr uint32_t pattern = 0x1b000000; |
| |
| DEFINE_STATIC_FORMAT(A64DOpcodeDataProcessing3Source, thisObj); |
| |
| const char* format(); |
| |
| const char* opName() { return ra() == 31 ? s_pseudoOpNames[opNum() & 0xf] : s_opNames[opNum() & 0xf]; } |
| unsigned ra() { return (m_opcode >> 10) & 0x1f; } |
| unsigned op54() { return (m_opcode >> 29) & 0x3; } |
| unsigned op31() { return (m_opcode >> 21) & 0x7; } |
| unsigned op0() { return (m_opcode >> 15) & 0x1; } |
| unsigned opNum() { return ((m_opcode >> 25) & 0x30) | ((m_opcode >> 20) & 0xe) | ((m_opcode >> 15) & 0x1); } |
| }; |
| |
| class A64OpcodeExceptionGeneration : public A64DOpcode { |
| public: |
| static constexpr uint32_t mask = 0xff000000; |
| static constexpr uint32_t pattern = 0xd4000000; |
| |
| DEFINE_STATIC_FORMAT(A64OpcodeExceptionGeneration, thisObj); |
| |
| const char* format(); |
| |
| unsigned opc() { return (m_opcode>>21) & 0x7; } |
| unsigned op2() { return (m_opcode>>2) & 0x7; } |
| unsigned ll() { return m_opcode & 0x3; } |
| unsigned immediate16() { return (static_cast<unsigned>((m_opcode >> 5) & 0xffff) << 16) >> 16; } |
| }; |
| |
| class A64DOpcodeExtract : public A64DOpcode { |
| public: |
| static constexpr uint32_t mask = 0x1f800000; |
| static constexpr uint32_t pattern = 0x13800000; |
| |
| DEFINE_STATIC_FORMAT(A64DOpcodeExtract, thisObj); |
| |
| const char* format(); |
| |
| unsigned op21() { return (m_opcode >> 29) & 0x3; } |
| unsigned nBit() { return (m_opcode >> 22) & 0x1; } |
| unsigned o0Bit() { return (m_opcode >> 21) & 0x1; } |
| unsigned immediateS() { return (m_opcode >> 10) & 0x3f; } |
| }; |
| |
| class A64DOpcodeFloatingPointOps : public A64DOpcode { |
| public: |
| unsigned mBit() { return (m_opcode >> 31) & 0x1; } |
| unsigned sBit() { return (m_opcode >> 29) & 0x1; } |
| unsigned type() { return (m_opcode >> 22) & 0x3; } |
| }; |
| |
| class A64DOpcodeFloatingPointCompare : public A64DOpcodeFloatingPointOps { |
| private: |
| static const char* const s_opNames[16]; |
| |
| public: |
| static constexpr uint32_t mask = 0x5f203c00; |
| static constexpr uint32_t pattern = 0x1e202000; |
| |
| DEFINE_STATIC_FORMAT(A64DOpcodeFloatingPointCompare, thisObj); |
| |
| const char* format(); |
| |
| const char* opName() { return (opNum() & 0x2) ? "fcmpe" : "fcmp"; } |
| |
| unsigned op() { return (m_opcode >> 14) & 0x3; } |
| unsigned opCode2() { return m_opcode & 0x1f; } |
| unsigned opNum() { return (m_opcode >> 3) & 0x3; } |
| }; |
| |
| class A64DOpcodeFloatingPointConditionalSelect : public A64DOpcodeFloatingPointOps { |
| public: |
| static constexpr uint32_t mask = 0x5f200c00; |
| static constexpr uint32_t pattern = 0x1e200c00; |
| |
| DEFINE_STATIC_FORMAT(A64DOpcodeFloatingPointConditionalSelect, thisObj); |
| |
| const char* format(); |
| |
| const char* opName() { return "fcsel"; } |
| |
| unsigned condition() { return (m_opcode >> 12) & 0xf; } |
| }; |
| |
| class A64DOpcodeFloatingPointDataProcessing1Source : public A64DOpcodeFloatingPointOps { |
| private: |
| static const char* const s_opNames[16]; |
| |
| public: |
| static constexpr uint32_t mask = 0x5f207c00; |
| static constexpr uint32_t pattern = 0x1e204000; |
| |
| DEFINE_STATIC_FORMAT(A64DOpcodeFloatingPointDataProcessing1Source, thisObj); |
| |
| const char* format(); |
| |
| const char* opName() { return s_opNames[opNum()]; } |
| |
| unsigned opNum() { return (m_opcode >> 15) & 0x3f; } |
| }; |
| |
| class A64DOpcodeFloatingPointDataProcessing2Source : public A64DOpcodeFloatingPointOps { |
| private: |
| static const char* const s_opNames[16]; |
| |
| public: |
| static constexpr uint32_t mask = 0x5f200800; |
| static constexpr uint32_t pattern = 0x1e200800; |
| |
| DEFINE_STATIC_FORMAT(A64DOpcodeFloatingPointDataProcessing2Source, thisObj); |
| |
| const char* format(); |
| |
| const char* opName() { return s_opNames[opNum()]; } |
| |
| unsigned opNum() { return (m_opcode >> 12) & 0xf; } |
| }; |
| |
| class A64DOpcodeFloatingFixedPointConversions : public A64DOpcodeFloatingPointOps { |
| private: |
| static const char* const s_opNames[4]; |
| |
| public: |
| static constexpr uint32_t mask = 0x5f200000; |
| static constexpr uint32_t pattern = 0x1e000000; |
| |
| DEFINE_STATIC_FORMAT(A64DOpcodeFloatingFixedPointConversions, thisObj); |
| |
| const char* format(); |
| |
| const char* opName() { return s_opNames[opNum()]; } |
| unsigned rmode() { return (m_opcode >> 19) & 0x3; } |
| unsigned opcode() { return (m_opcode >> 16) & 0x7; } |
| unsigned scale() { return (m_opcode >> 10) & 0x3f; } |
| unsigned opNum() { return (m_opcode >> 16) & 0x3; } |
| }; |
| |
| class A64DOpcodeFloatingPointIntegerConversions : public A64DOpcodeFloatingPointOps { |
| private: |
| static const char* const s_opNames[32]; |
| |
| public: |
| static constexpr uint32_t mask = 0x5f20fc00; |
| static constexpr uint32_t pattern = 0x1e200000; |
| |
| DEFINE_STATIC_FORMAT(A64DOpcodeFloatingPointIntegerConversions, thisObj); |
| |
| const char* format(); |
| |
| const char* opName() { return s_opNames[opNum()]; } |
| unsigned rmode() { return (m_opcode >> 19) & 0x3; } |
| unsigned opcode() { return (m_opcode >> 16) & 0x7; } |
| unsigned opNum() { return (m_opcode >> 16) & 0x1f; } |
| }; |
| |
| class A64DOpcodeSystem : public A64DOpcode { |
| public: |
| unsigned lBit() { return (m_opcode >> 21) & 0x1; } |
| unsigned op0() { return (m_opcode >> 19) & 0x3; } |
| unsigned op1() { return (m_opcode >> 16) & 0x7; } |
| unsigned crN() { return (m_opcode >> 12) & 0xf; } |
| unsigned crM() { return (m_opcode >> 8) & 0xf; } |
| unsigned op2() { return (m_opcode >> 5) & 0x7; } |
| }; |
| |
| class A64DOpcodeMSRImmediate : public A64DOpcodeSystem { |
| public: |
| static constexpr uint32_t mask = 0xfff8f01f; |
| static constexpr uint32_t pattern = 0xd500401f; |
| |
| DEFINE_STATIC_FORMAT(A64DOpcodeMSRImmediate, thisObj); |
| |
| const char* format(); |
| }; |
| |
| class A64DOpcodeMSROrMRSRegister : public A64DOpcodeSystem { |
| public: |
| static constexpr uint32_t mask = 0xffd00000; |
| static constexpr uint32_t pattern = 0xd5100000; |
| |
| DEFINE_STATIC_FORMAT(A64DOpcodeMSROrMRSRegister, thisObj); |
| |
| const char* format(); |
| |
| const char* opName() { return lBit() ? "mrs" : "msr"; } |
| unsigned systemRegister() { return ((op0() << 14) | (op1() << 11) | (crN() << 7) | (crM() << 3) | op2()); } |
| }; |
| |
| class A64DOpcodeHint : public A64DOpcodeSystem { |
| private: |
| static const char* const s_opNames[32]; |
| |
| public: |
| static constexpr uint32_t mask = 0xfffff01f; |
| static constexpr uint32_t pattern = 0xd503201f; |
| |
| DEFINE_STATIC_FORMAT(A64DOpcodeHint, thisObj); |
| |
| const char* format(); |
| |
| const char* opName(); |
| unsigned immediate7() { return (m_opcode >> 5) & 0x7f; } |
| }; |
| |
| class A64DOpcodeSystemSync : public A64DOpcodeSystem { |
| static const char* const s_opNames[8]; |
| static const char* const s_optionNames[16]; |
| |
| public: |
| static constexpr uint32_t mask = 0xfffff01f; |
| static constexpr uint32_t pattern = 0xd503301f; |
| |
| DEFINE_STATIC_FORMAT(A64DOpcodeSystemSync, thisObj); |
| |
| const char* format(); |
| |
| const char* opName() { return s_opNames[op2()]; } |
| const char* option() { return s_optionNames[crM()]; } |
| }; |
| |
| class A64DOpcodeLoadStore : public A64DOpcode { |
| private: |
| static const char* const s_opNames[32]; |
| |
| protected: |
| const char* opName() |
| { |
| return s_opNames[opNumber()]; |
| } |
| |
| unsigned size() { return (m_opcode >> 30) & 0x3; } |
| unsigned vBit() { return (m_opcode >> 26) & 0x1; } |
| unsigned opc() { return (m_opcode >> 22) & 0x3; } |
| unsigned opNumber() { return (size() <<3 ) | (vBit() << 2) | opc(); } |
| bool is64BitRT() { return ((opNumber() & 0x17) == 0x02) || ((opNumber() & 0x1e) == 0x18); } |
| }; |
| |
| class A64DOpcodeLoadStoreExclusive : public A64DOpcodeLoadStore { |
| private: |
| static const char* const s_opNames[64]; |
| |
| public: |
| static constexpr uint32_t mask = 0x3f000000; |
| static constexpr uint32_t pattern = 0x08000000; |
| |
| DEFINE_STATIC_FORMAT(A64DOpcodeLoadStoreExclusive, thisObj); |
| |
| const char* format(); |
| |
| const char* opName() |
| { |
| return s_opNames[opNumber()]; |
| } |
| |
| unsigned rs() { return rm(); } |
| unsigned rt2() { return (m_opcode >> 10) & 0x1f; } |
| unsigned o0() { return (m_opcode >> 15) & 0x1; } |
| unsigned o1() { return (m_opcode >> 21) & 0x1; } |
| unsigned o2() { return (m_opcode >> 23) & 0x1; } |
| unsigned loadBit() { return (m_opcode >> 22) & 0x1; } |
| unsigned opNumber() { return (size() << 4 ) | (o2() << 3) | (loadBit() << 2) | (o1() << 1) | o0(); } |
| bool isPairOp() { return (size() & 0x10) && o1(); } |
| }; |
| |
| class A64DOpcodeLoadStoreImmediate : public A64DOpcodeLoadStore { |
| private: |
| static const char* const s_unprivilegedOpNames[32]; |
| static const char* const s_unscaledOpNames[32]; |
| |
| public: |
| static constexpr uint32_t mask = 0x3b200000; |
| static constexpr uint32_t pattern = 0x38000000; |
| |
| DEFINE_STATIC_FORMAT(A64DOpcodeLoadStoreImmediate, thisObj); |
| |
| const char* format(); |
| |
| const char* unprivilegedOpName() |
| { |
| return s_unprivilegedOpNames[opNumber()]; |
| } |
| const char* unscaledOpName() |
| { |
| return s_unscaledOpNames[opNumber()]; |
| } |
| unsigned type() { return (m_opcode >> 10) & 0x3; } |
| int immediate9() { return (static_cast<int>((m_opcode >> 12) & 0x1ff) << 23) >> 23; } |
| }; |
| |
| class A64DOpcodeLoadStoreRegisterOffset : public A64DOpcodeLoadStore { |
| public: |
| static constexpr uint32_t mask = 0x3b200c00; |
| static constexpr uint32_t pattern = 0x38200800; |
| |
| DEFINE_STATIC_FORMAT(A64DOpcodeLoadStoreRegisterOffset, thisObj); |
| |
| const char* format(); |
| |
| unsigned option() { return (m_opcode >> 13) & 0x7; } |
| int sBit() { return (m_opcode >> 12) & 0x1; } |
| }; |
| |
| class A64DOpcodeLoadStoreAuthenticated : public A64DOpcodeLoadStore { |
| private: |
| static const char* const s_opNames[2]; |
| |
| protected: |
| const char* opName() |
| { |
| return s_opNames[opNumber()]; |
| } |
| |
| public: |
| static constexpr uint32_t mask = 0xff200400; |
| static constexpr uint32_t pattern = 0xf8200400; |
| |
| DEFINE_STATIC_FORMAT(A64DOpcodeLoadStoreAuthenticated, thisObj); |
| |
| const char* format(); |
| |
| unsigned opNum() { return mBit(); } |
| unsigned mBit() { return (m_opcode >> 23) & 0x1; } |
| unsigned sBit() { return (m_opcode >> 22) & 0x1; } |
| unsigned wBit() { return (m_opcode >> 11) & 0x1; } |
| int immediate10() { return (sBit() << 9) | ((m_opcode >> 12) & 0x1ff); } |
| |
| }; |
| |
| class A64DOpcodeLoadAtomic : public A64DOpcodeLoadStore { |
| private: |
| static const char* const s_opNames[64]; |
| |
| protected: |
| const char* opName() |
| { |
| unsigned number = opNumber(); |
| if (number < 64) |
| return s_opNames[number]; |
| return nullptr; |
| } |
| |
| public: |
| static constexpr uint32_t mask = 0b00111111'00100000'10001100'00000000U; |
| static constexpr uint32_t pattern = 0b00111000'00100000'00000000'00000000U; |
| |
| DEFINE_STATIC_FORMAT(A64DOpcodeLoadAtomic, thisObj); |
| |
| const char* format(); |
| |
| unsigned rs() { return rm(); } |
| unsigned opc() { return (m_opcode >> 12) & 0b111; } |
| unsigned ar() { return (m_opcode >> 22) & 0b11; } |
| unsigned opNumber() { return (opc() << 4) | (size() << 2) | ar(); } |
| }; |
| |
| class A64DOpcodeSwapAtomic : public A64DOpcodeLoadStore { |
| private: |
| static const char* const s_opNames[16]; |
| |
| protected: |
| const char* opName() |
| { |
| return s_opNames[opNumber()]; |
| } |
| |
| public: |
| static constexpr uint32_t mask = 0b00111111'00100000'11111100'00000000U; |
| static constexpr uint32_t pattern = 0b00111000'00100000'10000000'00000000U; |
| |
| DEFINE_STATIC_FORMAT(A64DOpcodeSwapAtomic, thisObj); |
| |
| const char* format(); |
| |
| unsigned rs() { return rm(); } |
| unsigned ar() { return (m_opcode >> 22) & 0b11; } |
| unsigned opNumber() { return (size() << 2) | ar(); } |
| }; |
| |
| class A64DOpcodeCAS : public A64DOpcodeLoadStore { |
| private: |
| static const char* const s_opNames[16]; |
| |
| protected: |
| const char* opName() |
| { |
| return s_opNames[opNumber()]; |
| } |
| |
| public: |
| static constexpr uint32_t mask = 0b00111111'10100000'01111100'00000000U; |
| static constexpr uint32_t pattern = 0b00001000'10100000'01111100'00000000U; |
| |
| DEFINE_STATIC_FORMAT(A64DOpcodeCAS, thisObj); |
| |
| const char* format(); |
| |
| unsigned rs() { return rm(); } |
| unsigned o1() { return (m_opcode >> 15) & 0x1; } |
| unsigned l() { return (m_opcode >> 22) & 0x1; } |
| unsigned opNumber() { return (size() << 2) | (l() << 1) | o1(); } |
| }; |
| |
| class A64DOpcodeLoadStoreRegisterPair : public A64DOpcodeLoadStore { |
| public: |
| static constexpr uint32_t mask = 0x38000000; |
| static constexpr uint32_t pattern = 0x28000000; |
| |
| DEFINE_STATIC_FORMAT(A64DOpcodeLoadStoreRegisterPair, thisObj); |
| |
| const char* format(); |
| const char* opName(); |
| |
| unsigned rt2() { return (m_opcode >> 10) & 0x1f; } |
| int immediate7() { return (static_cast<int>((m_opcode >> 15) & 0x7f) << 25) >> 25; } |
| unsigned offsetMode() { return (m_opcode >> 23) & 0x7; } |
| int lBit() { return (m_opcode >> 22) & 0x1; } |
| }; |
| |
| class A64DOpcodeLoadStoreUnsignedImmediate : public A64DOpcodeLoadStore { |
| public: |
| static constexpr uint32_t mask = 0x3b000000; |
| static constexpr uint32_t pattern = 0x39000000; |
| |
| DEFINE_STATIC_FORMAT(A64DOpcodeLoadStoreUnsignedImmediate, thisObj); |
| |
| const char* format(); |
| |
| unsigned immediate12() { return (m_opcode >> 10) & 0xfff; } |
| }; |
| |
| class A64DOpcodeLogical : public A64DOpcode { |
| private: |
| static const char* const s_opNames[8]; |
| |
| public: |
| const char* opName(unsigned opNumber) |
| { |
| return s_opNames[opNumber & 0x7]; |
| } |
| |
| unsigned opc() { return (m_opcode >> 29) & 0x3; } |
| unsigned nBit() { return (m_opcode >> 21) & 0x1; } |
| }; |
| |
| class A64DOpcodeLogicalImmediate : public A64DOpcodeLogical { |
| public: |
| static constexpr uint32_t mask = 0x1f800000; |
| static constexpr uint32_t pattern = 0x12000000; |
| |
| DEFINE_STATIC_FORMAT(A64DOpcodeLogicalImmediate, thisObj); |
| |
| const char* format(); |
| |
| bool isTst() { return ((opNumber() == 6) && (rd() == 31)); } |
| bool isMov() { return ((opNumber() == 2) && (rn() == 31)); } |
| unsigned opNumber() { return opc() << 1; } |
| unsigned nBit() { return (m_opcode >> 22) & 0x1; } |
| unsigned immediateR() { return (m_opcode >> 16) & 0x3f; } |
| unsigned immediateS() { return (m_opcode >> 10) & 0x3f; } |
| }; |
| |
| class A64DOpcodeLogicalShiftedRegister : public A64DOpcodeLogical { |
| public: |
| static constexpr uint32_t mask = 0x1f000000; |
| static constexpr uint32_t pattern = 0x0a000000; |
| |
| DEFINE_STATIC_FORMAT(A64DOpcodeLogicalShiftedRegister, thisObj); |
| |
| const char* format(); |
| |
| bool isTst() { return ((opNumber() == 6) && (rd() == 31)); } |
| bool isMov() { return ((opc() == 1) && (rn() == 31)); } |
| unsigned opNumber() { return (opc() << 1) | nBit(); } |
| unsigned shift() { return (m_opcode >> 22) & 0x3; } |
| int immediate6() { return (static_cast<uint32_t>((m_opcode >> 10) & 0x3f) << 26) >> 26; } |
| }; |
| |
| class A64DOpcodeMoveWide : public A64DOpcode { |
| private: |
| static const char* const s_opNames[4]; |
| |
| public: |
| static constexpr uint32_t mask = 0x1f800000; |
| static constexpr uint32_t pattern = 0x12800000; |
| |
| DEFINE_STATIC_FORMAT(A64DOpcodeMoveWide, thisObj); |
| |
| const char* format(); |
| |
| const char* opName() { return s_opNames[opc()]; } |
| unsigned opc() { return (m_opcode >> 29) & 0x3; } |
| unsigned hw() { return (m_opcode >> 21) & 0x3; } |
| unsigned immediate16() { return (m_opcode >> 5) & 0xffff; } |
| }; |
| |
| class A64DOpcodeTestAndBranchImmediate : public A64DOpcode { |
| public: |
| static constexpr uint32_t mask = 0x7e000000; |
| static constexpr uint32_t pattern = 0x36000000; |
| |
| DEFINE_STATIC_FORMAT(A64DOpcodeTestAndBranchImmediate, thisObj); |
| |
| const char* format(); |
| |
| unsigned bitNumber() { return ((m_opcode >> 26) & 0x20) | ((m_opcode >> 19) & 0x1f); } |
| unsigned opBit() { return (m_opcode >> 24) & 0x1; } |
| int immediate14() { return (static_cast<int>((m_opcode >> 5) & 0x3fff) << 18) >> 18; } |
| }; |
| |
| class A64DOpcodeUnconditionalBranchImmediate : public A64DOpcode { |
| public: |
| static constexpr uint32_t mask = 0x7c000000; |
| static constexpr uint32_t pattern = 0x14000000; |
| |
| DEFINE_STATIC_FORMAT(A64DOpcodeUnconditionalBranchImmediate, thisObj); |
| |
| const char* format(); |
| |
| unsigned op() { return (m_opcode >> 31) & 0x1; } |
| int immediate26() { return (static_cast<int>(m_opcode & 0x3ffffff) << 6) >> 6; } |
| }; |
| |
| class A64DOpcodeUnconditionalBranchRegister : public A64DOpcode { |
| private: |
| static const char* const s_opNames[8]; |
| static const char* const s_AuthOpNames[20]; |
| |
| public: |
| static constexpr uint32_t mask = 0xfe1f0000; |
| static constexpr uint32_t pattern = 0xd61f0000; |
| |
| DEFINE_STATIC_FORMAT(A64DOpcodeUnconditionalBranchRegister, thisObj); |
| |
| const char* format(); |
| |
| const char* opName() { return s_opNames[opc()]; } |
| const char* authOpName(); |
| unsigned opc() { return (m_opcode >> 21) & 0xf; } |
| unsigned authOpCode() {return (opc() << 1) | mBit(); } |
| unsigned op2() { return (m_opcode >> 16) & 0x1f; } |
| unsigned op3() { return (m_opcode >> 10) & 0x3f; } |
| unsigned op4() { return m_opcode & 0xf; } |
| unsigned mBit() { return (m_opcode >> 10) & 1; } |
| unsigned rm() { return rd(); } |
| }; |
| |
| } } // namespace JSC::ARM64Disassembler |
| |
| using JSC::ARM64Disassembler::A64DOpcode; |