| // 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" |
| #include "platform/globals.h" |
| |
| #include "vm/constants_base.h" |
| |
| namespace dart { |
| |
| // LR register should not be used directly in handwritten assembly patterns, |
| // because it might contain return address. Instead use macross CLOBBERS_LR, |
| // SPILLS_RETURN_ADDRESS_FROM_LR_TO_REGISTER, |
| // RESTORES_RETURN_ADDRESS_FROM_REGISTER_TO_LR, SPILLS_LR_TO_FRAME, |
| // RESTORES_LR_FROM_FRAME, READS_RETURN_ADDRESS_FROM_LR, |
| // WRITES_RETURN_ADDRESS_TO_LR to get access to LR constant in a checked way. |
| // |
| // To prevent accidental use of LR constant we rename it to |
| // LR_DO_NOT_USE_DIRECTLY (while keeping the code in this file and other files |
| // which are permitted to access LR constant the same by defining LR as |
| // LR_DO_NOT_USE_DIRECTLY). You can also use LINK_REGISTER if you need |
| // to compare LR register code. |
| #define LR LR_DO_NOT_USE_DIRECTLY |
| |
| 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, // reserved on iOS, shadow call stack on Fuchsia. |
| R19 = 19, |
| R20 = 20, |
| R21 = 21, // DISPATCH_TABLE_REG |
| R22 = 22, // NULL_REG |
| R23 = 23, |
| R24 = 24, |
| R25 = 25, |
| R26 = 26, // THR |
| R27 = 27, // PP |
| R28 = 28, // HEAP_BITS |
| R29 = 29, // FP |
| R30 = 30, // LR |
| R31 = 31, // ZR, CSP |
| kNumberOfCpuRegisters = 32, |
| kNoRegister = -1, |
| kNoRegister2 = -2, |
| |
| // 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, // Note: direct access to this constant is not allowed. See above. |
| }; |
| |
| 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 kFpuRegisterSize = 16; |
| typedef simd128_value_t fpu_register_t; |
| const int kNumberOfFpuRegisters = kNumberOfVRegisters; |
| const FpuRegister kNoFpuRegister = kNoVRegister; |
| |
| extern const char* const cpu_reg_names[kNumberOfCpuRegisters]; |
| extern const char* const cpu_reg_abi_names[kNumberOfCpuRegisters]; |
| extern const char* const 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 DISPATCH_TABLE_REG = R21; // Dispatch table register. |
| const Register CODE_REG = R24; |
| const Register FPREG = FP; // Frame pointer register. |
| const Register SPREG = R15; // Stack pointer 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 HEAP_BITS = R28; // write_barrier_mask << 32 | heap_base >> 32 |
| const Register NULL_REG = R22; // Caches NullObject() value. |
| |
| // 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; |
| |
| // Common ABI for shared slow path stubs. |
| struct SharedSlowPathStubABI { |
| static const Register kResultReg = R0; |
| }; |
| |
| // ABI for instantiation stubs. |
| struct InstantiationABI { |
| static const Register kUninstantiatedTypeArgumentsReg = R3; |
| static const Register kInstantiatorTypeArgumentsReg = R2; |
| static const Register kFunctionTypeArgumentsReg = R1; |
| static const Register kResultTypeArgumentsReg = R0; |
| static const Register kResultTypeReg = R0; |
| }; |
| |
| // Registers in addition to those listed in TypeTestABI used inside the |
| // implementation of type testing stubs that are _not_ preserved. |
| struct TTSInternalRegs { |
| static const Register kInstanceTypeArgumentsReg = R7; |
| static const Register kScratchReg = R9; |
| static const Register kSubTypeArgumentReg = R5; |
| static const Register kSuperTypeArgumentReg = R6; |
| |
| // Must be pushed/popped whenever generic type arguments are being checked as |
| // they overlap with registers in TypeTestABI. |
| static const intptr_t kSavedTypeArgumentRegisters = 0; |
| |
| static const intptr_t kInternalRegisters = |
| ((1 << kInstanceTypeArgumentsReg) | (1 << kScratchReg) | |
| (1 << kSubTypeArgumentReg) | (1 << kSuperTypeArgumentReg)) & |
| ~kSavedTypeArgumentRegisters; |
| }; |
| |
| // Registers in addition to those listed in TypeTestABI used inside the |
| // implementation of subtype test cache stubs that are _not_ preserved. |
| struct STCInternalRegs { |
| static const Register kInstanceCidOrSignatureReg = R6; |
| static const Register kInstanceInstantiatorTypeArgumentsReg = R5; |
| static const Register kInstanceParentFunctionTypeArgumentsReg = R9; |
| static const Register kInstanceDelayedFunctionTypeArgumentsReg = R10; |
| |
| static const intptr_t kInternalRegisters = |
| (1 << kInstanceCidOrSignatureReg) | |
| (1 << kInstanceInstantiatorTypeArgumentsReg) | |
| (1 << kInstanceParentFunctionTypeArgumentsReg) | |
| (1 << kInstanceDelayedFunctionTypeArgumentsReg); |
| }; |
| |
| // Calling convention when calling TypeTestingStub and SubtypeTestCacheStub. |
| struct TypeTestABI { |
| static const Register kInstanceReg = R0; |
| static const Register kDstTypeReg = R8; |
| static const Register kInstantiatorTypeArgumentsReg = R2; |
| static const Register kFunctionTypeArgumentsReg = R1; |
| static const Register kSubtypeTestCacheReg = R3; |
| static const Register kScratchReg = R4; |
| |
| // For calls to InstanceOfStub. |
| static const Register kInstanceOfResultReg = kInstanceReg; |
| // For calls to SubtypeNTestCacheStub. Must not overlap with any other |
| // registers above, for it is also used internally as kNullReg in those stubs. |
| static const Register kSubtypeTestCacheResultReg = R7; |
| |
| // Registers that need saving across SubtypeTestCacheStub calls. |
| static const intptr_t kSubtypeTestCacheStubCallerSavedRegisters = |
| 1 << kSubtypeTestCacheReg; |
| |
| static const intptr_t kPreservedAbiRegisters = |
| (1 << kInstanceReg) | (1 << kDstTypeReg) | |
| (1 << kInstantiatorTypeArgumentsReg) | (1 << kFunctionTypeArgumentsReg); |
| |
| static const intptr_t kNonPreservedAbiRegisters = |
| TTSInternalRegs::kInternalRegisters | |
| STCInternalRegs::kInternalRegisters | (1 << kSubtypeTestCacheReg) | |
| (1 << kScratchReg) | (1 << kSubtypeTestCacheResultReg) | (1 << CODE_REG); |
| |
| static const intptr_t kAbiRegisters = |
| kPreservedAbiRegisters | kNonPreservedAbiRegisters; |
| }; |
| |
| // Calling convention when calling AssertSubtypeStub. |
| struct AssertSubtypeABI { |
| static const Register kSubTypeReg = R0; |
| static const Register kSuperTypeReg = R8; |
| static const Register kInstantiatorTypeArgumentsReg = R2; |
| static const Register kFunctionTypeArgumentsReg = R1; |
| static const Register kDstNameReg = R3; |
| |
| static const intptr_t kAbiRegisters = |
| (1 << kSubTypeReg) | (1 << kSuperTypeReg) | |
| (1 << kInstantiatorTypeArgumentsReg) | (1 << kFunctionTypeArgumentsReg) | |
| (1 << kDstNameReg); |
| |
| // No result register, as AssertSubtype is only run for side effect |
| // (throws if the subtype check fails). |
| }; |
| |
| // ABI for InitStaticFieldStub. |
| struct InitStaticFieldABI { |
| static const Register kFieldReg = R2; |
| static const Register kResultReg = R0; |
| }; |
| |
| // Registers used inside the implementation of InitLateStaticFieldStub. |
| struct InitLateStaticFieldInternalRegs { |
| static const Register kFunctionReg = R0; |
| static const Register kAddressReg = R3; |
| static const Register kScratchReg = R4; |
| }; |
| |
| // ABI for InitInstanceFieldStub. |
| struct InitInstanceFieldABI { |
| static const Register kInstanceReg = R1; |
| static const Register kFieldReg = R2; |
| static const Register kResultReg = R0; |
| }; |
| |
| // Registers used inside the implementation of InitLateInstanceFieldStub. |
| struct InitLateInstanceFieldInternalRegs { |
| static const Register kFunctionReg = R0; |
| static const Register kAddressReg = R3; |
| static const Register kScratchReg = R4; |
| }; |
| |
| // ABI for LateInitializationError stubs. |
| struct LateInitializationErrorABI { |
| static const Register kFieldReg = R9; |
| }; |
| |
| // ABI for ThrowStub. |
| struct ThrowABI { |
| static const Register kExceptionReg = R0; |
| }; |
| |
| // ABI for ReThrowStub. |
| struct ReThrowABI { |
| static const Register kExceptionReg = R0; |
| static const Register kStackTraceReg = R1; |
| }; |
| |
| // ABI for AssertBooleanStub. |
| struct AssertBooleanABI { |
| static const Register kObjectReg = R0; |
| }; |
| |
| // ABI for RangeErrorStub. |
| struct RangeErrorABI { |
| static const Register kLengthReg = R0; |
| static const Register kIndexReg = R1; |
| }; |
| |
| // ABI for AllocateObjectStub. |
| struct AllocateObjectABI { |
| static const Register kResultReg = R0; |
| static const Register kTypeArgumentsReg = R1; |
| static const Register kTagsReg = R2; |
| }; |
| |
| // ABI for AllocateClosureStub. |
| struct AllocateClosureABI { |
| static const Register kResultReg = AllocateObjectABI::kResultReg; |
| static const Register kFunctionReg = R1; |
| static const Register kContextReg = R2; |
| static const Register kScratchReg = R4; |
| }; |
| |
| // ABI for AllocateMintShared*Stub. |
| struct AllocateMintABI { |
| static const Register kResultReg = AllocateObjectABI::kResultReg; |
| static const Register kTempReg = R1; |
| }; |
| |
| // ABI for Allocate{Mint,Double,Float32x4,Float64x2}Stub. |
| struct AllocateBoxABI { |
| static const Register kResultReg = AllocateObjectABI::kResultReg; |
| static const Register kTempReg = R1; |
| }; |
| |
| // ABI for AllocateArrayStub. |
| struct AllocateArrayABI { |
| static const Register kResultReg = AllocateObjectABI::kResultReg; |
| static const Register kLengthReg = R2; |
| static const Register kTypeArgumentsReg = R1; |
| }; |
| |
| // ABI for AllocateTypedDataArrayStub. |
| struct AllocateTypedDataArrayABI { |
| static const Register kResultReg = AllocateObjectABI::kResultReg; |
| static const Register kLengthReg = R4; |
| }; |
| |
| // ABI for BoxDoubleStub. |
| struct BoxDoubleStubABI { |
| static const FpuRegister kValueReg = V0; |
| static const Register kTempReg = R1; |
| static const Register kResultReg = R0; |
| }; |
| |
| // ABI for DoubleToIntegerStub. |
| struct DoubleToIntegerStubABI { |
| static const FpuRegister kInputReg = V0; |
| static const Register kRecognizedKindReg = R0; |
| static const Register kResultReg = R0; |
| }; |
| |
| // ABI for DispatchTableNullErrorStub and consequently for all dispatch |
| // table calls (though normal functions will not expect or use this |
| // register). This ABI is added to distinguish memory corruption errors from |
| // null errors. |
| struct DispatchTableNullErrorABI { |
| static const Register kClassIdReg = R0; |
| }; |
| |
| // TODO(regis): Add ABIs for type testing stubs and is-type test stubs instead |
| // of reusing the constants of the instantiation stubs ABI. |
| |
| // 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. |
| |
| #define R(reg) (static_cast<RegList>(1) << (reg)) |
| |
| // C++ ABI call registers. |
| const RegList kAbiArgumentCpuRegs = |
| R(R0) | R(R1) | R(R2) | R(R3) | R(R4) | R(R5) | R(R6) | R(R7); |
| #if defined(DART_TARGET_OS_FUCHSIA) |
| // We rely on R18 not being touched by Dart generated assembly or stubs at all. |
| // We rely on that any calls into C++ also preserve R18. |
| const RegList kAbiPreservedCpuRegs = R(R18) | R(R19) | R(R20) | R(R21) | |
| R(R22) | R(R23) | R(R24) | R(R25) | |
| R(R26) | R(R27) | R(R28); |
| const Register kAbiFirstPreservedCpuReg = R18; |
| const Register kAbiLastPreservedCpuReg = R28; |
| const int kAbiPreservedCpuRegCount = 11; |
| #else |
| const RegList kAbiPreservedCpuRegs = R(R19) | R(R20) | R(R21) | R(R22) | |
| R(R23) | R(R24) | R(R25) | R(R26) | |
| R(R27) | R(R28); |
| const Register kAbiFirstPreservedCpuReg = R19; |
| const Register kAbiLastPreservedCpuReg = R28; |
| const int kAbiPreservedCpuRegCount = 10; |
| #endif |
| const VRegister kAbiFirstPreservedFpuReg = V8; |
| const VRegister kAbiLastPreservedFpuReg = V15; |
| const int kAbiPreservedFpuRegCount = 8; |
| |
| const intptr_t kReservedCpuRegisters = R(SPREG) | // Dart SP |
| R(FPREG) | R(TMP) | R(TMP2) | R(PP) | |
| R(THR) | R(LR) | R(HEAP_BITS) | |
| R(NULL_REG) | R(R31) | // C++ SP |
| R(R18) | R(DISPATCH_TABLE_REG); |
| constexpr intptr_t kNumberOfReservedCpuRegisters = 13; |
| // 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; |
| |
| // Two callee save scratch registers used by leaf runtime call sequence. |
| const Register kCallLeafRuntimeCalleeSaveScratch1 = R20; |
| const Register kCallLeafRuntimeCalleeSaveScratch2 = R25; |
| static_assert((R(kCallLeafRuntimeCalleeSaveScratch1) & kAbiPreservedCpuRegs) != |
| 0, |
| "Need callee save scratch register for leaf runtime calls."); |
| static_assert((R(kCallLeafRuntimeCalleeSaveScratch2) & kAbiPreservedCpuRegs) != |
| 0, |
| "Need callee save scratch register for leaf runtime calls."); |
| |
| constexpr int kStoreBufferWrapperSize = 32; |
| |
| class CallingConventions { |
| public: |
| static const intptr_t kArgumentRegisters = kAbiArgumentCpuRegs; |
| static const Register ArgumentRegisters[]; |
| static const intptr_t kNumArgRegs = 8; |
| // The native ABI uses R8 to pass the pointer to the memory preallocated for |
| // struct return values. Arm64 is the only ABI in which this pointer is _not_ |
| // in ArgumentRegisters[0] or on the stack. |
| static const Register kPointerToReturnStructRegisterCall = R8; |
| static const Register kPointerToReturnStructRegisterReturn = R8; |
| |
| 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; |
| |
| static constexpr intptr_t kCalleeSaveCpuRegisters = kAbiPreservedCpuRegs; |
| |
| // Whether larger than wordsize arguments are aligned to even registers. |
| static constexpr AlignmentStrategy kArgumentRegisterAlignment = |
| kAlignedToWordSize; |
| |
| // How stack arguments are aligned. |
| #if defined(DART_TARGET_OS_MACOS_IOS) || defined(DART_TARGET_OS_MACOS) |
| // > Function arguments may consume slots on the stack that are not multiples |
| // > of 8 bytes. |
| // https://developer.apple.com/documentation/xcode/writing_arm64_code_for_apple_platforms |
| static constexpr AlignmentStrategy kArgumentStackAlignment = |
| kAlignedToValueSize; |
| #else |
| static constexpr AlignmentStrategy kArgumentStackAlignment = |
| kAlignedToWordSize; |
| #endif |
| |
| // How fields in compounds are aligned. |
| static constexpr AlignmentStrategy kFieldAlignment = kAlignedToValueSize; |
| |
| // Whether 1 or 2 byte-sized arguments or return values are passed extended |
| // to 4 bytes. |
| #if defined(DART_TARGET_OS_MACOS_IOS) || defined(DART_TARGET_OS_MACOS) |
| static constexpr ExtensionStrategy kReturnRegisterExtension = kExtendedTo4; |
| static constexpr ExtensionStrategy kArgumentRegisterExtension = kExtendedTo4; |
| #else |
| static constexpr ExtensionStrategy kReturnRegisterExtension = kNotExtended; |
| static constexpr ExtensionStrategy kArgumentRegisterExtension = kNotExtended; |
| #endif |
| static constexpr ExtensionStrategy kArgumentStackExtension = kNotExtended; |
| |
| static constexpr Register kReturnReg = R0; |
| static constexpr Register kSecondReturnReg = kNoRegister; |
| static constexpr FpuRegister kReturnFpuReg = V0; |
| |
| static constexpr Register kFfiAnyNonAbiRegister = R19; |
| static constexpr Register kFirstNonArgumentRegister = R9; |
| static constexpr Register kSecondNonArgumentRegister = R10; |
| static constexpr Register kStackPointerRegister = SPREG; |
| |
| COMPILE_ASSERT( |
| ((R(kFirstNonArgumentRegister) | R(kSecondNonArgumentRegister)) & |
| (kArgumentRegisters | R(kPointerToReturnStructRegisterCall))) == 0); |
| }; |
| |
| #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, |
| OVERFLOW = VS, |
| NO_OVERFLOW = VC, |
| |
| kInvalidCondition = 16 |
| }; |
| |
| static inline Condition InvertCondition(Condition c) { |
| COMPILE_ASSERT((EQ ^ NE) == 1); |
| COMPILE_ASSERT((CS ^ CC) == 1); |
| COMPILE_ASSERT((MI ^ PL) == 1); |
| COMPILE_ASSERT((VS ^ VC) == 1); |
| COMPILE_ASSERT((HI ^ LS) == 1); |
| COMPILE_ASSERT((GE ^ LT) == 1); |
| COMPILE_ASSERT((GT ^ LE) == 1); |
| COMPILE_ASSERT((AL ^ NV) == 1); |
| ASSERT(c != AL); |
| ASSERT(c != kInvalidCondition); |
| return static_cast<Condition>(c ^ 1); |
| } |
| |
| 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), |
| }; |
| |
| // 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, |
| LDAR = LoadStoreExclusiveFixed | B23 | B22 | B15, |
| STLR = LoadStoreExclusiveFixed | B23 | B15, |
| }; |
| |
| // 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, |
| RBIT = MiscDP1SourceFixed, // opc = '00' |
| }; |
| |
| // 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, |
| FCVTZS_D = FPIntCvtFixed | B22 | B20 | B19, |
| FCVTMS_D = FPIntCvtFixed | B22 | B20, |
| FCVTPS_D = FPIntCvtFixed | B22 | 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, // Zero extend byte. |
| UXTH = 1, // Zero extend halfword (16 bits). |
| UXTW = 2, // Zero extend word (32 bits). |
| UXTX = 3, // Zero extend doubleword (64 bits). |
| SXTB = 4, // Sign extend byte. |
| SXTH = 5, // Sign extend halfword (16 bits). |
| SXTW = 6, // Sign extend word (32 bits). |
| SXTX = 7, // Sign extend doubleword (64 bits). |
| 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); |
| uint8_t right = rotate & 63; |
| uint8_t left = (width - rotate) & 63; |
| return ((value & ((1ULL << right) - 1ULL)) << left) | (value >> right); |
| } |
| |
| 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; |
| } |
| |
| enum ScaleFactor { |
| TIMES_1 = 0, |
| TIMES_2 = 1, |
| TIMES_4 = 2, |
| TIMES_8 = 3, |
| TIMES_16 = 4, |
| // We can't include vm/compiler/runtime_api.h, so just be explicit instead |
| // of using (dart::)kWordSizeLog2. |
| #if defined(TARGET_ARCH_IS_64_BIT) |
| // Used for Smi-boxed indices. |
| TIMES_HALF_WORD_SIZE = kInt64SizeLog2 - 1, |
| // Used for unboxed indices. |
| TIMES_WORD_SIZE = kInt64SizeLog2, |
| #else |
| #error "Unexpected word size" |
| #endif |
| #if !defined(DART_COMPRESSED_POINTERS) |
| TIMES_COMPRESSED_WORD_SIZE = TIMES_WORD_SIZE, |
| #else |
| TIMES_COMPRESSED_WORD_SIZE = TIMES_HALF_WORD_SIZE, |
| #endif |
| }; |
| |
| // 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 }; |
| |
| enum class WideSize { k32Bits, k64Bits }; |
| |
| 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<const int32_t*>(this); |
| } |
| |
| // Set the raw instruction bits to value. |
| inline void SetInstructionBits(int32_t value) { |
| *reinterpret_cast<int32_t*>(this) = value; |
| } |
| |
| inline void SetMoveWideBits(MoveWideOp op, |
| Register rd, |
| uint16_t imm, |
| int hw, |
| WideSize sz) { |
| ASSERT((hw >= 0) && (hw <= 3)); |
| const int32_t size = (sz == WideSize::k64Bits) ? B31 : 0; |
| SetInstructionBits(op | size | (static_cast<int32_t>(rd) << kRdShift) | |
| (static_cast<int32_t>(hw) << kHWShift) | |
| (static_cast<int32_t>(imm) << kImm16Shift)); |
| } |
| |
| inline void SetUnconditionalBranchRegBits(UnconditionalBranchRegOp op, |
| Register rn) { |
| SetInstructionBits(op | (static_cast<int32_t>(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<Register>(Bits(kRdShift, kRdBits)); |
| } |
| inline Register RnField() const { |
| return static_cast<Register>(Bits(kRnShift, kRnBits)); |
| } |
| inline Register RaField() const { |
| return static_cast<Register>(Bits(kRaShift, kRaBits)); |
| } |
| inline Register RmField() const { |
| return static_cast<Register>(Bits(kRmShift, kRmBits)); |
| } |
| inline Register RtField() const { |
| return static_cast<Register>(Bits(kRtShift, kRtBits)); |
| } |
| inline Register Rt2Field() const { |
| return static_cast<Register>(Bits(kRt2Shift, kRt2Bits)); |
| } |
| inline Register RsField() const { |
| return static_cast<Register>(Bits(kRsShift, kRsBits)); |
| } |
| |
| inline VRegister VdField() const { |
| return static_cast<VRegister>(Bits(kVdShift, kVdBits)); |
| } |
| inline VRegister VnField() const { |
| return static_cast<VRegister>(Bits(kVnShift, kVnBits)); |
| } |
| inline VRegister VmField() const { |
| return static_cast<VRegister>(Bits(kVmShift, kVmBits)); |
| } |
| inline VRegister VtField() const { |
| return static_cast<VRegister>(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<int32_t>(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<int32_t>(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<int32_t>(Imm14Field()) << 18) >> 18; |
| } |
| inline int Imm19Field() const { return Bits(kImm19Shift, kImm19Bits); } |
| inline int64_t SImm19Field() const { |
| return (static_cast<int32_t>(Imm19Field()) << 13) >> 13; |
| } |
| inline int Imm26Field() const { return Bits(kImm26Shift, kImm26Bits); } |
| inline int64_t SImm26Field() const { |
| return (static_cast<int32_t>(Imm26Field()) << 6) >> 6; |
| } |
| |
| inline Condition ConditionField() const { |
| return static_cast<Condition>(Bits(kCondShift, kCondBits)); |
| } |
| inline Condition SelectConditionField() const { |
| return static_cast<Condition>(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<Shift>(Bits(kShiftTypeShift, kShiftTypeBits)); |
| } |
| inline Extend ExtendTypeField() const { |
| return static_cast<Extend>(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<int64_t>((imm8 & 0x80) >> 7) << 63; |
| const int64_t hi_exp = static_cast<int64_t>(!((imm8 & 0x40) >> 6)) << 62; |
| const int64_t mid_exp = (((imm8 & 0x40) >> 6) == 0) ? 0 : (0xffLL << 54); |
| const int64_t low_exp = static_cast<int64_t>((imm8 & 0x30) >> 4) << 52; |
| const int64_t frac = static_cast<int64_t>(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(uword pc) { return reinterpret_cast<Instr*>(pc); } |
| |
| private: |
| DISALLOW_ALLOCATION(); |
| DISALLOW_IMPLICIT_CONSTRUCTORS(Instr); |
| }; |
| |
| const uint64_t kBreakInstructionFiller = 0xD4200000D4200000L; // brk #0; brk #0 |
| |
| struct LinkRegister {}; |
| |
| constexpr bool operator==(Register r, LinkRegister) { |
| return r == LR; |
| } |
| |
| constexpr bool operator!=(Register r, LinkRegister lr) { |
| return !(r == lr); |
| } |
| |
| inline Register ConcreteRegister(LinkRegister) { |
| return LR; |
| } |
| |
| #undef LR |
| |
| #define LINK_REGISTER (LinkRegister()) |
| |
| } // namespace dart |
| |
| #endif // RUNTIME_VM_CONSTANTS_ARM64_H_ |