| // Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| // This file is a semi-automated port of |
| // runtime/vm/compiler/assembler/disassembler_arm64.cc |
| // with constants from runtime/vm/constants_arm64.h. |
| // |
| // TODO: use string interpoloation instead of custom format options. |
| // TODO: refactor to better fit idiomatic Dart style. |
| // TODO: follow the standard arm64 assembly language in the disassembly. |
| |
| import 'dart:typed_data'; |
| |
| // --- Constants from runtime/vm/constants_arm64.h --- |
| |
| // enum Register |
| const int R0 = 0; |
| const int R1 = 1; |
| const int R2 = 2; |
| const int R3 = 3; |
| const int R4 = 4; |
| const int R5 = 5; |
| const int R6 = 6; |
| const int R7 = 7; |
| const int R8 = 8; |
| const int R9 = 9; |
| const int R10 = 10; |
| const int R11 = 11; |
| const int R12 = 12; |
| const int R13 = 13; |
| const int R14 = 14; |
| const int R15 = 15; |
| const int R16 = 16; |
| const int R17 = 17; |
| const int R18 = 18; |
| const int R19 = 19; |
| const int R20 = 20; |
| const int R21 = 21; |
| const int R22 = 22; |
| const int R23 = 23; |
| const int R24 = 24; |
| const int R25 = 25; |
| const int R26 = 26; |
| const int R27 = 27; |
| const int R28 = 28; |
| const int R29 = 29; |
| const int R30 = 30; |
| const int R31 = 31; |
| const int kNumberOfCpuRegisters = 32; |
| const int kNoRegister = -1; |
| |
| // Aliases. |
| const int IP0 = R16; |
| const int IP1 = R17; |
| const int SP = R15; |
| const int FP = R29; |
| const int LR = R30; |
| const int LINK_REGISTER = R30; |
| |
| // enum VRegister |
| const int V0 = 0; |
| const int V1 = 1; |
| const int V2 = 2; |
| const int V3 = 3; |
| const int V4 = 4; |
| const int V5 = 5; |
| const int V6 = 6; |
| const int V7 = 7; |
| const int V8 = 8; |
| const int V9 = 9; |
| const int V10 = 10; |
| const int V11 = 11; |
| const int V12 = 12; |
| const int V13 = 13; |
| const int V14 = 14; |
| const int V15 = 15; |
| const int V16 = 16; |
| const int V17 = 17; |
| const int V18 = 18; |
| const int V19 = 19; |
| const int V20 = 20; |
| const int V21 = 21; |
| const int V22 = 22; |
| const int V23 = 23; |
| const int V24 = 24; |
| const int V25 = 25; |
| const int V26 = 26; |
| const int V27 = 27; |
| const int V28 = 28; |
| const int V29 = 29; |
| const int V30 = 30; |
| const int V31 = 31; |
| const int kNumberOfVRegisters = 32; |
| const int kNoVRegister = -1; |
| |
| const List<String> _cpuRegNames = [ |
| 'r0', |
| 'r1', |
| 'r2', |
| 'r3', |
| 'r4', |
| 'r5', |
| 'r6', |
| 'r7', |
| 'r8', |
| 'r9', |
| 'r10', |
| 'r11', |
| 'r12', |
| 'r13', |
| 'r14', |
| 'sp', |
| 'tmp', |
| 'r17', |
| 'r18', |
| 'r19', |
| 'r20', |
| 'r21', |
| 'null', |
| 'r23', |
| 'code', |
| 'r25', |
| 'thr', |
| 'pp', |
| 'heap_bits', |
| 'fp', |
| 'lr', |
| 'zr/csp', |
| ]; |
| |
| // Register aliases. |
| const int TMP = R16; |
| const int TMP2 = R17; |
| const int PP = R27; |
| const int CODE_REG = R24; |
| const int SPREG = R15; |
| const int THR = R26; |
| const int HEAP_BITS = R28; |
| const int NULL_REG = R22; |
| |
| // Bit constants |
| const int B0 = (1 << 0); |
| const int B1 = (1 << 1); |
| const int B2 = (1 << 2); |
| const int B3 = (1 << 3); |
| const int B4 = (1 << 4); |
| const int B5 = (1 << 5); |
| const int B6 = (1 << 6); |
| const int B7 = (1 << 7); |
| const int B8 = (1 << 8); |
| const int B9 = (1 << 9); |
| const int B10 = (1 << 10); |
| const int B11 = (1 << 11); |
| const int B12 = (1 << 12); |
| const int B13 = (1 << 13); |
| const int B14 = (1 << 14); |
| const int B15 = (1 << 15); |
| const int B16 = (1 << 16); |
| const int B17 = (1 << 17); |
| const int B18 = (1 << 18); |
| const int B19 = (1 << 19); |
| const int B20 = (1 << 20); |
| const int B21 = (1 << 21); |
| const int B22 = (1 << 22); |
| const int B23 = (1 << 23); |
| const int B24 = (1 << 24); |
| const int B25 = (1 << 25); |
| const int B26 = (1 << 26); |
| const int B27 = (1 << 27); |
| const int B28 = (1 << 28); |
| const int B29 = (1 << 29); |
| const int B30 = (1 << 30); |
| const int B31 = (1 << 31); |
| |
| // Opcodes |
| class MainOp { |
| static const int DPImmediateMask = 0x1c000000; |
| static const int DPImmediateFixed = B28; |
| static const int CompareBranchMask = 0x1c000000; |
| static const int CompareBranchFixed = B28 | B26; |
| static const int LoadStoreMask = B27 | B25; |
| static const int LoadStoreFixed = B27; |
| static const int DPRegisterMask = 0x0e000000; |
| static const int DPRegisterFixed = B27 | B25; |
| static const int DPSimd1Mask = 0x1e000000; |
| static const int DPSimd1Fixed = B27 | B26 | B25; |
| static const int DPSimd2Mask = 0x1e000000; |
| static const int DPSimd2Fixed = B28 | DPSimd1Fixed; |
| static const int FPMask = 0x5e000000; |
| static const int FPFixed = B28 | B27 | B26 | B25; |
| } |
| |
| class MoveWideOp { |
| static const int MoveWideMask = 0x1f800000; |
| static const int MoveWideFixed = MainOp.DPImmediateFixed | B25 | B23; |
| static const int MOVN = MoveWideFixed; |
| static const int MOVZ = MoveWideFixed | B30; |
| static const int MOVK = MoveWideFixed | B30 | B29; |
| } |
| |
| class AddSubImmOp { |
| static const int AddSubImmMask = 0x1f000000; |
| static const int AddSubImmFixed = MainOp.DPImmediateFixed | B24; |
| static const int ADDI = AddSubImmFixed; |
| static const int SUBI = AddSubImmFixed | B30; |
| } |
| |
| class BitfieldOp { |
| static const int BitfieldMask = 0x1f800000; |
| static const int BitfieldFixed = 0x13000000; |
| } |
| |
| class LogicalImmOp { |
| static const int LogicalImmMask = 0x1f800000; |
| static const int LogicalImmFixed = MainOp.DPImmediateFixed | B25; |
| } |
| |
| class PCRelOp { |
| static const int PCRelMask = 0x1f000000; |
| static const int PCRelFixed = MainOp.DPImmediateFixed; |
| } |
| |
| class ExceptionGenOp { |
| static const int ExceptionGenMask = 0xff000000; |
| static const int ExceptionGenFixed = MainOp.CompareBranchFixed | B31 | B30; |
| static const int SVC = ExceptionGenFixed | B0; |
| static const int BRK = ExceptionGenFixed | B21; |
| static const int HLT = ExceptionGenFixed | B22; |
| } |
| |
| class SystemOp { |
| static const int SystemMask = 0xffc00000; |
| static const int SystemFixed = MainOp.CompareBranchFixed | B31 | B30 | B24; |
| static const int CLREX = |
| SystemFixed | |
| B17 | |
| B16 | |
| B13 | |
| B12 | |
| B11 | |
| B10 | |
| B9 | |
| B8 | |
| B6 | |
| B4 | |
| B3 | |
| B2 | |
| B1 | |
| B0; |
| } |
| |
| class UnconditionalBranchRegOp { |
| static const int UnconditionalBranchRegMask = 0xfe000000; |
| static const int UnconditionalBranchRegFixed = |
| MainOp.CompareBranchFixed | B31 | B30 | B25; |
| } |
| |
| class CompareAndBranchOp { |
| static const int CompareAndBranchMask = 0x7e000000; |
| static const int CompareAndBranchFixed = MainOp.CompareBranchFixed | B29; |
| } |
| |
| class ConditionalBranchOp { |
| static const int ConditionalBranchMask = 0xfe000000; |
| static const int ConditionalBranchFixed = MainOp.CompareBranchFixed | B30; |
| } |
| |
| class TestAndBranchOp { |
| static const int TestAndBranchMask = 0x7e000000; |
| static const int TestAndBranchFixed = MainOp.CompareBranchFixed | B29 | B25; |
| } |
| |
| class UnconditionalBranchOp { |
| static const int UnconditionalBranchMask = 0x7c000000; |
| static const int UnconditionalBranchFixed = MainOp.CompareBranchFixed; |
| } |
| |
| class AtomicMemoryOp { |
| static const int AtomicMemoryMask = 0x3f200c00; |
| static const int AtomicMemoryFixed = B29 | B28 | B27 | B21; |
| static const int LDCLR = AtomicMemoryFixed | B12; |
| static const int LDSET = AtomicMemoryFixed | B13 | B12; |
| } |
| |
| class LoadStoreRegOp { |
| static const int LoadStoreRegMask = 0x3a000000; |
| static const int LoadStoreRegFixed = MainOp.LoadStoreFixed | B29 | B28; |
| } |
| |
| class LoadStoreRegPairOp { |
| static const int LoadStoreRegPairMask = 0x3a000000; |
| static const int LoadStoreRegPairFixed = MainOp.LoadStoreFixed | B29; |
| } |
| |
| class LoadRegLiteralOp { |
| static const int LoadRegLiteralMask = 0x3b000000; |
| static const int LoadRegLiteralFixed = MainOp.LoadStoreFixed | B28; |
| } |
| |
| class LoadStoreExclusiveOp { |
| static const int LoadStoreExclusiveMask = 0x3f000000; |
| static const int LoadStoreExclusiveFixed = B27; |
| } |
| |
| class AddSubShiftExtOp { |
| static const int AddSubShiftExtMask = 0x1f000000; |
| static const int AddSubShiftExtFixed = MainOp.DPRegisterFixed | B24; |
| } |
| |
| class AddSubWithCarryOp { |
| static const int AddSubWithCarryMask = 0x1fe00000; |
| static const int AddSubWithCarryFixed = MainOp.DPRegisterFixed | B28; |
| } |
| |
| class LogicalShiftOp { |
| static const int LogicalShiftMask = 0x1f000000; |
| static const int LogicalShiftFixed = MainOp.DPRegisterFixed; |
| } |
| |
| class MiscDP1SourceOp { |
| static const int MiscDP1SourceMask = 0x5fe00000; |
| static const int MiscDP1SourceFixed = |
| MainOp.DPRegisterFixed | B30 | B28 | B23 | B22; |
| } |
| |
| class MiscDP2SourceOp { |
| static const int MiscDP2SourceMask = 0x5fe00000; |
| static const int MiscDP2SourceFixed = |
| MainOp.DPRegisterFixed | B28 | B23 | B22; |
| } |
| |
| class MiscDP3SourceOp { |
| static const int MiscDP3SourceMask = 0x1f000000; |
| static const int MiscDP3SourceFixed = MainOp.DPRegisterFixed | B28 | B24; |
| static const int MADDW = MiscDP3SourceFixed; |
| static const int MADD = MiscDP3SourceFixed | B31; |
| static const int MSUBW = MiscDP3SourceFixed | B15; |
| static const int MSUB = MiscDP3SourceFixed | B31 | B15; |
| static const int SMULH = MiscDP3SourceFixed | B31 | B22; |
| static const int UMULH = MiscDP3SourceFixed | B31 | B23 | B22; |
| static const int UMADDL = MiscDP3SourceFixed | B31 | B23 | B21; |
| static const int SMADDL = MiscDP3SourceFixed | B31 | B21; |
| static const int SMSUBL = MiscDP3SourceFixed | B31 | B21 | B15; |
| static const int UMSUBL = MiscDP3SourceFixed | B31 | B23 | B21 | B15; |
| } |
| |
| class ConditionalSelectOp { |
| static const int ConditionalSelectMask = 0x1fe00000; |
| static const int ConditionalSelectFixed = MainOp.DPRegisterFixed | B28 | B23; |
| } |
| |
| class SIMDCopyOp { |
| static const int SIMDCopyMask = 0x9fe08400; |
| static const int SIMDCopyFixed = MainOp.DPSimd1Fixed | B10; |
| } |
| |
| class SIMDThreeSameOp { |
| static const int SIMDThreeSameMask = 0x9f200400; |
| static const int SIMDThreeSameFixed = MainOp.DPSimd1Fixed | B21 | B10; |
| } |
| |
| class SIMDTwoRegOp { |
| static const int SIMDTwoRegMask = 0x9f3e0c00; |
| static const int SIMDTwoRegFixed = MainOp.DPSimd1Fixed | B21 | B11; |
| } |
| |
| class FPImmOp { |
| static const int FPImmMask = 0x5f201c00; |
| static const int FPImmFixed = MainOp.FPFixed | B21 | B12; |
| } |
| |
| class FPIntCvtOp { |
| static const int FPIntCvtMask = 0x5f00fc00; |
| static const int FPIntCvtFixed = MainOp.FPFixed | B21; |
| } |
| |
| class FPOneSourceOp { |
| static const int FPOneSourceMask = 0x5f207c00; |
| static const int FPOneSourceFixed = MainOp.FPFixed | B21 | B14; |
| } |
| |
| class FPTwoSourceOp { |
| static const int FPTwoSourceMask = 0xff200c00; |
| static const int FPTwoSourceFixed = MainOp.FPFixed | B21 | B11; |
| } |
| |
| class FPCompareOp { |
| static const int FPCompareMask = 0xffa0fc07; |
| static const int FPCompareFixed = MainOp.FPFixed | B21 | B13; |
| } |
| |
| const int kDMB_ISH = 0xD5033BBF; |
| const int kDMB_ISHST = 0xD5033ABF; |
| |
| enum Shift { LSL, LSR, ASR, ROR, kMaxShift } |
| |
| const List<String> _shiftNames = ['lsl', 'lsr', 'asr', 'ror']; |
| |
| enum Extend { UXTB, UXTH, UXTW, UXTX, SXTB, SXTH, SXTW, SXTX, kMaxExtend } |
| |
| const List<String> _extendNames = [ |
| 'uxtb', |
| 'uxth', |
| 'uxtw', |
| 'uxtx', |
| 'sxtb', |
| 'sxth', |
| 'sxtw', |
| 'sxtx', |
| ]; |
| |
| enum R31Type { R31IsSP, R31IsZR } |
| |
| enum Condition { |
| EQ, |
| NE, |
| CS, |
| CC, |
| MI, |
| PL, |
| VS, |
| VC, |
| HI, |
| LS, |
| GE, |
| LT, |
| GT, |
| LE, |
| AL, |
| NV, |
| kNumberOfConditions, |
| kInvalidCondition, |
| } |
| |
| const List<String> _condNames = [ |
| 'eq', |
| 'ne', |
| 'cs', |
| 'cc', |
| 'mi', |
| 'pl', |
| 'vs', |
| 'vc', |
| 'hi', |
| 'ls', |
| 'ge', |
| 'lt', |
| 'gt', |
| 'le', |
| '', |
| 'invalid', |
| ]; |
| |
| int invertCondition(int cond) => cond ^ 1; |
| |
| class InstructionFields { |
| static const int kSFShift = 31; |
| static const int kRdShift = 0; |
| static const int kRdBits = 5; |
| static const int kRnShift = 5; |
| static const int kRnBits = 5; |
| static const int kRaShift = 10; |
| static const int kRaBits = 5; |
| static const int kRmShift = 16; |
| static const int kRmBits = 5; |
| static const int kRtShift = 0; |
| static const int kRtBits = 5; |
| static const int kRt2Shift = 10; |
| static const int kRt2Bits = 5; |
| static const int kRsShift = 16; |
| static const int kRsBits = 5; |
| static const int kVdShift = 0; |
| static const int kVdBits = 5; |
| static const int kVnShift = 5; |
| static const int kVnBits = 5; |
| static const int kVtShift = 0; |
| static const int kVtBits = 5; |
| static const int kImm12Shift = 10; |
| static const int kImm12Bits = 12; |
| static const int kImm16Shift = 5; |
| static const int kImm16Bits = 16; |
| static const int kImm19Shift = 5; |
| static const int kImm19Bits = 19; |
| static const int kImm26Shift = 0; |
| static const int kImm26Bits = 26; |
| static const int kImmSShift = 10; |
| static const int kImmSBits = 6; |
| static const int kImmRShift = 16; |
| static const int kImmRBits = 6; |
| static const int kNShift = 22; |
| static const int kHWShift = 21; |
| static const int kHWBits = 2; |
| static const int kImm12ShiftShift = 22; |
| static const int kShiftTypeShift = 22; |
| static const int kShiftTypeBits = 2; |
| static const int kExtendTypeShift = 13; |
| static const int kExtendTypeBits = 3; |
| static const int kAddShiftExtendShift = 21; |
| static const int kImm6Shift = 10; |
| static const int kImm9Shift = 12; |
| static const int kSImm7Shift = 15; |
| static const int kSelCondShift = 12; |
| static const int kCondShift = 0; |
| } |
| |
| extension type Instr(int value) { |
| int bit(int pos) => (value >> pos) & 1; |
| int bits(int start, int len) => (value >> start) & ((1 << len) - 1); |
| int signedBits(int start, int len) { |
| int val = bits(start, len); |
| if ((val & (1 << (len - 1))) != 0) { |
| val -= (1 << len); |
| } |
| return val; |
| } |
| |
| // Instruction classification using masks. |
| bool isDPImmediateOp() => |
| (value & MainOp.DPImmediateMask) == MainOp.DPImmediateFixed; |
| bool isCompareBranchOp() => |
| (value & MainOp.CompareBranchMask) == MainOp.CompareBranchFixed; |
| bool isLoadStoreOp() => |
| (value & MainOp.LoadStoreMask) == MainOp.LoadStoreFixed; |
| bool isDPRegisterOp() => |
| (value & MainOp.DPRegisterMask) == MainOp.DPRegisterFixed; |
| bool isDPSimd1Op() => (value & MainOp.DPSimd1Mask) == MainOp.DPSimd1Fixed; |
| bool isDPSimd2Op() => (value & MainOp.DPSimd2Mask) == MainOp.DPSimd2Fixed; |
| bool isFPOp() => (value & MainOp.FPMask) == MainOp.FPFixed; |
| |
| bool isMoveWideOp() => |
| (value & MoveWideOp.MoveWideMask) == MoveWideOp.MoveWideFixed; |
| bool isAddSubImmOp() => |
| (value & AddSubImmOp.AddSubImmMask) == AddSubImmOp.AddSubImmFixed; |
| bool isBitfieldOp() => |
| (value & BitfieldOp.BitfieldMask) == BitfieldOp.BitfieldFixed; |
| bool isLogicalImmOp() => |
| (value & LogicalImmOp.LogicalImmMask) == LogicalImmOp.LogicalImmFixed; |
| bool isPCRelOp() => (value & PCRelOp.PCRelMask) == PCRelOp.PCRelFixed; |
| |
| bool isExceptionGenOp() => |
| (value & ExceptionGenOp.ExceptionGenMask) == |
| ExceptionGenOp.ExceptionGenFixed; |
| bool isSystemOp() => (value & SystemOp.SystemMask) == SystemOp.SystemFixed; |
| bool isUnconditionalBranchRegOp() => |
| (value & UnconditionalBranchRegOp.UnconditionalBranchRegMask) == |
| UnconditionalBranchRegOp.UnconditionalBranchRegFixed; |
| bool isCompareAndBranchOp() => |
| (value & CompareAndBranchOp.CompareAndBranchMask) == |
| CompareAndBranchOp.CompareAndBranchFixed; |
| bool isConditionalBranchOp() => |
| (value & ConditionalBranchOp.ConditionalBranchMask) == |
| ConditionalBranchOp.ConditionalBranchFixed; |
| bool isTestAndBranchOp() => |
| (value & TestAndBranchOp.TestAndBranchMask) == |
| TestAndBranchOp.TestAndBranchFixed; |
| bool isUnconditionalBranchOp() => |
| (value & UnconditionalBranchOp.UnconditionalBranchMask) == |
| UnconditionalBranchOp.UnconditionalBranchFixed; |
| |
| bool isAtomicMemoryOp() => |
| (value & AtomicMemoryOp.AtomicMemoryMask) == |
| AtomicMemoryOp.AtomicMemoryFixed; |
| bool isLoadStoreRegOp() => |
| (value & LoadStoreRegOp.LoadStoreRegMask) == |
| LoadStoreRegOp.LoadStoreRegFixed; |
| bool isLoadStoreRegPairOp() => |
| (value & LoadStoreRegPairOp.LoadStoreRegPairMask) == |
| LoadStoreRegPairOp.LoadStoreRegPairFixed; |
| bool isLoadRegLiteralOp() => |
| (value & LoadRegLiteralOp.LoadRegLiteralMask) == |
| LoadRegLiteralOp.LoadRegLiteralFixed; |
| bool isLoadStoreExclusiveOp() => |
| (value & LoadStoreExclusiveOp.LoadStoreExclusiveMask) == |
| LoadStoreExclusiveOp.LoadStoreExclusiveFixed; |
| |
| bool isAddSubShiftExtOp() => |
| (value & AddSubShiftExtOp.AddSubShiftExtMask) == |
| AddSubShiftExtOp.AddSubShiftExtFixed; |
| bool isAddSubWithCarryOp() => |
| (value & AddSubWithCarryOp.AddSubWithCarryMask) == |
| AddSubWithCarryOp.AddSubWithCarryFixed; |
| bool isLogicalShiftOp() => |
| (value & LogicalShiftOp.LogicalShiftMask) == |
| LogicalShiftOp.LogicalShiftFixed; |
| bool isMiscDP1SourceOp() => |
| (value & MiscDP1SourceOp.MiscDP1SourceMask) == |
| MiscDP1SourceOp.MiscDP1SourceFixed; |
| bool isMiscDP2SourceOp() => |
| (value & MiscDP2SourceOp.MiscDP2SourceMask) == |
| MiscDP2SourceOp.MiscDP2SourceFixed; |
| bool isMiscDP3SourceOp() => |
| (value & MiscDP3SourceOp.MiscDP3SourceMask) == |
| MiscDP3SourceOp.MiscDP3SourceFixed; |
| bool isConditionalSelectOp() => |
| (value & ConditionalSelectOp.ConditionalSelectMask) == |
| ConditionalSelectOp.ConditionalSelectFixed; |
| |
| bool isSIMDCopyOp() => |
| (value & SIMDCopyOp.SIMDCopyMask) == SIMDCopyOp.SIMDCopyFixed; |
| bool isSIMDThreeSameOp() => |
| (value & SIMDThreeSameOp.SIMDThreeSameMask) == |
| SIMDThreeSameOp.SIMDThreeSameFixed; |
| bool isSIMDTwoRegOp() => |
| (value & SIMDTwoRegOp.SIMDTwoRegMask) == SIMDTwoRegOp.SIMDTwoRegFixed; |
| |
| bool isFPImmOp() => (value & FPImmOp.FPImmMask) == FPImmOp.FPImmFixed; |
| bool isFPIntCvtOp() => |
| (value & FPIntCvtOp.FPIntCvtMask) == FPIntCvtOp.FPIntCvtFixed; |
| bool isFPOneSourceOp() => |
| (value & FPOneSourceOp.FPOneSourceMask) == FPOneSourceOp.FPOneSourceFixed; |
| bool isFPTwoSourceOp() => |
| (value & FPTwoSourceOp.FPTwoSourceMask) == FPTwoSourceOp.FPTwoSourceFixed; |
| bool isFPCompareOp() => |
| (value & FPCompareOp.FPCompareMask) == FPCompareOp.FPCompareFixed; |
| |
| int rdField() => bits(InstructionFields.kRdShift, InstructionFields.kRdBits); |
| int rnField() => bits(InstructionFields.kRnShift, InstructionFields.kRnBits); |
| int rmField() => bits(InstructionFields.kRmShift, InstructionFields.kRmBits); |
| int raField() => bits(InstructionFields.kRaShift, InstructionFields.kRaBits); |
| int rtField() => bits(InstructionFields.kRtShift, InstructionFields.kRtBits); |
| int rt2Field() => |
| bits(InstructionFields.kRt2Shift, InstructionFields.kRt2Bits); |
| int rsField() => bits(InstructionFields.kRsShift, InstructionFields.kRsBits); |
| |
| int vdField() => bits(InstructionFields.kVdShift, InstructionFields.kVdBits); |
| int vnField() => bits(InstructionFields.kVnShift, InstructionFields.kVnBits); |
| int vmField() => bits(16, 5); // kVmShift is not defined |
| int vtField() => bits(InstructionFields.kVtShift, InstructionFields.kVtBits); |
| int vt2Field() => bits(10, 5); // kVt2Shift is not defined |
| |
| int imm12Field() => |
| bits(InstructionFields.kImm12Shift, InstructionFields.kImm12Bits); |
| int imm12ShiftField() => bits(InstructionFields.kImm12ShiftShift, 2); |
| int sImm9Field() => signedBits(InstructionFields.kImm9Shift, 9); |
| int sImm7Field() => signedBits(InstructionFields.kSImm7Shift, 7); |
| |
| int shiftTypeField() => |
| bits(InstructionFields.kShiftTypeShift, InstructionFields.kShiftTypeBits); |
| int shiftAmountField() => bits(InstructionFields.kImm6Shift, 6); |
| int extendTypeField() => bits( |
| InstructionFields.kExtendTypeShift, |
| InstructionFields.kExtendTypeBits, |
| ); |
| int extShiftAmountField() => bits(10, 3); // kImm3Shift is not defined |
| |
| int sfField() => bit(InstructionFields.kSFShift); |
| int szField() => bits(30, 2); |
| bool hasS() => bit(29) == 1; |
| int sField() => bit(29); |
| int conditionField() => bits(InstructionFields.kCondShift, 4); |
| int selectConditionField() => bits(InstructionFields.kSelCondShift, 4); |
| |
| R31Type rnMode() => |
| (isAddSubImmOp() || |
| isLoadStoreRegOp() || |
| (isAddSubShiftExtOp() && isExtend())) |
| ? R31Type.R31IsSP |
| : R31Type.R31IsZR; |
| R31Type rdMode() { |
| if (isAddSubImmOp() || (isAddSubShiftExtOp() && isExtend())) { |
| return hasS() ? R31Type.R31IsZR : R31Type.R31IsSP; |
| } |
| if (isLogicalImmOp()) { |
| return bits(29, 2) == 3 ? R31Type.R31IsZR : R31Type.R31IsSP; |
| } |
| return R31Type.R31IsZR; |
| } |
| |
| bool isShift() => |
| isLogicalShiftOp() || (bit(InstructionFields.kAddShiftExtendShift) == 0); |
| bool isExtend() => |
| !isLogicalShiftOp() && (bit(InstructionFields.kAddShiftExtendShift) == 1); |
| |
| int sImm26Field() => |
| signedBits(InstructionFields.kImm26Shift, InstructionFields.kImm26Bits); |
| int sImm19Field() => |
| signedBits(InstructionFields.kImm19Shift, InstructionFields.kImm19Bits); |
| int sImm14Field() => signedBits(5, 14); // kImm14Shift is not defined |
| |
| int imm16Field() => |
| bits(InstructionFields.kImm16Shift, InstructionFields.kImm16Bits); |
| int hwField() => bits(InstructionFields.kHWShift, InstructionFields.kHWBits); |
| int nField() => bit(InstructionFields.kNShift); |
| int immRField() => |
| bits(InstructionFields.kImmRShift, InstructionFields.kImmRBits); |
| int immSField() => |
| bits(InstructionFields.kImmSShift, InstructionFields.kImmSBits); |
| |
| int imm8Field() => bits(13, 8); // kImm8Shift is not defined |
| |
| static int vfpExpandImm(int imm8) { |
| int sign = (imm8 >> 7) & 0x1; |
| int exp = (imm8 >> 4) & 0x7; |
| int frac = imm8 & 0xf; |
| |
| if ((exp & 0x4) == 0) { |
| exp = (0x2 | (exp & 0x1)) << 1; |
| if ((exp & 0x2) == 0) { |
| exp = ((exp & 0x1) ^ 0x1); |
| } else { |
| exp = ((exp & 0x1) | 0x2); |
| } |
| exp = (0x3ff ^ 0x7) | (exp << 2); |
| } else { |
| exp = 0x3ff; |
| } |
| return (sign << 63) | (exp << 52) | (frac << 48); |
| } |
| |
| int immLogical() { |
| final n = nField(); |
| final immR = immRField(); |
| final immS = immSField(); |
| |
| if (n == 1) { |
| if (immS == 0x3F) return 0; |
| int bits = (1 << (immS + 1)) - 1; |
| return rotateRight(bits, immR, 64); |
| } else { |
| for (var width = 32; width >= 2; width ~/= 2) { |
| if ((immS & width) == 0) { |
| int mask = width - 1; |
| if ((immS & mask) == mask) return 0; |
| int bits = (1 << ((immS & mask) + 1)) - 1; |
| return repeatBitsAcrossReg( |
| 64, |
| rotateRight(bits, immR & mask, width), |
| width, |
| ); |
| } |
| } |
| } |
| return 0; // Should be unreachable |
| } |
| } |
| |
| int rotateRight(int value, int rotate, int width) { |
| int right = rotate & 63; |
| int left = (width - rotate) & 63; |
| return ((value & ((1 << right) - 1)) << left) | (value >> right); |
| } |
| |
| int repeatBitsAcrossReg(int regSize, int value, int width) { |
| int result = value & ((1 << width) - 1); |
| for (var i = width; i < regSize; i *= 2) { |
| result |= (result << i); |
| } |
| return result; |
| } |
| |
| class ARM64Decoder { |
| final StringBuffer _buffer; |
| |
| ARM64Decoder(this._buffer); |
| |
| void instructionDecode(Instr instr) { |
| if (instr.isDPImmediateOp()) { |
| decodeDPImmediate(instr); |
| } else if (instr.isCompareBranchOp()) { |
| decodeCompareBranch(instr); |
| } else if (instr.isLoadStoreOp()) { |
| decodeLoadStore(instr); |
| } else if (instr.isDPRegisterOp()) { |
| decodeDPRegister(instr); |
| } else if (instr.isDPSimd1Op()) { |
| decodeDPSimd1(instr); |
| } else if (instr.isDPSimd2Op()) { |
| decodeDPSimd2(instr); |
| } else { |
| unknown(instr); |
| } |
| } |
| |
| void print(String s) => _buffer.write(s); |
| void printInt(int val) => _buffer.write(val.toString()); |
| |
| void printRegister(int reg, R31Type r31t) { |
| if (reg == 31) { |
| print((r31t == R31Type.R31IsZR) ? "zr" : "csp"); |
| } else { |
| print(_cpuRegNames[reg]); |
| } |
| } |
| |
| void printVRegister(int reg) => print("v$reg"); |
| |
| void printShiftExtendRm(Instr instr) { |
| int rm = instr.rmField(); |
| Shift shift = Shift.values[instr.shiftTypeField()]; |
| int shiftAmount = instr.shiftAmountField(); |
| Extend extend = Extend.values[instr.extendTypeField()]; |
| int extendShiftAmount = instr.extShiftAmountField(); |
| |
| printRegister(rm, R31Type.R31IsZR); |
| |
| if (instr.isShift() && (shift == Shift.LSL) && (shiftAmount == 0)) { |
| return; |
| } |
| if (instr.isShift()) { |
| if ((shift == Shift.ROR) && (shiftAmount == 0)) { |
| print(" RRX"); |
| return; |
| } else if (((shift == Shift.LSR) || (shift == Shift.ASR)) && |
| (shiftAmount == 0)) { |
| shiftAmount = 32; |
| } |
| print(" "); |
| print(_shiftNames[shift.index]); |
| print(" #"); |
| printInt(shiftAmount); |
| } else { |
| assert(instr.isExtend()); |
| print(" "); |
| print(_extendNames[extend.index]); |
| if (((instr.sfField() == 1) && (extend == Extend.UXTX)) || |
| ((instr.sfField() == 0) && (extend == Extend.UXTW))) { |
| print(" "); |
| printInt(extendShiftAmount); |
| } |
| } |
| } |
| |
| void printMemOperand(Instr instr) { |
| final int rn = instr.rnField(); |
| if (instr.bit(24) == 1) { |
| final int scale = instr.szField(); |
| final int imm12 = instr.imm12Field(); |
| final int off = imm12 << scale; |
| print("["); |
| printRegister(rn, R31Type.R31IsSP); |
| if (off != 0) { |
| print(", #"); |
| printInt(off); |
| } |
| print("]"); |
| } else { |
| switch (instr.bits(10, 2)) { |
| case 0: |
| final int imm9 = instr.sImm9Field(); |
| print("["); |
| printRegister(rn, R31Type.R31IsSP); |
| print(", #"); |
| printInt(imm9); |
| print("]"); |
| break; |
| case 1: |
| final int imm9 = instr.sImm9Field(); |
| print("["); |
| printRegister(rn, R31Type.R31IsSP); |
| print("]"); |
| print(", #"); |
| printInt(imm9); |
| print(" !"); |
| break; |
| case 2: |
| final int rm = instr.rmField(); |
| final Extend ext = Extend.values[instr.extendTypeField()]; |
| final int s = instr.bit(12); |
| print("["); |
| printRegister(rn, R31Type.R31IsSP); |
| print(", "); |
| printRegister(rm, R31Type.R31IsZR); |
| print(" "); |
| print(_extendNames[ext.index]); |
| if (s == 1) print(" scaled"); |
| print("]"); |
| break; |
| case 3: |
| final int imm9 = instr.sImm9Field(); |
| print("["); |
| printRegister(rn, R31Type.R31IsSP); |
| print(", #"); |
| printInt(imm9); |
| print("]!"); |
| break; |
| default: |
| print("???"); |
| } |
| } |
| } |
| |
| void printPairMemOperand(Instr instr) { |
| final int rn = instr.rnField(); |
| final int simm7 = instr.sImm7Field(); |
| final int shift = (instr.bit(26) == 1) |
| ? 2 + instr.szField() |
| : 2 + instr.sfField(); |
| final int offset = simm7 << shift; |
| print("["); |
| printRegister(rn, R31Type.R31IsSP); |
| switch (instr.bits(23, 3)) { |
| case 1: |
| print("], #"); |
| printInt(offset); |
| print(" !"); |
| break; |
| case 2: |
| print(", #"); |
| printInt(offset); |
| print("]"); |
| break; |
| case 3: |
| print(", #"); |
| printInt(offset); |
| print("]!"); |
| break; |
| default: |
| print(", ???]"); |
| break; |
| } |
| } |
| |
| void printCondition(Instr instr) { |
| if (instr.isConditionalSelectOp()) { |
| print(_condNames[instr.selectConditionField()]); |
| } else { |
| print(_condNames[instr.conditionField()]); |
| } |
| } |
| |
| void printInvertedCondition(Instr instr) { |
| if (instr.isConditionalSelectOp()) { |
| print(_condNames[invertCondition(instr.selectConditionField())]); |
| } else { |
| print(_condNames[invertCondition(instr.conditionField())]); |
| } |
| } |
| |
| void format(Instr instr, String formatStr) { |
| var currentPos = 0; |
| while (currentPos < formatStr.length) { |
| final char = formatStr[currentPos]; |
| if (char == "'") { |
| var end = formatStr.indexOf(' ', currentPos); |
| if (end == -1) end = formatStr.length; |
| final option = formatStr.substring(currentPos + 1, end); |
| currentPos += formatOption(instr, option) + 1; |
| } else { |
| print(char); |
| currentPos++; |
| } |
| } |
| } |
| |
| int formatOption(Instr instr, String option) { |
| if (option.startsWith('r')) return formatRegister(instr, option); |
| if (option.startsWith('v')) return formatVRegister(instr, option); |
| |
| switch (option[0]) { |
| case 'b': |
| if (option.startsWith('bitimm')) { |
| var imm = instr.immLogical(); |
| if (instr.sfField() == 0) { |
| imm &= 0xffffffff; |
| } |
| print("0x"); |
| if (imm < 0) { |
| // Print in two halves to avoid printing value as signed "0x-123". |
| print((imm >>> 32).toRadixString(16)); |
| print((imm & 0xffffffff).toRadixString(16).padLeft(8, '0')); |
| } else { |
| print(imm.toRadixString(16)); |
| } |
| return 6; // 'bitimm'.length; |
| } else if (option.startsWith('bitpos')) { |
| int bitpos = instr.bits(19, 5) | (instr.bit(31) << 5); |
| print("#"); |
| printInt(bitpos); |
| return 6; |
| } |
| break; |
| case 'c': |
| if (option.startsWith('csz')) { |
| final imm5 = instr.bits(16, 5); |
| var typ = "??"; |
| if ((imm5 & 0x1) != 0) { |
| typ = "b"; |
| } else if ((imm5 & 0x2) != 0) { |
| typ = "h"; |
| } else if ((imm5 & 0x4) != 0) { |
| typ = "s"; |
| } else if ((imm5 & 0x8) != 0) { |
| typ = "d"; |
| } |
| print(typ); |
| return 3; |
| } else if (option.startsWith('condinverted')) { |
| printInvertedCondition(instr); |
| return 12; |
| } else if (option.startsWith('cond')) { |
| printCondition(instr); |
| return 4; |
| } |
| break; |
| case 'd': |
| { |
| int off; |
| if (option.startsWith('dest26')) { |
| off = instr.sImm26Field() << 2; |
| } else if (option.startsWith('dest19')) { |
| off = instr.sImm19Field() << 2; |
| } else if (option.startsWith('dest14')) { |
| off = instr.sImm14Field() << 2; |
| } else { |
| break; |
| } |
| print("${off >= 0 ? '+' : ''}$off"); |
| return 6; |
| } |
| case 'f': |
| if (option.startsWith('fsz')) { |
| int sz = instr.szField(); |
| print(switch (sz) { |
| 0 => (instr.bit(23) == 1) ? "q" : "b", |
| 1 => "h", |
| 2 => "s", |
| 3 => "d", |
| _ => "?", |
| }); |
| return 3; |
| } |
| break; |
| case 'h': |
| if (option.startsWith('hw')) { |
| final shift = instr.hwField() << 4; |
| if (shift != 0) print(" lsl $shift"); |
| return 2; |
| } |
| break; |
| case 'i': |
| if (option.startsWith('imm12s')) { |
| int imm = instr.imm12Field(); |
| if (instr.imm12ShiftField() == 1) imm <<= 12; |
| print("#0x${imm.toRadixString(16)}"); |
| return 6; |
| } else if (option.startsWith('imm12')) { |
| print("#0x${instr.imm12Field().toRadixString(16)}"); |
| return 5; |
| } else if (option.startsWith('imm16')) { |
| print("#0x${instr.imm16Field().toRadixString(16)}"); |
| return 5; |
| } else if (option.startsWith('immd')) { |
| final imm = Instr.vfpExpandImm(instr.imm8Field()); |
| final d = ByteData(8) |
| ..setInt64(0, imm, Endian.host) |
| ..getFloat64(0, Endian.host); |
| print(d.toString()); |
| return 4; |
| } else if (option.startsWith('immr')) { |
| print("#${instr.immRField()}"); |
| return 4; |
| } else if (option.startsWith('imms')) { |
| print("#${instr.immSField()}"); |
| return 4; |
| } |
| break; |
| case 'm': |
| if (option.startsWith('memop')) { |
| printMemOperand(instr); |
| return 5; |
| } |
| break; |
| case 'o': |
| if (option.startsWith('opc')) { |
| if (instr.bit(26) == 0) { |
| if (instr.bit(31) == 0) { |
| if (instr.bit(30) == 1) { |
| print("sw"); |
| } else { |
| print("w"); |
| } |
| } else { |
| // 64-bit width is most commonly used, no need to print "x". |
| } |
| } else { |
| switch (instr.bits(30, 2)) { |
| case 0: |
| print("s"); |
| break; |
| case 1: |
| print("d"); |
| break; |
| case 2: |
| print("q"); |
| break; |
| case 3: |
| print("?"); |
| break; |
| } |
| } |
| return 3; |
| } |
| break; |
| case 'p': |
| if (option.startsWith('pmemop')) { |
| printPairMemOperand(instr); |
| return 6; |
| } |
| break; |
| case 's': |
| if (option.startsWith('shift_op')) { |
| printShiftExtendRm(instr); |
| return 8; |
| } else if (option.startsWith('sf')) { |
| if (instr.sfField() == 0) print('w'); |
| return 2; |
| } else if (option.startsWith('sz')) { |
| final sz = instr.szField(); |
| print(switch (sz) { |
| 0 => "b", |
| 1 => "h", |
| 2 => "w", |
| 3 => "", |
| _ => "?", |
| }); |
| return 2; |
| } else { |
| if (instr.hasS()) print("s"); |
| return 1; |
| } |
| } |
| throw 'Unexpected format option $option'; |
| } |
| |
| int formatRegister(Instr instr, String format) { |
| assert(format[0] == 'r'); |
| switch (format[1]) { |
| case 'n': |
| printRegister(instr.rnField(), instr.rnMode()); |
| return 2; |
| case 'd': |
| printRegister(instr.rdField(), instr.rdMode()); |
| return 2; |
| case 'm': |
| printRegister(instr.rmField(), R31Type.R31IsZR); |
| return 2; |
| case 't': |
| if (format[2] == '2') { |
| printRegister(instr.rt2Field(), R31Type.R31IsZR); |
| return 3; |
| } |
| printRegister(instr.rtField(), R31Type.R31IsZR); |
| return 2; |
| case 'a': |
| printRegister(instr.raField(), R31Type.R31IsZR); |
| return 2; |
| case 's': |
| printRegister(instr.rsField(), R31Type.R31IsZR); |
| return 2; |
| } |
| throw 'Unexpected register format $format'; |
| } |
| |
| int formatVRegister(Instr instr, String format) { |
| assert(format[0] == 'v'); |
| switch (format[1]) { |
| case 'd': |
| printVRegister(instr.vdField()); |
| return 2; |
| case 'n': |
| printVRegister(instr.vnField()); |
| return 2; |
| case 'm': |
| printVRegister(instr.vmField()); |
| return 2; |
| case 't': |
| if (format[2] == '2') { |
| printVRegister(instr.vt2Field()); |
| return 3; |
| } |
| printVRegister(instr.vtField()); |
| return 2; |
| } |
| throw 'Unexpected v-register format $format'; |
| } |
| |
| void unknown(Instr instr) => print("unknown"); |
| |
| void decodeMoveWide(Instr instr) { |
| switch (instr.bits(29, 2)) { |
| case 0: |
| format(instr, "movn'sf 'rd, 'imm16'hw"); |
| break; |
| case 2: |
| format(instr, "movz'sf 'rd, 'imm16'hw"); |
| break; |
| case 3: |
| format(instr, "movk'sf 'rd, 'imm16'hw"); |
| break; |
| default: |
| unknown(instr); |
| } |
| } |
| |
| void decodeLoadStoreReg(Instr instr) { |
| if (instr.bit(26) == 1) { |
| // SIMD or FP |
| if (instr.bit(22) == 1) { |
| format(instr, "fldr'fsz 'vt, 'memop"); |
| } else { |
| format(instr, "fstr'fsz 'vt, 'memop"); |
| } |
| } else { |
| // Integer |
| if (instr.bits(22, 2) == 0) { |
| format(instr, "str'sz 'rt, 'memop"); |
| } else if (instr.bits(23, 1) == 1) { |
| format(instr, "ldrs'sz 'rt, 'memop"); |
| } else { |
| format(instr, "ldr'sz 'rt, 'memop"); |
| } |
| } |
| } |
| |
| void decodeDPImmediate(Instr instr) { |
| if (instr.isMoveWideOp()) { |
| decodeMoveWide(instr); |
| } else if (instr.isAddSubImmOp()) { |
| decodeAddSubImm(instr); |
| } else if (instr.isBitfieldOp()) { |
| decodeBitfield(instr); |
| } else if (instr.isLogicalImmOp()) { |
| decodeLogicalImm(instr); |
| } else if (instr.isPCRelOp()) { |
| decodePCRel(instr); |
| } else { |
| unknown(instr); |
| } |
| } |
| |
| void decodeCompareBranch(Instr instr) { |
| if (instr.isExceptionGenOp()) { |
| decodeExceptionGen(instr); |
| } else if (instr.isSystemOp()) { |
| decodeSystem(instr); |
| } else if (instr.isUnconditionalBranchRegOp()) { |
| decodeUnconditionalBranchReg(instr); |
| } else if (instr.isCompareAndBranchOp()) { |
| decodeCompareAndBranch(instr); |
| } else if (instr.isConditionalBranchOp()) { |
| decodeConditionalBranch(instr); |
| } else if (instr.isTestAndBranchOp()) { |
| decodeTestAndBranch(instr); |
| } else if (instr.isUnconditionalBranchOp()) { |
| decodeUnconditionalBranch(instr); |
| } else { |
| unknown(instr); |
| } |
| } |
| |
| void decodeLoadStore(Instr instr) { |
| if (instr.isAtomicMemoryOp()) { |
| decodeAtomicMemory(instr); |
| } else if (instr.isLoadStoreRegOp()) { |
| decodeLoadStoreReg(instr); |
| } else if (instr.isLoadStoreRegPairOp()) { |
| decodeLoadStoreRegPair(instr); |
| } else if (instr.isLoadRegLiteralOp()) { |
| decodeLoadRegLiteral(instr); |
| } else if (instr.isLoadStoreExclusiveOp()) { |
| decodeLoadStoreExclusive(instr); |
| } else { |
| unknown(instr); |
| } |
| } |
| |
| void decodeDPRegister(Instr instr) { |
| if (instr.isAddSubShiftExtOp()) { |
| decodeAddSubShiftExt(instr); |
| } else if (instr.isAddSubWithCarryOp()) { |
| decodeAddSubWithCarry(instr); |
| } else if (instr.isLogicalShiftOp()) { |
| decodeLogicalShift(instr); |
| } else if (instr.isMiscDP1SourceOp()) { |
| decodeMiscDP1Source(instr); |
| } else if (instr.isMiscDP2SourceOp()) { |
| decodeMiscDP2Source(instr); |
| } else if (instr.isMiscDP3SourceOp()) { |
| decodeMiscDP3Source(instr); |
| } else if (instr.isConditionalSelectOp()) { |
| decodeConditionalSelect(instr); |
| } else { |
| unknown(instr); |
| } |
| } |
| |
| void decodeDPSimd1(Instr instr) { |
| if (instr.isSIMDCopyOp()) { |
| decodeSIMDCopy(instr); |
| } else if (instr.isSIMDThreeSameOp()) { |
| decodeSIMDThreeSame(instr); |
| } else if (instr.isSIMDTwoRegOp()) { |
| decodeSIMDTwoReg(instr); |
| } else { |
| unknown(instr); |
| } |
| } |
| |
| void decodeDPSimd2(Instr instr) { |
| if (instr.isFPOp()) { |
| decodeFP(instr); |
| } else { |
| unknown(instr); |
| } |
| } |
| |
| void decodeAddSubImm(Instr instr) { |
| switch (instr.bit(30)) { |
| case 0: |
| { |
| if ((instr.rdField() == R31) && (instr.sField() == 1)) { |
| format(instr, "cmn'sf 'rn, 'imm12s"); |
| } else { |
| if (((instr.rdField() == R31) || (instr.rnField() == R31)) && |
| (instr.imm12Field() == 0) && |
| (instr.bit(29) == 0)) { |
| format(instr, "mov'sf 'rd, 'rn"); |
| } else { |
| format(instr, "add'sf's 'rd, 'rn, 'imm12s"); |
| } |
| } |
| break; |
| } |
| case 1: |
| { |
| if ((instr.rdField() == R31) && (instr.sField() == 1)) { |
| format(instr, "cmp'sf 'rn, 'imm12s"); |
| } else { |
| format(instr, "sub'sf's 'rd, 'rn, 'imm12s"); |
| } |
| break; |
| } |
| default: |
| unknown(instr); |
| break; |
| } |
| } |
| |
| void decodeBitfield(Instr instr) { |
| var regSize = instr.sfField() == 0 ? 32 : 64; |
| int op = instr.bits(29, 2); |
| int rImm = instr.immRField(); |
| int sImm = instr.immSField(); |
| switch (op) { |
| case 0: |
| if (rImm == 0) { |
| if (sImm == 7) { |
| format(instr, "sxtb 'rd, 'rn"); |
| return; |
| } else if (sImm == 15) { |
| format(instr, "sxth 'rd, 'rn"); |
| return; |
| } else if (sImm == 31) { |
| format(instr, "sxtw 'rd, 'rn"); |
| return; |
| } |
| } |
| if (sImm == (regSize - 1)) { |
| format(instr, "asr'sf 'rd, 'rn, 'immr"); |
| return; |
| } |
| format(instr, "sbfm'sf 'rd, 'rn, 'immr, 'imms"); |
| break; |
| case 1: |
| format(instr, "bfm'sf 'rd, 'rn, 'immr, 'imms"); |
| break; |
| case 2: |
| if (rImm == 0) { |
| if (sImm == 7) { |
| format(instr, "uxtb 'rd, 'rn"); |
| return; |
| } else if (sImm == 15) { |
| format(instr, "uxth 'rd, 'rn"); |
| return; |
| } |
| } |
| if ((sImm != (regSize - 1)) && ((sImm + 1) == rImm)) { |
| int shift = regSize - sImm - 1; |
| format(instr, "lsl'sf 'rd, 'rn, #"); |
| printInt(shift); |
| return; |
| } else if (sImm == (regSize - 1)) { |
| format(instr, "lsr'sf 'rd, 'rn, 'immr"); |
| return; |
| } |
| format(instr, "ubfm'sf 'rd, 'rn, 'immr, 'imms"); |
| break; |
| default: |
| unknown(instr); |
| } |
| } |
| |
| void decodeLogicalImm(Instr instr) { |
| int op = instr.bits(29, 2); |
| switch (op) { |
| case 0: |
| format(instr, "and'sf 'rd, 'rn, 'bitimm"); |
| break; |
| case 1: |
| if (instr.rnField() == R31) { |
| format(instr, "mov'sf 'rd, 'bitimm"); |
| } else { |
| format(instr, "orr'sf 'rd, 'rn, 'bitimm"); |
| } |
| break; |
| case 2: |
| format(instr, "eor'sf 'rd, 'rn, 'bitimm"); |
| break; |
| case 3: |
| if (instr.rdField() == R31) { |
| format(instr, "tst'sf 'rn, 'bitimm"); |
| } else { |
| format(instr, "and'sfs 'rd, 'rn, 'bitimm"); |
| } |
| break; |
| default: |
| unknown(instr); |
| } |
| } |
| |
| void decodePCRel(Instr instr) { |
| final op = instr.bit(31); |
| if (op == 0) { |
| format(instr, "adr 'rd, 'pcadr"); |
| } else { |
| unknown(instr); |
| } |
| } |
| |
| void decodeExceptionGen(Instr instr) { |
| if ((instr.bits(0, 2) == 1) && |
| (instr.bits(2, 3) == 0) && |
| (instr.bits(21, 3) == 0)) { |
| format(instr, "svc 'imm16"); |
| } else if ((instr.bits(0, 2) == 0) && |
| (instr.bits(2, 3) == 0) && |
| (instr.bits(21, 3) == 1)) { |
| format(instr, "brk 'imm16"); |
| } else if ((instr.bits(0, 2) == 0) && |
| (instr.bits(2, 3) == 0) && |
| (instr.bits(21, 3) == 2)) { |
| format(instr, "hlt 'imm16"); |
| } else { |
| unknown(instr); |
| } |
| } |
| |
| void decodeSystem(Instr instr) { |
| if (instr.value == SystemOp.CLREX) { |
| format(instr, "clrex"); |
| return; |
| } |
| if (instr.value == kDMB_ISH) { |
| format(instr, "dmb ish"); |
| return; |
| } |
| if (instr.value == kDMB_ISHST) { |
| format(instr, "dmb ishst"); |
| return; |
| } |
| if ((instr.bits(0, 8) == 0x1f) && |
| (instr.bits(12, 4) == 2) && |
| (instr.bits(16, 3) == 3) && |
| (instr.bits(19, 2) == 0) && |
| (instr.bit(21) == 0)) { |
| if (instr.bits(8, 4) == 0) { |
| format(instr, "nop"); |
| } else { |
| unknown(instr); |
| } |
| } else { |
| unknown(instr); |
| } |
| } |
| |
| void decodeUnconditionalBranchReg(Instr instr) { |
| if ((instr.bits(0, 5) == 0) && |
| (instr.bits(10, 5) == 0) && |
| (instr.bits(16, 5) == 0x1f)) { |
| switch (instr.bits(21, 4)) { |
| case 0: |
| format(instr, "br 'rn"); |
| break; |
| case 1: |
| format(instr, "blr 'rn"); |
| break; |
| case 2: |
| if (instr.rnField() == LINK_REGISTER) { |
| format(instr, "ret"); |
| } else { |
| format(instr, "ret 'rn"); |
| } |
| break; |
| default: |
| unknown(instr); |
| } |
| } |
| } |
| |
| void decodeCompareAndBranch(Instr instr) { |
| final op = instr.bit(24); |
| if (op == 0) { |
| format(instr, "cbz'sf 'rt, 'dest19"); |
| } else { |
| format(instr, "cbnz'sf 'rt, 'dest19"); |
| } |
| } |
| |
| void decodeConditionalBranch(Instr instr) { |
| if ((instr.bit(24) != 0) || (instr.bit(4) != 0)) { |
| unknown(instr); |
| return; |
| } |
| format(instr, "b'cond 'dest19"); |
| } |
| |
| void decodeTestAndBranch(Instr instr) { |
| final op = instr.bit(24); |
| if (op == 0) { |
| format(instr, "tbz'sf 'rt, 'bitpos, 'dest14"); |
| } else { |
| format(instr, "tbnz'sf 'rt, 'bitpos, 'dest14"); |
| } |
| } |
| |
| void decodeUnconditionalBranch(Instr instr) { |
| final op = instr.bit(31); |
| if (op == 0) { |
| format(instr, "b 'dest26"); |
| } else { |
| format(instr, "bl 'dest26"); |
| } |
| } |
| |
| void decodeAtomicMemory(Instr instr) { |
| switch (instr.bits(12, 3)) { |
| case 1: |
| format(instr, "ldclr'sz 'rs, 'rt, ['rn]"); |
| break; |
| case 3: |
| format(instr, "ldset'sz 'rs, 'rt, ['rn]"); |
| break; |
| default: |
| unknown(instr); |
| } |
| } |
| |
| void decodeLoadStoreRegPair(Instr instr) { |
| if (instr.bit(26) == 1) { |
| // SIMD or FP |
| if (instr.bit(22) == 1) { |
| format(instr, "fldp'opc 'vt, 'vt2, 'pmemop"); |
| } else { |
| format(instr, "fstp'opc 'vt, 'vt2, 'pmemop"); |
| } |
| } else { |
| // Integer |
| if (instr.bit(22) == 1) { |
| format(instr, "ldp'opc 'rt, 'rt2, 'pmemop"); |
| } else { |
| format(instr, "stp'opc 'rt, 'rt2, 'pmemop"); |
| } |
| } |
| } |
| |
| void decodeLoadRegLiteral(Instr instr) { |
| if ((instr.bit(31) != 0) || |
| (instr.bit(29) != 0) || |
| (instr.bits(24, 3) != 0)) { |
| unknown(instr); |
| } |
| if (instr.bit(30) != 0) { |
| format(instr, "ldrx 'rt, 'pcldr"); |
| } else { |
| format(instr, "ldrw 'rt, 'pcldr"); |
| } |
| } |
| |
| void decodeLoadStoreExclusive(Instr instr) { |
| if (instr.bit(31) != 1 || |
| instr.bit(21) != 0 || |
| instr.bit(23) != instr.bit(15)) { |
| unknown(instr); |
| } |
| final isLoad = instr.bit(22) == 1; |
| final isExclusive = instr.bit(23) == 0; |
| final isOrdered = instr.bit(15) == 1; |
| if (isLoad) { |
| final isLoadAcquire = !isExclusive && isOrdered; |
| if (isLoadAcquire) { |
| format(instr, "ldar'sz 'rt, ['rn]"); |
| } else { |
| format(instr, "ldxr'sz 'rt, ['rn]"); |
| } |
| } else { |
| final isStoreRelease = !isExclusive && isOrdered; |
| if (isStoreRelease) { |
| format(instr, "stlr'sz 'rt, ['rn]"); |
| } else { |
| format(instr, "stxr'sz 'rs, 'rt, ['rn]"); |
| } |
| } |
| } |
| |
| void decodeAddSubShiftExt(Instr instr) { |
| switch (instr.bit(30)) { |
| case 0: |
| if ((instr.rdField() == R31) && (instr.sField() == 1)) { |
| format(instr, "cmn'sf 'rn, 'shift_op"); |
| } else { |
| format(instr, "add'sf's 'rd, 'rn, 'shift_op"); |
| } |
| break; |
| case 1: |
| if ((instr.rdField() == R31) && (instr.sField() == 1)) { |
| format(instr, "cmp'sf 'rn, 'shift_op"); |
| } else { |
| if (instr.rnField() == R31) { |
| format(instr, "neg'sf's 'rd, 'shift_op"); |
| } else { |
| format(instr, "sub'sf's 'rd, 'rn, 'shift_op"); |
| } |
| } |
| break; |
| default: |
| // unreachable |
| break; |
| } |
| } |
| |
| void decodeAddSubWithCarry(Instr instr) { |
| switch (instr.bit(30)) { |
| case 0: |
| format(instr, "adc'sf's 'rd, 'rn, 'rm"); |
| break; |
| case 1: |
| format(instr, "sbc'sf's 'rd, 'rn, 'rm"); |
| break; |
| default: // unreachable |
| } |
| } |
| |
| void decodeLogicalShift(Instr instr) { |
| final op = (instr.bits(29, 2) << 1) | instr.bit(21); |
| switch (op) { |
| case 0: |
| format(instr, "and'sf 'rd, 'rn, 'shift_op"); |
| break; |
| case 1: |
| format(instr, "bic'sf 'rd, 'rn, 'shift_op"); |
| break; |
| case 2: |
| if ((instr.rnField() == R31) && |
| (instr.isShift()) && |
| (instr.shiftTypeField() == Shift.LSL.index)) { |
| if (instr.shiftAmountField() == 0) { |
| format(instr, "mov'sf 'rd, 'rm"); |
| } else { |
| format(instr, "lsl'sf 'rd, 'rm, 'imms"); |
| } |
| } else { |
| format(instr, "orr'sf 'rd, 'rn, 'shift_op"); |
| } |
| break; |
| case 3: |
| format(instr, "orn'sf 'rd, 'rn, 'shift_op"); |
| break; |
| case 4: |
| format(instr, "eor'sf 'rd, 'rn, 'shift_op"); |
| break; |
| case 5: |
| format(instr, "eon'sf 'rd, 'rn, 'shift_op"); |
| break; |
| case 6: |
| if (instr.rdField() == R31) { |
| format(instr, "tst'sf 'rn, 'shift_op"); |
| } else { |
| format(instr, "and'sfs 'rd, 'rn, 'shift_op"); |
| } |
| break; |
| case 7: |
| format(instr, "bic'sfs 'rd, 'rn, 'shift_op"); |
| break; |
| default: // unreachable |
| } |
| } |
| |
| void decodeMiscDP1Source(Instr instr) { |
| if (instr.bit(29) != 0) { |
| unknown(instr); |
| return; |
| } |
| final op = instr.bits(10, 10); |
| switch (op) { |
| case 0: |
| format(instr, "rbit'sf 'rd, 'rn"); |
| break; |
| case 4: |
| format(instr, "clz'sf 'rd, 'rn"); |
| break; |
| default: |
| unknown(instr); |
| } |
| } |
| |
| void decodeMiscDP2Source(Instr instr) { |
| if (instr.bit(29) != 0) { |
| unknown(instr); |
| return; |
| } |
| final op = instr.bits(10, 5); |
| switch (op) { |
| case 2: |
| format(instr, "udiv'sf 'rd, 'rn, 'rm"); |
| break; |
| case 3: |
| format(instr, "sdiv'sf 'rd, 'rn, 'rm"); |
| break; |
| case 8: |
| format(instr, "lsl'sf 'rd, 'rn, 'rm"); |
| break; |
| case 9: |
| format(instr, "lsr'sf 'rd, 'rn, 'rm"); |
| break; |
| case 10: |
| format(instr, "asr'sf 'rd, 'rn, 'rm"); |
| break; |
| default: |
| unknown(instr); |
| } |
| } |
| |
| void decodeMiscDP3Source(Instr instr) { |
| var zeroOperand = instr.raField() == R31; |
| int maskedBits = |
| instr.value & ~(0x1F << 5 | 0x1F << 16 | 0x1F << 10 | 0x1F << 0); |
| |
| if (maskedBits == MiscDP3SourceOp.MADD || |
| maskedBits == MiscDP3SourceOp.MADDW) { |
| if (zeroOperand) { |
| format(instr, "mul'sf 'rd, 'rn, 'rm"); |
| } else { |
| format(instr, "madd'sf 'rd, 'rn, 'rm, 'ra"); |
| } |
| } else if (maskedBits == MiscDP3SourceOp.MSUB || |
| maskedBits == MiscDP3SourceOp.MSUBW) { |
| if (zeroOperand) { |
| format(instr, "mneg'sf 'rd, 'rn, 'rm"); |
| } else { |
| format(instr, "msub'sf 'rd, 'rn, 'rm, 'ra"); |
| } |
| } else if (maskedBits == MiscDP3SourceOp.SMULH) { |
| format(instr, "smulh 'rd, 'rn, 'rm"); |
| } else if (maskedBits == MiscDP3SourceOp.UMULH) { |
| format(instr, "umulh 'rd, 'rn, 'rm"); |
| } else if (maskedBits == MiscDP3SourceOp.UMADDL) { |
| if (zeroOperand) { |
| format(instr, "umull 'rd, 'rn, 'rm"); |
| } else { |
| format(instr, "umaddl 'rd, 'rn, 'rm, 'ra"); |
| } |
| } else if (maskedBits == MiscDP3SourceOp.SMADDL) { |
| if (zeroOperand) { |
| format(instr, "smull 'rd, 'rn, 'rm"); |
| } else { |
| format(instr, "smaddl 'rd, 'rn, 'rm, 'ra"); |
| } |
| } else if (maskedBits == MiscDP3SourceOp.SMSUBL) { |
| if (zeroOperand) { |
| format(instr, "smnegl 'rd, 'rn, 'rm"); |
| } else { |
| format(instr, "smsubl 'rd, 'rn, 'rm, 'ra"); |
| } |
| } else if (maskedBits == MiscDP3SourceOp.UMSUBL) { |
| if (zeroOperand) { |
| format(instr, "umnegl 'rd, 'rn, 'rm"); |
| } else { |
| format(instr, "umsubl 'rd, 'rn, 'rm, 'ra"); |
| } |
| } else { |
| unknown(instr); |
| } |
| } |
| |
| void decodeConditionalSelect(Instr instr) { |
| int cond = instr.selectConditionField(); |
| bool nonSelect = |
| (instr.rnField() == instr.rmField()) && ((cond & 0xe) != 0xe); |
| if ((instr.bits(29, 2) == 0) && (instr.bits(10, 2) == 0)) { |
| format(instr, "csel'sf 'rd, 'rn, 'rm, 'cond"); |
| } else if ((instr.bits(29, 2) == 0) && (instr.bits(10, 2) == 1)) { |
| if (nonSelect) { |
| if (instr.rnField() == 31 && instr.rmField() == 31) { |
| format(instr, "cset'sf 'rd, 'condinverted"); |
| } else { |
| format(instr, "cinc'sf 'rd, 'rn, 'condinverted"); |
| } |
| } else { |
| format(instr, "csinc'sf 'rd, 'rn, 'rm, 'cond"); |
| } |
| } else if ((instr.bits(29, 2) == 2) && (instr.bits(10, 2) == 0)) { |
| if (nonSelect) { |
| if (instr.rnField() == 31 && instr.rmField() == 31) { |
| format(instr, "csetm'sf 'rd, 'condinverted"); |
| } else { |
| format(instr, "cinv'sf 'rd, 'rn, 'condinverted"); |
| } |
| } else { |
| format(instr, "csinv'sf 'rd, 'rn, 'rm, 'cond"); |
| } |
| } else if ((instr.bits(29, 2) == 2) && (instr.bits(10, 2) == 1)) { |
| if (nonSelect) { |
| format(instr, "cneg'sf 'rd, 'rn, 'condinverted"); |
| } else { |
| format(instr, "csneg'sf 'rd, 'rn, 'rm, 'cond"); |
| } |
| } else { |
| unknown(instr); |
| } |
| } |
| |
| void decodeSIMDCopy(Instr instr) => unknown(instr); |
| void decodeSIMDThreeSame(Instr instr) => unknown(instr); |
| void decodeSIMDTwoReg(Instr instr) => unknown(instr); |
| |
| void decodeFP(Instr instr) { |
| if (instr.isFPImmOp()) { |
| decodeFPImm(instr); |
| } else if (instr.isFPIntCvtOp()) { |
| decodeFPIntCvt(instr); |
| } else if (instr.isFPOneSourceOp()) { |
| decodeFPOneSource(instr); |
| } else if (instr.isFPTwoSourceOp()) { |
| decodeFPTwoSource(instr); |
| } else if (instr.isFPCompareOp()) { |
| decodeFPCompare(instr); |
| } else { |
| unknown(instr); |
| } |
| } |
| |
| void decodeFPImm(Instr instr) { |
| if ((instr.bit(31) != 0) || |
| (instr.bit(29) != 0) || |
| (instr.bit(23) != 0) || |
| (instr.bits(5, 5) != 0)) { |
| unknown(instr); |
| return; |
| } |
| if (instr.bit(22) == 1) { |
| // Double. |
| format(instr, "fmovd 'vd, 'immd"); |
| } else { |
| // Single. |
| unknown(instr); |
| } |
| } |
| |
| void decodeFPIntCvt(Instr instr) { |
| if (instr.bit(29) != 0) { |
| unknown(instr); |
| return; |
| } |
| |
| if ((instr.sfField() == 0) && (instr.bits(22, 2) == 0)) { |
| if (instr.bits(16, 5) == 6) { |
| format(instr, "fmovrs'sf 'rd, 'vn"); |
| } else if (instr.bits(16, 5) == 7) { |
| format(instr, "fmovsr'sf 'vd, 'rn"); |
| } else { |
| unknown(instr); |
| } |
| } else if (instr.bits(22, 2) == 1) { |
| if (instr.bits(16, 5) == 2) { |
| format(instr, "scvtfd'sf 'vd, 'rn"); |
| } else if (instr.bits(16, 5) == 6) { |
| format(instr, "fmovrd'sf 'rd, 'vn"); |
| } else if (instr.bits(16, 5) == 7) { |
| format(instr, "fmovdr'sf 'vd, 'rn"); |
| } else if (instr.bits(16, 5) == 8) { |
| format(instr, "fcvtps'sf 'rd, 'vn"); |
| } else if (instr.bits(16, 5) == 16) { |
| format(instr, "fcvtms'sf 'rd, 'vn"); |
| } else if (instr.bits(16, 5) == 24) { |
| format(instr, "fcvtzs'sf 'rd, 'vn"); |
| } else { |
| unknown(instr); |
| } |
| } else { |
| unknown(instr); |
| } |
| } |
| |
| void decodeFPOneSource(Instr instr) { |
| final opc = instr.bits(15, 6); |
| |
| if ((opc != 5) && (instr.bit(22) != 1)) { |
| // Source is interpreted as single-precision only if we're doing a |
| // conversion from single -> double. |
| unknown(instr); |
| return; |
| } |
| |
| switch (opc) { |
| case 0: |
| format(instr, "fmovdd 'vd, 'vn"); |
| break; |
| case 1: |
| format(instr, "fabsd 'vd, 'vn"); |
| break; |
| case 2: |
| format(instr, "fnegd 'vd, 'vn"); |
| break; |
| case 3: |
| format(instr, "fsqrtd 'vd, 'vn"); |
| break; |
| case 4: |
| format(instr, "fcvtsd 'vd, 'vn"); |
| break; |
| case 5: |
| format(instr, "fcvtds 'vd, 'vn"); |
| break; |
| default: |
| unknown(instr); |
| } |
| } |
| |
| void decodeFPTwoSource(Instr instr) { |
| if (instr.bits(22, 2) != 1) { |
| unknown(instr); |
| return; |
| } |
| final opc = instr.bits(12, 4); |
| |
| switch (opc) { |
| case 0: |
| format(instr, "fmuld 'vd, 'vn, 'vm"); |
| break; |
| case 1: |
| format(instr, "fdivd 'vd, 'vn, 'vm"); |
| break; |
| case 2: |
| format(instr, "faddd 'vd, 'vn, 'vm"); |
| break; |
| case 3: |
| format(instr, "fsubd 'vd, 'vn, 'vm"); |
| break; |
| default: |
| unknown(instr); |
| } |
| } |
| |
| void decodeFPCompare(Instr instr) { |
| if ((instr.bit(22) == 1) && (instr.bits(3, 2) == 0)) { |
| format(instr, "fcmpd 'vn, 'vm"); |
| } else if ((instr.bit(22) == 1) && (instr.bits(3, 2) == 1)) { |
| if (instr.vmField() == V0) { |
| format(instr, "fcmpd 'vn, #0.0"); |
| } else { |
| unknown(instr); |
| } |
| } else { |
| unknown(instr); |
| } |
| } |
| } |
| |
| class Disassembler { |
| static String decodeInstruction(int instruction) { |
| final buffer = StringBuffer(); |
| final decoder = ARM64Decoder(buffer); |
| decoder.instructionDecode(Instr(instruction)); |
| return buffer.toString(); |
| } |
| |
| static String decodeInstructions(Uint32List instructions) { |
| final buffer = StringBuffer(); |
| final decoder = ARM64Decoder(buffer); |
| for (final instruction in instructions) { |
| decoder.instructionDecode(Instr(instruction)); |
| buffer.writeln(); |
| } |
| return buffer.toString(); |
| } |
| } |