aboutsummaryrefslogtreecommitdiffstats
path: root/src/3rdparty/masm/disassembler/ARM64/A64DOpcode.cpp
diff options
context:
space:
mode:
authorErik Verbruggen <erik.verbruggen@digia.com>2016-01-14 15:39:17 +0100
committerErik Verbruggen <erik.verbruggen@theqtcompany.com>2016-01-19 11:19:38 +0000
commitf8bcf723ce920981705c6913fffb1f4d3de6793d (patch)
treed0bced1e1e422c32d3f148c1ded7846063fb25f6 /src/3rdparty/masm/disassembler/ARM64/A64DOpcode.cpp
parent9876f7ac8bd3679a9ed0e617aaadad032403a88f (diff)
V4: add Aarch64/ARM64 support.
This uses the JavaScriptCore assembler rev. 195098. It is tested on iOS (for which it is disabled, as it only allows marking pages as executable when running from Xcode). Testing on Linux will be done when hardware arrives. Change-Id: I650e15fec03c27d4b326a2d70863a89b85cfc5c3 Reviewed-by: Simon Hausmann <simon.hausmann@theqtcompany.com>
Diffstat (limited to 'src/3rdparty/masm/disassembler/ARM64/A64DOpcode.cpp')
-rw-r--r--src/3rdparty/masm/disassembler/ARM64/A64DOpcode.cpp1202
1 files changed, 1202 insertions, 0 deletions
diff --git a/src/3rdparty/masm/disassembler/ARM64/A64DOpcode.cpp b/src/3rdparty/masm/disassembler/ARM64/A64DOpcode.cpp
new file mode 100644
index 0000000000..52a92c669c
--- /dev/null
+++ b/src/3rdparty/masm/disassembler/ARM64/A64DOpcode.cpp
@@ -0,0 +1,1202 @@
+/*
+ * 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 <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(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<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* 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<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
+
+#endif // USE(ARM64_DISASSEMBLER)