]> git.saurik.com Git - apple/javascriptcore.git/blobdiff - disassembler/ARM64/A64DOpcode.cpp
JavaScriptCore-1218.tar.gz
[apple/javascriptcore.git] / disassembler / ARM64 / A64DOpcode.cpp
diff --git a/disassembler/ARM64/A64DOpcode.cpp b/disassembler/ARM64/A64DOpcode.cpp
new file mode 100644 (file)
index 0000000..5e79cc3
--- /dev/null
@@ -0,0 +1,1132 @@
+/*
+ * 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.
+ */
+
+#include "config.h"
+
+#include "A64DOpcode.h"
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+
+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(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 == 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<int32_t>(immediate19()));
+    return m_formatBuffer;
+}
+
+const char* A64DOpcodeConditionalBranchImmediate::format()
+{
+    bufferPrintf("   b.%-5.5s", conditionName(condition()));
+    appendPCRelativeOffset(m_currentPC, static_cast<int32_t>(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* 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<int32_t>(immediate14()));
+    return m_formatBuffer;
+}
+
+const char* A64DOpcodeUnconditionalBranchImmediate::format()
+{
+    appendInstructionName(op() ? "bl" : "b");
+    appendPCRelativeOffset(m_currentPC, static_cast<int32_t>(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