// Copyright (c) 2014, 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. #ifndef RUNTIME_VM_CONSTANTS_ARM64_H_ #define RUNTIME_VM_CONSTANTS_ARM64_H_ #ifndef RUNTIME_VM_CONSTANTS_H_ #error Do not include constants_arm64.h directly; use constants.h instead. #endif #include "platform/assert.h" namespace arch_arm64 { enum Register { R0 = 0, R1 = 1, R2 = 2, R3 = 3, R4 = 4, R5 = 5, R6 = 6, R7 = 7, R8 = 8, R9 = 9, R10 = 10, R11 = 11, R12 = 12, R13 = 13, R14 = 14, R15 = 15, // SP in Dart code. R16 = 16, // IP0 aka TMP R17 = 17, // IP1 aka TMP2 R18 = 18, // "platform register" on iOS. R19 = 19, R20 = 20, R21 = 21, R22 = 22, R23 = 23, R24 = 24, R25 = 25, R26 = 26, // THR R27 = 27, // PP R28 = 28, // BARRIER_MASK R29 = 29, // FP R30 = 30, // LR R31 = 31, // ZR, CSP kNumberOfCpuRegisters = 32, kNoRegister = -1, // These registers both use the encoding R31, but to avoid mistakes we give // them different values, and then translate before encoding. CSP = 32, ZR = 33, // Aliases. IP0 = R16, IP1 = R17, SP = R15, FP = R29, LR = R30, }; enum VRegister { V0 = 0, V1 = 1, V2 = 2, V3 = 3, V4 = 4, V5 = 5, V6 = 6, V7 = 7, V8 = 8, V9 = 9, V10 = 10, V11 = 11, V12 = 12, V13 = 13, V14 = 14, V15 = 15, V16 = 16, V17 = 17, V18 = 18, V19 = 19, V20 = 20, V21 = 21, V22 = 22, V23 = 24, V24 = 24, V25 = 25, V26 = 26, V27 = 27, V28 = 28, V29 = 29, V30 = 30, V31 = 31, kNumberOfVRegisters = 32, kNoVRegister = -1, }; // Register alias for floating point scratch register. const VRegister VTMP = V31; // Architecture independent aliases. typedef VRegister FpuRegister; const FpuRegister FpuTMP = VTMP; const int kNumberOfFpuRegisters = kNumberOfVRegisters; const FpuRegister kNoFpuRegister = kNoVRegister; extern const char* cpu_reg_names[kNumberOfCpuRegisters]; extern const char* fpu_reg_names[kNumberOfFpuRegisters]; // Register aliases. const Register TMP = R16; // Used as scratch register by assembler. const Register TMP2 = R17; const Register PP = R27; // Caches object pool pointer in generated code. const Register CODE_REG = R24; const Register FPREG = FP; // Frame pointer register. const Register SPREG = R15; // Stack pointer register. const Register LRREG = LR; // Link register. const Register ARGS_DESC_REG = R4; // Arguments descriptor register. const Register THR = R26; // Caches current thread in generated code. const Register CALLEE_SAVED_TEMP = R19; const Register CALLEE_SAVED_TEMP2 = R20; const Register BARRIER_MASK = R28; // ABI for catch-clause entry point. const Register kExceptionObjectReg = R0; const Register kStackTraceObjectReg = R1; // ABI for write barrier stub. const Register kWriteBarrierObjectReg = R1; const Register kWriteBarrierValueReg = R0; const Register kWriteBarrierSlotReg = R25; // Masks, sizes, etc. const int kXRegSizeInBits = 64; const int kWRegSizeInBits = 32; const int64_t kXRegMask = 0xffffffffffffffffL; const int64_t kWRegMask = 0x00000000ffffffffL; // List of registers used in load/store multiple. typedef uint32_t RegList; const RegList kAllCpuRegistersList = 0xFFFFFFFF; // See "Procedure Call Standard for the ARM 64-bit Architecture", document // number "ARM IHI 0055B", May 22 2013. // C++ ABI call registers. const RegList kAbiArgumentCpuRegs = (1 << R0) | (1 << R1) | (1 << R2) | (1 << R3) | (1 << R4) | (1 << R5) | (1 << R6) | (1 << R7); const RegList kAbiPreservedCpuRegs = (1 << R19) | (1 << R20) | (1 << R21) | (1 << R22) | (1 << R23) | (1 << R24) | (1 << R25) | (1 << R26) | (1 << R27) | (1 << R28); const Register kAbiFirstPreservedCpuReg = R19; const Register kAbiLastPreservedCpuReg = R28; const int kAbiPreservedCpuRegCount = 10; const VRegister kAbiFirstPreservedFpuReg = V8; const VRegister kAbiLastPreservedFpuReg = V15; const int kAbiPreservedFpuRegCount = 8; const intptr_t kReservedCpuRegisters = (1 << SPREG) | // Dart SP (1 << FPREG) | (1 << TMP) | (1 << TMP2) | (1 << PP) | (1 << THR) | (1 << LR) | (1 << BARRIER_MASK) | (1 << R31) | // C++ SP (1 << R18); // iOS platform register. // TODO(rmacnak): Only reserve on Mac & iOS. constexpr intptr_t kNumberOfReservedCpuRegisters = 10; // CPU registers available to Dart allocator. const RegList kDartAvailableCpuRegs = kAllCpuRegistersList & ~kReservedCpuRegisters; constexpr int kNumberOfDartAvailableCpuRegs = kNumberOfCpuRegisters - kNumberOfReservedCpuRegisters; // Registers available to Dart that are not preserved by runtime calls. const RegList kDartVolatileCpuRegs = kDartAvailableCpuRegs & ~kAbiPreservedCpuRegs; const Register kDartFirstVolatileCpuReg = R0; const Register kDartLastVolatileCpuReg = R14; const int kDartVolatileCpuRegCount = 15; const int kDartVolatileFpuRegCount = 24; constexpr int kStoreBufferWrapperSize = 32; #define R(REG) (1 << REG) class CallingConventions { public: static const intptr_t kArgumentRegisters = kAbiArgumentCpuRegs; static const Register ArgumentRegisters[]; static const intptr_t kNumArgRegs = 8; static const FpuRegister FpuArgumentRegisters[]; static const intptr_t kFpuArgumentRegisters = R(V0) | R(V1) | R(V2) | R(V3) | R(V4) | R(V5) | R(V6) | R(V7); static const intptr_t kNumFpuArgRegs = 8; static const bool kArgumentIntRegXorFpuReg = false; // Whether floating-point values should be passed as integers ("softfp" vs // "hardfp"). static constexpr bool kAbiSoftFP = false; // Whether 64-bit arguments must be aligned to an even register or 8-byte // stack address. Not relevant on X64 since the word size is 64-bits already. static constexpr bool kAlignArguments = false; static constexpr Register kReturnReg = R0; static constexpr Register kSecondReturnReg = kNoRegister; static constexpr FpuRegister kReturnFpuReg = V0; static constexpr Register kFirstCalleeSavedCpuReg = kAbiFirstPreservedCpuReg; static constexpr Register kFirstNonArgumentRegister = R8; static constexpr Register kSecondNonArgumentRegister = R9; static constexpr Register kStackPointerRegister = SPREG; }; #undef R static inline Register ConcreteRegister(Register r) { return ((r == ZR) || (r == CSP)) ? R31 : r; } // Values for the condition field as defined in section A3.2. enum Condition { kNoCondition = -1, EQ = 0, // equal NE = 1, // not equal CS = 2, // carry set/unsigned higher or same CC = 3, // carry clear/unsigned lower MI = 4, // minus/negative PL = 5, // plus/positive or zero VS = 6, // overflow VC = 7, // no overflow HI = 8, // unsigned higher LS = 9, // unsigned lower or same GE = 10, // signed greater than or equal LT = 11, // signed less than GT = 12, // signed greater than LE = 13, // signed less than or equal AL = 14, // always (unconditional) NV = 15, // special condition (refer to section C1.2.3) kNumberOfConditions = 16, // Platform-independent variants declared for all platforms EQUAL = EQ, ZERO = EQUAL, NOT_EQUAL = NE, NOT_ZERO = NOT_EQUAL, LESS = LT, LESS_EQUAL = LE, GREATER_EQUAL = GE, GREATER = GT, UNSIGNED_LESS = CC, UNSIGNED_LESS_EQUAL = LS, UNSIGNED_GREATER = HI, UNSIGNED_GREATER_EQUAL = CS, kInvalidCondition = 16 }; static inline Condition InvertCondition(Condition c) { const int32_t i = static_cast(c) ^ 0x1; return static_cast(i); } enum Bits { B0 = (1 << 0), B1 = (1 << 1), B2 = (1 << 2), B3 = (1 << 3), B4 = (1 << 4), B5 = (1 << 5), B6 = (1 << 6), B7 = (1 << 7), B8 = (1 << 8), B9 = (1 << 9), B10 = (1 << 10), B11 = (1 << 11), B12 = (1 << 12), B13 = (1 << 13), B14 = (1 << 14), B15 = (1 << 15), B16 = (1 << 16), B17 = (1 << 17), B18 = (1 << 18), B19 = (1 << 19), B20 = (1 << 20), B21 = (1 << 21), B22 = (1 << 22), B23 = (1 << 23), B24 = (1 << 24), B25 = (1 << 25), B26 = (1 << 26), B27 = (1 << 27), B28 = (1 << 28), B29 = (1 << 29), B30 = (1 << 30), B31 = (1 << 31), }; enum OperandSize { kByte, kUnsignedByte, kHalfword, kUnsignedHalfword, kWord, kUnsignedWord, kDoubleWord, kSWord, kDWord, kQWord, }; static inline int Log2OperandSizeBytes(OperandSize os) { switch (os) { case kByte: case kUnsignedByte: return 0; case kHalfword: case kUnsignedHalfword: return 1; case kWord: case kUnsignedWord: case kSWord: return 2; case kDoubleWord: case kDWord: return 3; case kQWord: return 4; default: UNREACHABLE(); break; } return -1; } static inline bool IsSignedOperand(OperandSize os) { switch (os) { case kByte: case kHalfword: case kWord: return true; case kUnsignedByte: case kUnsignedHalfword: case kUnsignedWord: case kDoubleWord: case kSWord: case kDWord: case kQWord: return false; default: UNREACHABLE(); break; } return false; } // Opcodes from C3 // C3.1. enum MainOp { DPImmediateMask = 0x1c000000, DPImmediateFixed = B28, CompareBranchMask = 0x1c000000, CompareBranchFixed = B28 | B26, LoadStoreMask = B27 | B25, LoadStoreFixed = B27, DPRegisterMask = 0x0e000000, DPRegisterFixed = B27 | B25, DPSimd1Mask = 0x1e000000, DPSimd1Fixed = B27 | B26 | B25, DPSimd2Mask = 0x1e000000, DPSimd2Fixed = B28 | DPSimd1Fixed, FPMask = 0x5e000000, FPFixed = B28 | B27 | B26 | B25, }; // C3.2.1 enum CompareAndBranchOp { CompareAndBranchMask = 0x7e000000, CompareAndBranchFixed = CompareBranchFixed | B29, CBZ = CompareAndBranchFixed, CBNZ = CompareAndBranchFixed | B24, }; // C.3.2.2 enum ConditionalBranchOp { ConditionalBranchMask = 0xfe000000, ConditionalBranchFixed = CompareBranchFixed | B30, BCOND = ConditionalBranchFixed, }; // C3.2.3 enum ExceptionGenOp { ExceptionGenMask = 0xff000000, ExceptionGenFixed = CompareBranchFixed | B31 | B30, SVC = ExceptionGenFixed | B0, BRK = ExceptionGenFixed | B21, HLT = ExceptionGenFixed | B22, }; // C3.2.4 enum SystemOp { SystemMask = 0xffc00000, SystemFixed = CompareBranchFixed | B31 | B30 | B24, HINT = SystemFixed | B17 | B16 | B13 | B4 | B3 | B2 | B1 | B0, CLREX = SystemFixed | B17 | B16 | B13 | B12 | B11 | B10 | B9 | B8 | B6 | B4 | B3 | B2 | B1 | B0, }; // C3.2.5 enum TestAndBranchOp { TestAndBranchMask = 0x7e000000, TestAndBranchFixed = CompareBranchFixed | B29 | B25, TBZ = TestAndBranchFixed, TBNZ = TestAndBranchFixed | B24, }; // C3.2.6 enum UnconditionalBranchOp { UnconditionalBranchMask = 0x7c000000, UnconditionalBranchFixed = CompareBranchFixed, B = UnconditionalBranchFixed, BL = UnconditionalBranchFixed | B31, }; // C3.2.7 enum UnconditionalBranchRegOp { UnconditionalBranchRegMask = 0xfe000000, UnconditionalBranchRegFixed = CompareBranchFixed | B31 | B30 | B25, BR = UnconditionalBranchRegFixed | B20 | B19 | B18 | B17 | B16, BLR = BR | B21, RET = BR | B22, }; // C3.3.5 enum LoadRegLiteralOp { LoadRegLiteralMask = 0x3b000000, LoadRegLiteralFixed = LoadStoreFixed | B28, LDRpc = LoadRegLiteralFixed, }; // C3.3.6 enum LoadStoreExclusiveOp { LoadStoreExclusiveMask = 0x3f000000, LoadStoreExclusiveFixed = B27, LDXR = LoadStoreExclusiveFixed | B22, STXR = LoadStoreExclusiveFixed, }; // C3.3.7-10 enum LoadStoreRegOp { LoadStoreRegMask = 0x3a000000, LoadStoreRegFixed = LoadStoreFixed | B29 | B28, STR = LoadStoreRegFixed, LDR = LoadStoreRegFixed | B22, LDRS = LoadStoreRegFixed | B23, FSTR = STR | B26, FLDR = LDR | B26, FSTRQ = STR | B26 | B23, FLDRQ = LDR | B26 | B23, }; // C3.3.14-16 enum LoadStoreRegPairOp { LoadStoreRegPairMask = 0x3a000000, LoadStoreRegPairFixed = LoadStoreFixed | B29, STP = LoadStoreRegPairFixed, LDP = LoadStoreRegPairFixed | B22, }; // C3.4.1 enum AddSubImmOp { AddSubImmMask = 0x1f000000, AddSubImmFixed = DPImmediateFixed | B24, ADDI = AddSubImmFixed, SUBI = AddSubImmFixed | B30, }; // C3.4.2 enum BitfieldOp { BitfieldMask = 0x1f800000, BitfieldFixed = 0x13000000, SBFM = BitfieldFixed, BFM = BitfieldFixed | B29, UBFM = BitfieldFixed | B30, Bitfield64 = B31 | B22, }; // C3.4.4 enum LogicalImmOp { LogicalImmMask = 0x1f800000, LogicalImmFixed = DPImmediateFixed | B25, ANDI = LogicalImmFixed, ORRI = LogicalImmFixed | B29, EORI = LogicalImmFixed | B30, ANDIS = LogicalImmFixed | B30 | B29, }; // C3.4.5 enum MoveWideOp { MoveWideMask = 0x1f800000, MoveWideFixed = DPImmediateFixed | B25 | B23, MOVN = MoveWideFixed, MOVZ = MoveWideFixed | B30, MOVK = MoveWideFixed | B30 | B29, }; // C3.4.6 enum PCRelOp { PCRelMask = 0x1f000000, PCRelFixed = DPImmediateFixed, ADR = PCRelFixed, ADRP = PCRelFixed | B31, }; // C3.5.1 enum AddSubShiftExtOp { AddSubShiftExtMask = 0x1f000000, AddSubShiftExtFixed = DPRegisterFixed | B24, ADD = AddSubShiftExtFixed, SUB = AddSubShiftExtFixed | B30, }; // C3.5.3 enum AddSubWithCarryOp { AddSubWithCarryMask = 0x1fe00000, AddSubWithCarryFixed = DPRegisterFixed | B28, ADC = AddSubWithCarryFixed, SBC = AddSubWithCarryFixed | B30, }; // C3.5.6 enum ConditionalSelectOp { ConditionalSelectMask = 0x1fe00000, ConditionalSelectFixed = DPRegisterFixed | B28 | B23, CSEL = ConditionalSelectFixed, CSINC = ConditionalSelectFixed | B10, CSINV = ConditionalSelectFixed | B30, CSNEG = ConditionalSelectFixed | B10 | B30, }; // C3.5.7 enum MiscDP1SourceOp { MiscDP1SourceMask = 0x5fe00000, MiscDP1SourceFixed = DPRegisterFixed | B30 | B28 | B23 | B22, CLZ = MiscDP1SourceFixed | B12, }; // C3.5.8 enum MiscDP2SourceOp { MiscDP2SourceMask = 0x5fe00000, MiscDP2SourceFixed = DPRegisterFixed | B28 | B23 | B22, UDIV = MiscDP2SourceFixed | B11, SDIV = MiscDP2SourceFixed | B11 | B10, LSLV = MiscDP2SourceFixed | B13, LSRV = MiscDP2SourceFixed | B13 | B10, ASRV = MiscDP2SourceFixed | B13 | B11, }; // C3.5.9 enum MiscDP3SourceOp { MiscDP3SourceMask = 0x1f000000, MiscDP3SourceFixed = DPRegisterFixed | B28 | B24, MADDW = MiscDP3SourceFixed, MADD = MiscDP3SourceFixed | B31, MSUBW = MiscDP3SourceFixed | B15, MSUB = MiscDP3SourceFixed | B31 | B15, SMULH = MiscDP3SourceFixed | B31 | B22, UMULH = MiscDP3SourceFixed | B31 | B23 | B22, SMADDL = MiscDP3SourceFixed | B31 | B21, UMADDL = MiscDP3SourceFixed | B31 | B23 | B21, SMSUBL = MiscDP3SourceFixed | B31 | B21 | B15, UMSUBL = MiscDP3SourceFixed | B31 | B23 | B21 | B15, }; // C3.5.10 enum LogicalShiftOp { LogicalShiftMask = 0x1f000000, LogicalShiftFixed = DPRegisterFixed, AND = LogicalShiftFixed, BIC = LogicalShiftFixed | B21, ORR = LogicalShiftFixed | B29, ORN = LogicalShiftFixed | B29 | B21, EOR = LogicalShiftFixed | B30, EON = LogicalShiftFixed | B30 | B21, ANDS = LogicalShiftFixed | B30 | B29, BICS = LogicalShiftFixed | B30 | B29 | B21, }; // C.3.6.5 enum SIMDCopyOp { SIMDCopyMask = 0x9fe08400, SIMDCopyFixed = DPSimd1Fixed | B10, VDUPI = SIMDCopyFixed | B30 | B11, VINSI = SIMDCopyFixed | B30 | B12 | B11, VMOVW = SIMDCopyFixed | B13 | B12 | B11, VMOVX = SIMDCopyFixed | B30 | B13 | B12 | B11, VDUP = SIMDCopyFixed | B30, VINS = SIMDCopyFixed | B30 | B29, }; // C.3.6.16 enum SIMDThreeSameOp { SIMDThreeSameMask = 0x9f200400, SIMDThreeSameFixed = DPSimd1Fixed | B21 | B10, VAND = SIMDThreeSameFixed | B30 | B12 | B11, VORR = SIMDThreeSameFixed | B30 | B23 | B12 | B11, VEOR = SIMDThreeSameFixed | B30 | B29 | B12 | B11, VADDW = SIMDThreeSameFixed | B30 | B23 | B15, VADDX = SIMDThreeSameFixed | B30 | B23 | B22 | B15, VSUBW = SIMDThreeSameFixed | B30 | B29 | B23 | B15, VSUBX = SIMDThreeSameFixed | B30 | B29 | B23 | B22 | B15, VADDS = SIMDThreeSameFixed | B30 | B15 | B14 | B12, VADDD = SIMDThreeSameFixed | B30 | B22 | B15 | B14 | B12, VSUBS = SIMDThreeSameFixed | B30 | B23 | B15 | B14 | B12, VSUBD = SIMDThreeSameFixed | B30 | B23 | B22 | B15 | B14 | B12, VMULS = SIMDThreeSameFixed | B30 | B29 | B15 | B14 | B12 | B11, VMULD = SIMDThreeSameFixed | B30 | B29 | B22 | B15 | B14 | B12 | B11, VDIVS = SIMDThreeSameFixed | B30 | B29 | B15 | B14 | B13 | B12 | B11, VDIVD = SIMDThreeSameFixed | B30 | B29 | B22 | B15 | B14 | B13 | B12 | B11, VCEQS = SIMDThreeSameFixed | B30 | B15 | B14 | B13, VCEQD = SIMDThreeSameFixed | B30 | B22 | B15 | B14 | B13, VCGES = SIMDThreeSameFixed | B30 | B29 | B15 | B14 | B13, VCGED = SIMDThreeSameFixed | B30 | B29 | B22 | B15 | B14 | B13, VCGTS = SIMDThreeSameFixed | B30 | B29 | B23 | B15 | B14 | B13, VCGTD = SIMDThreeSameFixed | B30 | B29 | B23 | B22 | B15 | B14 | B13, VMAXS = SIMDThreeSameFixed | B30 | B15 | B14 | B13 | B12, VMAXD = SIMDThreeSameFixed | B30 | B22 | B15 | B14 | B13 | B12, VMINS = SIMDThreeSameFixed | B30 | B23 | B15 | B14 | B13 | B12, VMIND = SIMDThreeSameFixed | B30 | B23 | B22 | B15 | B14 | B13 | B12, VRECPSS = SIMDThreeSameFixed | B30 | B15 | B14 | B13 | B12 | B11, VRSQRTSS = SIMDThreeSameFixed | B30 | B23 | B15 | B14 | B13 | B12 | B11, }; // C.3.6.17 enum SIMDTwoRegOp { SIMDTwoRegMask = 0x9f3e0c00, SIMDTwoRegFixed = DPSimd1Fixed | B21 | B11, VNOT = SIMDTwoRegFixed | B30 | B29 | B14 | B12, VABSS = SIMDTwoRegFixed | B30 | B23 | B15 | B14 | B13 | B12, VNEGS = SIMDTwoRegFixed | B30 | B29 | B23 | B15 | B14 | B13 | B12, VABSD = SIMDTwoRegFixed | B30 | B23 | B22 | B15 | B14 | B13 | B12, VNEGD = SIMDTwoRegFixed | B30 | B29 | B23 | B22 | B15 | B14 | B13 | B12, VSQRTS = SIMDTwoRegFixed | B30 | B29 | B23 | B16 | B15 | B14 | B13 | B12, VSQRTD = SIMDTwoRegFixed | B30 | B29 | B23 | B22 | B16 | B15 | B14 | B13 | B12, VRECPES = SIMDTwoRegFixed | B30 | B23 | B16 | B15 | B14 | B12, VRSQRTES = SIMDTwoRegFixed | B30 | B29 | B23 | B16 | B15 | B14 | B12, }; // C.3.6.22 enum FPCompareOp { FPCompareMask = 0xffa0fc07, FPCompareFixed = FPFixed | B21 | B13, FCMPD = FPCompareFixed | B22, FCMPZD = FPCompareFixed | B22 | B3, }; // C3.6.25 enum FPOneSourceOp { FPOneSourceMask = 0x5f207c00, FPOneSourceFixed = FPFixed | B21 | B14, FMOVDD = FPOneSourceFixed | B22, FABSD = FPOneSourceFixed | B22 | B15, FNEGD = FPOneSourceFixed | B22 | B16, FSQRTD = FPOneSourceFixed | B22 | B16 | B15, FCVTDS = FPOneSourceFixed | B15 | B17, FCVTSD = FPOneSourceFixed | B22 | B17, }; // C3.6.26 enum FPTwoSourceOp { FPTwoSourceMask = 0xff200c00, FPTwoSourceFixed = FPFixed | B21 | B11, FMULD = FPTwoSourceFixed | B22, FDIVD = FPTwoSourceFixed | B22 | B12, FADDD = FPTwoSourceFixed | B22 | B13, FSUBD = FPTwoSourceFixed | B22 | B13 | B12, }; // C3.6.28 enum FPImmOp { FPImmMask = 0x5f201c00, FPImmFixed = FPFixed | B21 | B12, FMOVSI = FPImmFixed, FMOVDI = FPImmFixed | B22, }; // C3.6.30 enum FPIntCvtOp { FPIntCvtMask = 0x5f00fc00, FPIntCvtFixed = FPFixed | B21, FMOVRS = FPIntCvtFixed | B18 | B17, FMOVSR = FPIntCvtFixed | B18 | B17 | B16, FMOVRD = FPIntCvtFixed | B22 | B18 | B17, FMOVDR = FPIntCvtFixed | B22 | B18 | B17 | B16, FCVTZDS = FPIntCvtFixed | B22 | B20 | B19, SCVTFD = FPIntCvtFixed | B22 | B17, }; #define APPLY_OP_LIST(_V) \ _V(DPImmediate) \ _V(CompareBranch) \ _V(LoadStore) \ _V(DPRegister) \ _V(DPSimd1) \ _V(DPSimd2) \ _V(FP) \ _V(CompareAndBranch) \ _V(ConditionalBranch) \ _V(ExceptionGen) \ _V(System) \ _V(TestAndBranch) \ _V(UnconditionalBranch) \ _V(UnconditionalBranchReg) \ _V(LoadStoreReg) \ _V(LoadStoreRegPair) \ _V(LoadRegLiteral) \ _V(LoadStoreExclusive) \ _V(AddSubImm) \ _V(Bitfield) \ _V(LogicalImm) \ _V(MoveWide) \ _V(PCRel) \ _V(AddSubShiftExt) \ _V(AddSubWithCarry) \ _V(ConditionalSelect) \ _V(MiscDP1Source) \ _V(MiscDP2Source) \ _V(MiscDP3Source) \ _V(LogicalShift) \ _V(SIMDCopy) \ _V(SIMDThreeSame) \ _V(SIMDTwoReg) \ _V(FPCompare) \ _V(FPOneSource) \ _V(FPTwoSource) \ _V(FPImm) \ _V(FPIntCvt) enum Shift { kNoShift = -1, LSL = 0, // Logical shift left LSR = 1, // Logical shift right ASR = 2, // Arithmetic shift right ROR = 3, // Rotate right kMaxShift = 4, }; enum Extend { kNoExtend = -1, UXTB = 0, UXTH = 1, UXTW = 2, UXTX = 3, SXTB = 4, SXTH = 5, SXTW = 6, SXTX = 7, kMaxExtend = 8, }; enum R31Type { R31IsSP, R31IsZR, }; // Constants used for the decoding or encoding of the individual fields of // instructions. Based on the "Figure 3-1 ARM instruction set summary". enum InstructionFields { // S-bit (modify condition register) kSShift = 29, kSBits = 1, // sf field. kSFShift = 31, kSFBits = 1, // size field, kSzShift = 30, kSzBits = 2, // Registers. kRdShift = 0, kRdBits = 5, kRnShift = 5, kRnBits = 5, kRaShift = 10, kRaBits = 5, kRmShift = 16, kRmBits = 5, kRtShift = 0, kRtBits = 5, kRt2Shift = 10, kRt2Bits = 5, kRsShift = 16, kRsBits = 5, // V Registers. kVdShift = 0, kVdBits = 5, kVnShift = 5, kVnBits = 5, kVmShift = 16, kVmBits = 5, kVtShift = 0, kVtBits = 5, // Immediates. kImm3Shift = 10, kImm3Bits = 3, kImm4Shift = 11, kImm4Bits = 4, kImm5Shift = 16, kImm5Bits = 5, kImm6Shift = 10, kImm6Bits = 6, kImm7Shift = 15, kImm7Bits = 7, kImm7Mask = 0x7f << kImm7Shift, kImm8Shift = 13, kImm8Bits = 8, kImm9Shift = 12, kImm9Bits = 9, kImm12Shift = 10, kImm12Bits = 12, kImm12Mask = 0xfff << kImm12Shift, kImm12ShiftShift = 22, kImm12ShiftBits = 2, kImm14Shift = 5, kImm14Bits = 14, kImm14Mask = 0x3fff << kImm14Shift, kImm16Shift = 5, kImm16Bits = 16, kImm16Mask = 0xffff << kImm16Shift, kImm19Shift = 5, kImm19Bits = 19, kImm19Mask = 0x7ffff << kImm19Shift, kImm26Shift = 0, kImm26Bits = 26, kImm26Mask = 0x03ffffff << kImm26Shift, kCondShift = 0, kCondBits = 4, kCondMask = 0xf << kCondShift, kSelCondShift = 12, kSelCondBits = 4, // Bitfield immediates. kNShift = 22, kNBits = 1, kImmRShift = 16, kImmRBits = 6, kImmSShift = 10, kImmSBits = 6, kHWShift = 21, kHWBits = 2, // Shift and Extend. kAddShiftExtendShift = 21, kAddShiftExtendBits = 1, kShiftTypeShift = 22, kShiftTypeBits = 2, kExtendTypeShift = 13, kExtendTypeBits = 3, // Hint Fields. kHintCRmShift = 8, kHintCRmBits = 4, kHintOp2Shift = 5, kHintOp2Bits = 3, }; // Helper functions for decoding logical immediates. static inline uint64_t RotateRight(uint64_t value, uint8_t rotate, uint8_t width) { ASSERT(width <= 64); rotate &= 63; return ((value & ((1ULL << rotate) - 1ULL)) << (width - rotate)) | (value >> rotate); } static inline uint64_t RepeatBitsAcrossReg(uint8_t reg_size, uint64_t value, uint8_t width) { ASSERT((width == 2) || (width == 4) || (width == 8) || (width == 16) || (width == 32)); ASSERT((reg_size == kWRegSizeInBits) || (reg_size == kXRegSizeInBits)); uint64_t result = value & ((1ULL << width) - 1ULL); for (unsigned i = width; i < reg_size; i *= 2) { result |= (result << i); } return result; } // The class Instr enables access to individual fields defined in the ARM // architecture instruction set encoding as described in figure A3-1. // // Example: Test whether the instruction at ptr sets the condition code bits. // // bool InstructionSetsConditionCodes(byte* ptr) { // Instr* instr = Instr::At(ptr); // int type = instr->TypeField(); // return ((type == 0) || (type == 1)) && instr->HasS(); // } // class Instr { public: enum { kInstrSize = 4, kInstrSizeLog2 = 2, kPCReadOffset = 8 }; static const int32_t kNopInstruction = HINT; // hint #0 === nop. // Reserved brk and hlt instruction codes. static const int32_t kBreakPointCode = 0xdeb0; // For breakpoint. static const int32_t kSimulatorBreakCode = 0xdeb2; // For breakpoint in sim. static const int32_t kSimulatorRedirectCode = 0xca11; // For redirection. // Breakpoint instruction filling assembler code buffers in debug mode. static const int32_t kBreakPointInstruction = // brk(0xdeb0). BRK | (kBreakPointCode << kImm16Shift); // Breakpoint instruction used by the simulator. // Should be distinct from kBreakPointInstruction and from a typical user // breakpoint inserted in generated code for debugging, e.g. brk(0). static const int32_t kSimulatorBreakpointInstruction = HLT | (kSimulatorBreakCode << kImm16Shift); // Runtime call redirection instruction used by the simulator. static const int32_t kSimulatorRedirectInstruction = HLT | (kSimulatorRedirectCode << kImm16Shift); // Read one particular bit out of the instruction bits. inline int Bit(int nr) const { return (InstructionBits() >> nr) & 1; } // Read a bit field out of the instruction bits. inline int Bits(int shift, int count) const { return (InstructionBits() >> shift) & ((1 << count) - 1); } // Get the raw instruction bits. inline int32_t InstructionBits() const { return *reinterpret_cast(this); } // Set the raw instruction bits to value. inline void SetInstructionBits(int32_t value) { *reinterpret_cast(this) = value; } inline void SetMoveWideBits(MoveWideOp op, Register rd, uint16_t imm, int hw, OperandSize sz) { ASSERT((hw >= 0) && (hw <= 3)); ASSERT((sz == kDoubleWord) || (sz == kWord)); const int32_t size = (sz == kDoubleWord) ? B31 : 0; SetInstructionBits(op | size | (static_cast(rd) << kRdShift) | (static_cast(hw) << kHWShift) | (static_cast(imm) << kImm16Shift)); } inline void SetUnconditionalBranchRegBits(UnconditionalBranchRegOp op, Register rn) { SetInstructionBits(op | (static_cast(rn) << kRnShift)); } inline void SetImm12Bits(int32_t orig, int32_t imm12) { ASSERT((imm12 & 0xfffff000) == 0); SetInstructionBits((orig & ~kImm12Mask) | (imm12 << kImm12Shift)); } inline int NField() const { return Bit(22); } inline int SField() const { return Bit(kSShift); } inline int SFField() const { return Bit(kSFShift); } inline int SzField() const { return Bits(kSzShift, kSzBits); } inline Register RdField() const { return static_cast(Bits(kRdShift, kRdBits)); } inline Register RnField() const { return static_cast(Bits(kRnShift, kRnBits)); } inline Register RaField() const { return static_cast(Bits(kRaShift, kRaBits)); } inline Register RmField() const { return static_cast(Bits(kRmShift, kRmBits)); } inline Register RtField() const { return static_cast(Bits(kRtShift, kRtBits)); } inline Register Rt2Field() const { return static_cast(Bits(kRt2Shift, kRt2Bits)); } inline Register RsField() const { return static_cast(Bits(kRsShift, kRsBits)); } inline VRegister VdField() const { return static_cast(Bits(kVdShift, kVdBits)); } inline VRegister VnField() const { return static_cast(Bits(kVnShift, kVnBits)); } inline VRegister VmField() const { return static_cast(Bits(kVmShift, kVmBits)); } inline VRegister VtField() const { return static_cast(Bits(kVtShift, kVtBits)); } // Immediates inline int Imm3Field() const { return Bits(kImm3Shift, kImm3Bits); } inline int Imm6Field() const { return Bits(kImm6Shift, kImm6Bits); } inline int Imm7Field() const { return Bits(kImm7Shift, kImm7Bits); } // Sign-extended Imm7Field() inline int64_t SImm7Field() const { return (static_cast(Imm7Field()) << 25) >> 25; } inline int Imm8Field() const { return Bits(kImm8Shift, kImm8Bits); } inline int Imm9Field() const { return Bits(kImm9Shift, kImm9Bits); } // Sign-extended Imm9Field() inline int64_t SImm9Field() const { return (static_cast(Imm9Field()) << 23) >> 23; } inline int Imm12Field() const { return Bits(kImm12Shift, kImm12Bits); } inline int Imm12ShiftField() const { return Bits(kImm12ShiftShift, kImm12ShiftBits); } inline int Imm16Field() const { return Bits(kImm16Shift, kImm16Bits); } inline int HWField() const { return Bits(kHWShift, kHWBits); } inline int ImmRField() const { return Bits(kImmRShift, kImmRBits); } inline int ImmSField() const { return Bits(kImmSShift, kImmSBits); } inline int Imm14Field() const { return Bits(kImm14Shift, kImm14Bits); } inline int64_t SImm14Field() const { return (static_cast(Imm14Field()) << 18) >> 18; } inline int Imm19Field() const { return Bits(kImm19Shift, kImm19Bits); } inline int64_t SImm19Field() const { return (static_cast(Imm19Field()) << 13) >> 13; } inline int Imm26Field() const { return Bits(kImm26Shift, kImm26Bits); } inline int64_t SImm26Field() const { return (static_cast(Imm26Field()) << 6) >> 6; } inline Condition ConditionField() const { return static_cast(Bits(kCondShift, kCondBits)); } inline Condition SelectConditionField() const { return static_cast(Bits(kSelCondShift, kSelCondBits)); } // Shift and Extend. inline bool IsShift() const { return IsLogicalShiftOp() || (Bit(kAddShiftExtendShift) == 0); } inline bool IsExtend() const { return !IsLogicalShiftOp() && (Bit(kAddShiftExtendShift) == 1); } inline Shift ShiftTypeField() const { return static_cast(Bits(kShiftTypeShift, kShiftTypeBits)); } inline Extend ExtendTypeField() const { return static_cast(Bits(kExtendTypeShift, kExtendTypeBits)); } inline int ShiftAmountField() const { return Imm6Field(); } inline int ExtShiftAmountField() const { return Imm3Field(); } // Instruction identification. #define IS_OP(op) \ inline bool Is##op##Op() const { \ return ((InstructionBits() & op##Mask) == (op##Fixed & op##Mask)); \ } APPLY_OP_LIST(IS_OP) #undef IS_OP inline bool HasS() const { return (SField() == 1); } // Indicate whether Rd can be the CSP or ZR. This does not check that the // instruction actually has an Rd field. R31Type RdMode() const { // The following instructions use CSP as Rd: // Add/sub (immediate) when not setting the flags. // Add/sub (extended) when not setting the flags. // Logical (immediate) when not setting the flags. // Otherwise, R31 is the ZR. if (IsAddSubImmOp() || (IsAddSubShiftExtOp() && IsExtend())) { if (HasS()) { return R31IsZR; } else { return R31IsSP; } } if (IsLogicalImmOp()) { const int op = Bits(29, 2); const bool set_flags = op == 3; if (set_flags) { return R31IsZR; } else { return R31IsSP; } } return R31IsZR; } // Indicate whether Rn can be CSP or ZR. This does not check that the // instruction actually has an Rn field. R31Type RnMode() const { // The following instructions use CSP as Rn: // All loads and stores. // Add/sub (immediate). // Add/sub (extended). // Otherwise, r31 is ZR. if (IsLoadStoreOp() || IsAddSubImmOp() || (IsAddSubShiftExtOp() && IsExtend())) { return R31IsSP; } return R31IsZR; } // Logical immediates can't encode zero, so a return value of zero is used to // indicate a failure case. Specifically, where the constraints on imm_s are // not met. uint64_t ImmLogical() { const uint8_t reg_size = SFField() == 1 ? kXRegSizeInBits : kWRegSizeInBits; const int64_t n = NField(); const int64_t imm_s = ImmSField(); const int64_t imm_r = ImmRField(); // An integer is constructed from the n, imm_s and imm_r bits according to // the following table: // // N imms immr size S R // 1 ssssss rrrrrr 64 UInt(ssssss) UInt(rrrrrr) // 0 0sssss xrrrrr 32 UInt(sssss) UInt(rrrrr) // 0 10ssss xxrrrr 16 UInt(ssss) UInt(rrrr) // 0 110sss xxxrrr 8 UInt(sss) UInt(rrr) // 0 1110ss xxxxrr 4 UInt(ss) UInt(rr) // 0 11110s xxxxxr 2 UInt(s) UInt(r) // (s bits must not be all set) // // A pattern is constructed of size bits, where the least significant S+1 // bits are set. The pattern is rotated right by R, and repeated across a // 32 or 64-bit value, depending on destination register width. if (n == 1) { if (imm_s == 0x3F) { return 0; } uint64_t bits = (1ULL << (imm_s + 1)) - 1; return RotateRight(bits, imm_r, 64); } else { if ((imm_s >> 1) == 0x1F) { return 0; } for (int width = 0x20; width >= 0x2; width >>= 1) { if ((imm_s & width) == 0) { int mask = width - 1; if ((imm_s & mask) == mask) { return 0; } uint64_t bits = (1ULL << ((imm_s & mask) + 1)) - 1; return RepeatBitsAcrossReg( reg_size, RotateRight(bits, imm_r & mask, width), width); } } } UNREACHABLE(); return 0; } static int64_t VFPExpandImm(uint8_t imm8) { const int64_t sign = static_cast((imm8 & 0x80) >> 7) << 63; const int64_t hi_exp = static_cast(!((imm8 & 0x40) >> 6)) << 62; const int64_t mid_exp = (((imm8 & 0x40) >> 6) == 0) ? 0 : (0xffLL << 54); const int64_t low_exp = static_cast((imm8 & 0x30) >> 4) << 52; const int64_t frac = static_cast(imm8 & 0x0f) << 48; return sign | hi_exp | mid_exp | low_exp | frac; } // Instructions are read out of a code stream. The only way to get a // reference to an instruction is to convert a pointer. There is no way // to allocate or create instances of class Instr. // Use the At(pc) function to create references to Instr. static Instr* At(::dart::uword pc) { return reinterpret_cast(pc); } private: DISALLOW_ALLOCATION(); DISALLOW_IMPLICIT_CONSTRUCTORS(Instr); }; } // namespace arch_arm64 #endif // RUNTIME_VM_CONSTANTS_ARM64_H_