| // Copyright (c) 2013, 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_ARM_H_ | |
| #define RUNTIME_VM_CONSTANTS_ARM_H_ | |
| #ifndef RUNTIME_VM_CONSTANTS_H_ | |
| #error Do not include constants_arm.h directly; use constants.h instead. | |
| #endif | |
| #include "platform/assert.h" | |
| #include "platform/globals.h" | |
| #include "platform/utils.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 | |
| #define R(reg) (static_cast<RegList>(1) << (reg)) | |
| // We support both VFPv3-D16 and VFPv3-D32 profiles, but currently only one at | |
| // a time. | |
| #if defined(DART_TARGET_OS_ANDROID) || defined(DART_TARGET_OS_LINUX) | |
| #define VFPv3_D16 | |
| #elif defined(DART_TARGET_OS_MACOS_IOS) || defined(DART_TARGET_OS_WINDOWS) | |
| #define VFPv3_D32 | |
| #else | |
| #error Which VFP? | |
| #endif | |
| // The Linux/Android ABI and the iOS ABI differ in their choice of frame | |
| // pointer, their treatment of R9, and the interprocedural stack alignment. | |
| // EABI (Linux, Android, Windows) | |
| // See "Procedure Call Standard for the ARM Architecture". | |
| // R0-R1: Argument / result / volatile | |
| // R2-R3: Argument / volatile | |
| // R4-R10: Preserved | |
| // R11: Frame pointer | |
| // R12: Volatile | |
| // R13: Stack pointer | |
| // R14: Link register | |
| // R15: Program counter | |
| // Stack alignment: 4 bytes always, 8 bytes at public interfaces | |
| // Linux (Debian armhf), Windows and Android also differ in whether floating | |
| // point arguments are passed in floating point registers. Linux and Windows | |
| // use hardfp and Android uses softfp. See | |
| // TargetCPUFeatures::hardfp_supported(). | |
| // iOS ABI | |
| // See "iOS ABI Function Call Guide" | |
| // R0-R1: Argument / result / volatile | |
| // R2-R3: Argument / volatile | |
| // R4-R6: Preserved | |
| // R7: Frame pointer | |
| // R8-R11: Preserved | |
| // R12: Volatile | |
| // R13: Stack pointer | |
| // R14: Link register | |
| // R15: Program counter | |
| // Stack alignment: 4 bytes always, 4 bytes at public interfaces | |
| // iOS passes floating point arguments in integer registers (softfp) | |
| enum Register { | |
| R0 = 0, | |
| R1 = 1, | |
| R2 = 2, | |
| R3 = 3, | |
| R4 = 4, | |
| R5 = 5, // PP | |
| R6 = 6, // CODE_REG | |
| R7 = 7, // FP on iOS, DISPATCH_TABLE_REG on non-iOS (AOT only) | |
| R8 = 8, | |
| R9 = 9, | |
| R10 = 10, // THR | |
| R11 = 11, // FP on non-iOS, DISPATCH_TABLE_REG on iOS (AOT only) | |
| R12 = 12, // IP aka TMP | |
| R13 = 13, // SP | |
| R14 = 14, // LR | |
| R15 = 15, // PC | |
| kNumberOfCpuRegisters = 16, | |
| kNoRegister = -1, // Signals an illegal register. | |
| // Aliases. | |
| #if defined(DART_TARGET_OS_MACOS) || defined(DART_TARGET_OS_MACOS_IOS) | |
| FP = R7, | |
| NOTFP = R11, | |
| #else | |
| FP = R11, | |
| NOTFP = R7, | |
| #endif | |
| IP = R12, | |
| SP = R13, | |
| LR = R14, // Note: direct access to this constant is not allowed. See above. | |
| PC = R15, | |
| }; | |
| // Values for single-precision floating point registers. | |
| enum SRegister { | |
| kNoSRegister = -1, | |
| S0 = 0, | |
| S1 = 1, | |
| S2 = 2, | |
| S3 = 3, | |
| S4 = 4, | |
| S5 = 5, | |
| S6 = 6, | |
| S7 = 7, | |
| S8 = 8, | |
| S9 = 9, | |
| S10 = 10, | |
| S11 = 11, | |
| S12 = 12, | |
| S13 = 13, | |
| S14 = 14, | |
| S15 = 15, | |
| S16 = 16, | |
| S17 = 17, | |
| S18 = 18, | |
| S19 = 19, | |
| S20 = 20, | |
| S21 = 21, | |
| S22 = 22, | |
| S23 = 23, | |
| S24 = 24, | |
| S25 = 25, | |
| S26 = 26, | |
| S27 = 27, | |
| S28 = 28, | |
| S29 = 29, | |
| S30 = 30, | |
| S31 = 31, | |
| kNumberOfSRegisters = 32, | |
| }; | |
| // Values for double-precision floating point registers. | |
| enum DRegister { | |
| kNoDRegister = -1, | |
| D0 = 0, | |
| D1 = 1, | |
| D2 = 2, | |
| D3 = 3, | |
| D4 = 4, | |
| D5 = 5, | |
| D6 = 6, | |
| D7 = 7, | |
| D8 = 8, | |
| D9 = 9, | |
| D10 = 10, | |
| D11 = 11, | |
| D12 = 12, | |
| D13 = 13, | |
| D14 = 14, | |
| D15 = 15, | |
| #if defined(VFPv3_D16) | |
| kNumberOfDRegisters = 16, | |
| // Leaving these defined, but marking them as kNoDRegister to avoid polluting | |
| // other parts of the code with #ifdef's. Instead, query kNumberOfDRegisters | |
| // to see which registers are valid. | |
| D16 = kNoDRegister, | |
| D17 = kNoDRegister, | |
| D18 = kNoDRegister, | |
| D19 = kNoDRegister, | |
| D20 = kNoDRegister, | |
| D21 = kNoDRegister, | |
| D22 = kNoDRegister, | |
| D23 = kNoDRegister, | |
| D24 = kNoDRegister, | |
| D25 = kNoDRegister, | |
| D26 = kNoDRegister, | |
| D27 = kNoDRegister, | |
| D28 = kNoDRegister, | |
| D29 = kNoDRegister, | |
| D30 = kNoDRegister, | |
| D31 = kNoDRegister, | |
| #else | |
| D16 = 16, | |
| D17 = 17, | |
| D18 = 18, | |
| D19 = 19, | |
| D20 = 20, | |
| D21 = 21, | |
| D22 = 22, | |
| D23 = 23, | |
| D24 = 24, | |
| D25 = 25, | |
| D26 = 26, | |
| D27 = 27, | |
| D28 = 28, | |
| D29 = 29, | |
| D30 = 30, | |
| D31 = 31, | |
| kNumberOfDRegisters = 32, | |
| #endif | |
| // Number of D registers that overlap S registers. | |
| // One D register overlaps two S registers, so regardless of the numbers of D | |
| // registers, there are only 32 S registers that are overlapped. | |
| kNumberOfOverlappingDRegisters = 16, | |
| }; | |
| enum QRegister { | |
| kNoQRegister = -1, | |
| Q0 = 0, | |
| Q1 = 1, | |
| Q2 = 2, | |
| Q3 = 3, | |
| Q4 = 4, | |
| Q5 = 5, | |
| Q6 = 6, | |
| Q7 = 7, | |
| #if defined(VFPv3_D16) | |
| kNumberOfQRegisters = 8, | |
| Q8 = kNoQRegister, | |
| Q9 = kNoQRegister, | |
| Q10 = kNoQRegister, | |
| Q11 = kNoQRegister, | |
| Q12 = kNoQRegister, | |
| Q13 = kNoQRegister, | |
| Q14 = kNoQRegister, | |
| Q15 = kNoQRegister, | |
| #else | |
| Q8 = 8, | |
| Q9 = 9, | |
| Q10 = 10, | |
| Q11 = 11, | |
| Q12 = 12, | |
| Q13 = 13, | |
| Q14 = 14, | |
| Q15 = 15, | |
| kNumberOfQRegisters = 16, | |
| #endif | |
| // Number of Q registers that overlap S registers. | |
| // One Q register overlaps four S registers, so regardless of the numbers of Q | |
| // registers, there are only 32 S registers that are overlapped. | |
| kNumberOfOverlappingQRegisters = 8, | |
| }; | |
| static inline DRegister EvenDRegisterOf(QRegister q) { | |
| return static_cast<DRegister>(q * 2); | |
| } | |
| static inline DRegister OddDRegisterOf(QRegister q) { | |
| return static_cast<DRegister>((q * 2) + 1); | |
| } | |
| static inline SRegister EvenSRegisterOf(DRegister d) { | |
| #if defined(VFPv3_D32) | |
| // When we have 32 D registers, the S registers only overlap the first 16. | |
| // That is, there are only ever 32 S registers in any extension. | |
| ASSERT(d < D16); | |
| #endif | |
| return static_cast<SRegister>(d * 2); | |
| } | |
| static inline SRegister OddSRegisterOf(DRegister d) { | |
| #if defined(VFPv3_D32) | |
| ASSERT(d < D16); | |
| #endif | |
| return static_cast<SRegister>((d * 2) + 1); | |
| } | |
| static inline QRegister QRegisterOf(DRegister d) { | |
| return static_cast<QRegister>(d / 2); | |
| } | |
| static inline QRegister QRegisterOf(SRegister s) { | |
| return static_cast<QRegister>(s / 4); | |
| } | |
| static inline DRegister DRegisterOf(SRegister s) { | |
| return static_cast<DRegister>(s / 2); | |
| } | |
| // Register aliases for floating point scratch registers. | |
| const QRegister QTMP = Q7; // Overlaps with DTMP, STMP. | |
| const DRegister DTMP = EvenDRegisterOf(QTMP); // Overlaps with STMP. | |
| const SRegister STMP DART_USED = EvenSRegisterOf(DTMP); | |
| // Architecture independent aliases. | |
| typedef QRegister FpuRegister; | |
| const FpuRegister FpuTMP = QTMP; | |
| const int kFpuRegisterSize = 16; | |
| typedef simd128_value_t fpu_register_t; | |
| const int kNumberOfFpuRegisters = kNumberOfQRegisters; | |
| const FpuRegister kNoFpuRegister = kNoQRegister; | |
| 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]; | |
| extern const char* const fpu_s_reg_names[kNumberOfSRegisters]; | |
| extern const char* const fpu_d_reg_names[kNumberOfDRegisters]; | |
| // Register aliases. | |
| const Register TMP = IP; // Used as scratch register by assembler. | |
| const Register TMP2 = kNoRegister; // There is no second assembler temporary. | |
| const Register PP = R5; // Caches object pool pointer in generated code. | |
| const Register DISPATCH_TABLE_REG = NOTFP; // Dispatch table register. | |
| const Register SPREG = SP; // Stack pointer register. | |
| const Register FPREG = FP; // Frame pointer register. | |
| const Register IC_DATA_REG = R9; // ICData/MegamorphicCache register. | |
| const Register ARGS_DESC_REG = R4; | |
| const Register CODE_REG = R6; | |
| // Set when calling Dart functions in JIT mode, used by LazyCompileStub. | |
| const Register FUNCTION_REG = R0; | |
| const Register THR = R10; // Caches current thread in generated code. | |
| const Register CALLEE_SAVED_TEMP = R8; | |
| // R15 encodes APSR in the vmrs instruction. | |
| const Register APSR = R15; | |
| // 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 = R9; | |
| // Common ABI for shared slow path stubs. | |
| struct SharedSlowPathStubABI { | |
| static constexpr Register kResultReg = R0; | |
| }; | |
| // ABI for instantiation stubs. | |
| struct InstantiationABI { | |
| static constexpr Register kUninstantiatedTypeArgumentsReg = R3; | |
| static constexpr Register kInstantiatorTypeArgumentsReg = R2; | |
| static constexpr Register kFunctionTypeArgumentsReg = R1; | |
| static constexpr Register kResultTypeArgumentsReg = R0; | |
| static constexpr Register kResultTypeReg = R0; | |
| static constexpr Register kScratchReg = R8; | |
| }; | |
| // Registers in addition to those listed in InstantiationABI used inside the | |
| // implementation of the InstantiateTypeArguments stubs. | |
| struct InstantiateTAVInternalRegs { | |
| // The set of registers that must be pushed/popped when probing a hash-based | |
| // cache due to overlap with the registers in InstantiationABI. | |
| static constexpr intptr_t kSavedRegisters = | |
| #if defined(DART_PRECOMPILER) | |
| (1 << DISPATCH_TABLE_REG) | | |
| #endif | |
| (1 << InstantiationABI::kUninstantiatedTypeArgumentsReg); | |
| // Additional registers used to probe hash-based caches. | |
| static constexpr Register kEntryStartReg = R9; | |
| static constexpr Register kProbeMaskReg = R4; | |
| static constexpr Register kProbeDistanceReg = DISPATCH_TABLE_REG; | |
| static constexpr Register kCurrentEntryIndexReg = | |
| InstantiationABI::kUninstantiatedTypeArgumentsReg; | |
| }; | |
| // Registers in addition to those listed in TypeTestABI used inside the | |
| // implementation of type testing stubs that are _not_ preserved. | |
| struct TTSInternalRegs { | |
| static constexpr Register kInstanceTypeArgumentsReg = R4; | |
| static constexpr Register kScratchReg = R9; | |
| static constexpr Register kSubTypeArgumentReg = R3; | |
| static constexpr Register kSuperTypeArgumentReg = R8; | |
| // Must be pushed/popped whenever generic type arguments are being checked as | |
| // they overlap with registers in TypeTestABI. | |
| static constexpr intptr_t kSavedTypeArgumentRegisters = | |
| (1 << kSubTypeArgumentReg) | (1 << kSuperTypeArgumentReg); | |
| static constexpr 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 constexpr Register kInstanceCidOrSignatureReg = R9; | |
| static constexpr intptr_t kInternalRegisters = | |
| (1 << kInstanceCidOrSignatureReg); | |
| }; | |
| // Calling convention when calling TypeTestingStub and SubtypeTestCacheStub. | |
| struct TypeTestABI { | |
| static constexpr Register kInstanceReg = R0; | |
| static constexpr Register kDstTypeReg = R8; | |
| static constexpr Register kInstantiatorTypeArgumentsReg = R2; | |
| static constexpr Register kFunctionTypeArgumentsReg = R1; | |
| static constexpr Register kSubtypeTestCacheReg = R3; | |
| static constexpr Register kScratchReg = R4; | |
| // For calls to SubtypeNTestCacheStub. Must not be the same as any non-scratch | |
| // register above. | |
| static constexpr Register kSubtypeTestCacheResultReg = kScratchReg; | |
| // For calls to InstanceOfStub. | |
| static constexpr Register kInstanceOfResultReg = kInstanceReg; | |
| static constexpr intptr_t kPreservedAbiRegisters = | |
| (1 << kInstanceReg) | (1 << kDstTypeReg) | | |
| (1 << kInstantiatorTypeArgumentsReg) | (1 << kFunctionTypeArgumentsReg); | |
| static constexpr intptr_t kNonPreservedAbiRegisters = | |
| TTSInternalRegs::kInternalRegisters | | |
| STCInternalRegs::kInternalRegisters | (1 << kSubtypeTestCacheReg) | | |
| (1 << kScratchReg) | (1 << kSubtypeTestCacheResultReg) | (1 << CODE_REG); | |
| static constexpr intptr_t kAbiRegisters = | |
| kPreservedAbiRegisters | kNonPreservedAbiRegisters; | |
| }; | |
| // Calling convention when calling AssertSubtypeStub. | |
| struct AssertSubtypeABI { | |
| static constexpr Register kSubTypeReg = R0; | |
| static constexpr Register kSuperTypeReg = R8; | |
| static constexpr Register kInstantiatorTypeArgumentsReg = R2; | |
| static constexpr Register kFunctionTypeArgumentsReg = R1; | |
| static constexpr Register kDstNameReg = R3; | |
| static constexpr 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 constexpr Register kFieldReg = R2; | |
| static constexpr Register kResultReg = R0; | |
| }; | |
| // Registers used inside the implementation of InitLateStaticFieldStub. | |
| struct InitLateStaticFieldInternalRegs { | |
| static constexpr Register kAddressReg = R3; | |
| static constexpr Register kScratchReg = R4; | |
| }; | |
| // ABI for InitInstanceFieldStub. | |
| struct InitInstanceFieldABI { | |
| static constexpr Register kInstanceReg = R1; | |
| static constexpr Register kFieldReg = R2; | |
| static constexpr Register kResultReg = R0; | |
| }; | |
| // Registers used inside the implementation of InitLateInstanceFieldStub. | |
| struct InitLateInstanceFieldInternalRegs { | |
| static constexpr Register kAddressReg = R3; | |
| static constexpr Register kScratchReg = R4; | |
| }; | |
| // ABI for LateInitializationError stubs. | |
| struct LateInitializationErrorABI { | |
| static constexpr Register kFieldReg = R9; | |
| }; | |
| // ABI for FieldAccessError stubs. | |
| struct FieldAccessErrorABI { | |
| static constexpr Register kFieldReg = R9; | |
| }; | |
| // ABI for ThrowStub. | |
| struct ThrowABI { | |
| static constexpr Register kExceptionReg = R0; | |
| }; | |
| // ABI for ReThrowStub. | |
| struct ReThrowABI { | |
| static constexpr Register kExceptionReg = R0; | |
| static constexpr Register kStackTraceReg = R1; | |
| }; | |
| // ABI for RangeErrorStub. | |
| struct RangeErrorABI { | |
| static constexpr Register kLengthReg = R0; | |
| static constexpr Register kIndexReg = R1; | |
| }; | |
| // ABI for AllocateObjectStub. | |
| struct AllocateObjectABI { | |
| static constexpr Register kResultReg = R0; | |
| static constexpr Register kTypeArgumentsReg = R3; | |
| static constexpr Register kTagsReg = R2; | |
| }; | |
| // ABI for AllocateClosureStub. | |
| struct AllocateClosureABI { | |
| static constexpr Register kResultReg = AllocateObjectABI::kResultReg; | |
| static constexpr Register kFunctionReg = R1; | |
| static constexpr Register kLengthAndFlagsReg = R2; | |
| static constexpr Register kContextReg = R3; | |
| static constexpr Register kScratchReg = R4; | |
| }; | |
| // ABI for AllocateMintShared*Stub. | |
| struct AllocateMintABI { | |
| static constexpr Register kResultReg = AllocateObjectABI::kResultReg; | |
| static constexpr Register kTempReg = R1; | |
| }; | |
| // ABI for Allocate{Mint,Double,Float32x4,Float64x2,Int32x4}Stub. | |
| struct AllocateBoxABI { | |
| static constexpr Register kResultReg = AllocateObjectABI::kResultReg; | |
| static constexpr Register kTempReg = R1; | |
| }; | |
| // ABI for AllocateArrayStub. | |
| struct AllocateArrayABI { | |
| static constexpr Register kResultReg = AllocateObjectABI::kResultReg; | |
| static constexpr Register kLengthReg = R2; | |
| static constexpr Register kTypeArgumentsReg = R1; | |
| }; | |
| // ABI for AllocateRecordStub. | |
| struct AllocateRecordABI { | |
| static constexpr Register kResultReg = AllocateObjectABI::kResultReg; | |
| static constexpr Register kShapeReg = R1; | |
| static constexpr Register kTemp1Reg = R2; | |
| static constexpr Register kTemp2Reg = R3; | |
| }; | |
| // ABI for AllocateSmallRecordStub (AllocateRecord2, AllocateRecord2Named, | |
| // AllocateRecord3, AllocateRecord3Named). | |
| struct AllocateSmallRecordABI { | |
| static constexpr Register kResultReg = AllocateObjectABI::kResultReg; | |
| static constexpr Register kShapeReg = R1; | |
| static constexpr Register kValue0Reg = R2; | |
| static constexpr Register kValue1Reg = R3; | |
| static constexpr Register kValue2Reg = R4; | |
| static constexpr Register kTempReg = R9; | |
| }; | |
| // ABI for AllocateTypedDataArrayStub. | |
| struct AllocateTypedDataArrayABI { | |
| static constexpr Register kResultReg = AllocateObjectABI::kResultReg; | |
| static constexpr Register kLengthReg = R4; | |
| }; | |
| // ABI for BoxDoubleStub. | |
| struct BoxDoubleStubABI { | |
| static constexpr FpuRegister kValueReg = Q0; | |
| static constexpr Register kTempReg = R1; | |
| static constexpr Register kResultReg = R0; | |
| }; | |
| // ABI for DoubleToIntegerStub. | |
| struct DoubleToIntegerStubABI { | |
| static constexpr FpuRegister kInputReg = Q0; | |
| static constexpr Register kRecognizedKindReg = R0; | |
| static constexpr Register kResultReg = R0; | |
| }; | |
| // ABI for CheckedStoreIntoSharedStub. | |
| struct CheckedStoreIntoSharedStubABI { | |
| static constexpr Register kFieldReg = R1; | |
| static constexpr Register kValueReg = R2; | |
| static constexpr Register kResultReg = R0; | |
| }; | |
| // ABI for EnsureDeeplyImmutableStub. | |
| struct EnsureDeeplyImmutableStubABI { | |
| static constexpr Register kValueReg = R0; | |
| static constexpr Register kTempReg = R1; | |
| }; | |
| // ABI for SuspendStub (AwaitStub, AwaitWithTypeCheckStub, YieldAsyncStarStub, | |
| // SuspendSyncStarAtStartStub, SuspendSyncStarAtYieldStub). | |
| struct SuspendStubABI { | |
| static constexpr Register kArgumentReg = R0; | |
| static constexpr Register kTypeArgsReg = R1; // Can be the same as kTempReg | |
| static constexpr Register kTempReg = R1; | |
| static constexpr Register kFrameSizeReg = R2; | |
| static constexpr Register kSuspendStateReg = R3; | |
| static constexpr Register kFunctionDataReg = R4; | |
| static constexpr Register kSrcFrameReg = R8; | |
| static constexpr Register kDstFrameReg = R9; | |
| // Number of bytes to skip after | |
| // suspend stub return address in order to resume. | |
| static constexpr intptr_t kResumePcDistance = 0; | |
| }; | |
| // ABI for InitSuspendableFunctionStub (InitAsyncStub, InitAsyncStarStub, | |
| // InitSyncStarStub). | |
| struct InitSuspendableFunctionStubABI { | |
| static constexpr Register kTypeArgsReg = R0; | |
| }; | |
| // ABI for ResumeStub | |
| struct ResumeStubABI { | |
| static constexpr Register kSuspendStateReg = R2; | |
| static constexpr Register kTempReg = R0; | |
| // Registers for the frame copying (the 1st part). | |
| static constexpr Register kFrameSizeReg = R1; | |
| static constexpr Register kSrcFrameReg = R3; | |
| static constexpr Register kDstFrameReg = R4; | |
| // Registers for control transfer. | |
| // (the 2nd part, can reuse registers from the 1st part) | |
| static constexpr Register kResumePcReg = R1; | |
| // Can also reuse kSuspendStateReg but should not conflict with CODE_REG/PP. | |
| static constexpr Register kExceptionReg = R3; | |
| static constexpr Register kStackTraceReg = R4; | |
| }; | |
| // ABI for ReturnStub (ReturnAsyncStub, ReturnAsyncNotFutureStub, | |
| // ReturnAsyncStarStub). | |
| struct ReturnStubABI { | |
| static constexpr Register kSuspendStateReg = R2; | |
| }; | |
| // ABI for AsyncExceptionHandlerStub. | |
| struct AsyncExceptionHandlerStubABI { | |
| static constexpr Register kSuspendStateReg = R2; | |
| }; | |
| // ABI for CloneSuspendStateStub. | |
| struct CloneSuspendStateStubABI { | |
| static constexpr Register kSourceReg = R0; | |
| static constexpr Register kDestinationReg = R1; | |
| static constexpr Register kTempReg = R2; | |
| static constexpr Register kFrameSizeReg = R3; | |
| static constexpr Register kSrcFrameReg = R4; | |
| static constexpr Register kDstFrameReg = R8; | |
| }; | |
| // ABI for FfiAsyncCallbackSendStub. | |
| struct FfiAsyncCallbackSendStubABI { | |
| static constexpr Register kArgsReg = 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 constexpr 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. | |
| // List of registers used in load/store multiple. | |
| typedef uint16_t RegList; | |
| const RegList kAllCpuRegistersList = 0xFFFF; | |
| const RegList kAllFpuRegistersList = (1 << kNumberOfFpuRegisters) - 1; | |
| // C++ ABI call registers. | |
| const RegList kAbiArgumentCpuRegs = | |
| (1 << R0) | (1 << R1) | (1 << R2) | (1 << R3); | |
| const RegList kAbiVolatileCpuRegs = kAbiArgumentCpuRegs | (1 << IP) | (1 << LR); | |
| #if defined(DART_TARGET_OS_MACOS) || defined(DART_TARGET_OS_MACOS_IOS) | |
| const RegList kAbiPreservedCpuRegs = | |
| (1 << R4) | (1 << R5) | (1 << R6) | (1 << R8) | (1 << R10) | (1 << R11); | |
| const int kAbiPreservedCpuRegCount = 6; | |
| #else | |
| const RegList kAbiPreservedCpuRegs = (1 << R4) | (1 << R5) | (1 << R6) | | |
| (1 << R7) | (1 << R8) | (1 << R9) | | |
| (1 << R10); | |
| const int kAbiPreservedCpuRegCount = 7; | |
| #endif | |
| const QRegister kAbiFirstPreservedFpuReg = Q4; | |
| const QRegister kAbiLastPreservedFpuReg = Q7; | |
| const int kAbiPreservedFpuRegCount = 4; | |
| const RegList kReservedCpuRegisters = (1 << SPREG) | (1 << FPREG) | (1 << TMP) | | |
| (1 << PP) | (1 << THR) | (1 << LR) | | |
| (1 << PC) | (1 << NOTFP); | |
| constexpr intptr_t kNumberOfReservedCpuRegisters = | |
| Utils::CountOneBits32(kReservedCpuRegisters); | |
| // CPU registers available to Dart allocator. | |
| constexpr RegList kDartAvailableCpuRegs = | |
| kAllCpuRegistersList & ~kReservedCpuRegisters; | |
| constexpr int kNumberOfDartAvailableCpuRegs = | |
| kNumberOfCpuRegisters - kNumberOfReservedCpuRegisters; | |
| // No reason to prefer certain registers on ARM. | |
| constexpr int kRegisterAllocationBias = 0; | |
| const intptr_t kStoreBufferWrapperSize = 24; | |
| // Registers available to Dart that are not preserved by runtime calls. | |
| const RegList kDartVolatileCpuRegs = | |
| kDartAvailableCpuRegs & ~kAbiPreservedCpuRegs; | |
| #if defined(DART_TARGET_OS_MACOS) || defined(DART_TARGET_OS_MACOS_IOS) | |
| const int kDartVolatileCpuRegCount = 6; | |
| #else | |
| const int kDartVolatileCpuRegCount = 5; | |
| #endif | |
| const RegList kAbiVolatileFpuRegs = R(Q0) | R(Q1) | R(Q2) | R(Q3); | |
| const RegList kFpuRegistersWithoutSOverlap = | |
| kAllFpuRegistersList & | |
| ~((1 << QRegister::kNumberOfOverlappingQRegisters) - 1); | |
| class CallingConventions { | |
| public: | |
| static constexpr intptr_t kArgumentRegisters = kAbiArgumentCpuRegs; | |
| static const Register ArgumentRegisters[]; | |
| static constexpr intptr_t kNumArgRegs = 4; | |
| static constexpr Register kPointerToReturnStructRegisterCall = R0; | |
| static constexpr intptr_t kFpuArgumentRegisters = 0; | |
| static const FpuRegister FpuArgumentRegisters[]; | |
| static constexpr intptr_t kNumFpuArgRegs = 4; | |
| static const DRegister FpuDArgumentRegisters[]; | |
| static constexpr intptr_t kNumDFpuArgRegs = 8; | |
| static const SRegister FpuSArgumentRegisters[]; | |
| static constexpr intptr_t kNumSFpuArgRegs = 16; | |
| static constexpr bool kArgumentIntRegXorFpuReg = false; | |
| static constexpr intptr_t kCalleeSaveCpuRegisters = kAbiPreservedCpuRegs; | |
| // Whether larger than wordsize arguments are aligned to even registers. | |
| static constexpr AlignmentStrategy kArgumentRegisterAlignment = | |
| kAlignedToWordSizeAndValueSize; | |
| static constexpr AlignmentStrategy kArgumentRegisterAlignmentVarArgs = | |
| kArgumentRegisterAlignment; | |
| // How stack arguments are aligned. | |
| static constexpr AlignmentStrategy kArgumentStackAlignment = | |
| kAlignedToWordSizeAndValueSize; | |
| static constexpr AlignmentStrategy kArgumentStackAlignmentVarArgs = | |
| kArgumentStackAlignment; | |
| // How fields in compounds are aligned. | |
| #if defined(DART_TARGET_OS_MACOS_IOS) | |
| static constexpr AlignmentStrategy kFieldAlignment = | |
| kAlignedToValueSizeBut8AlignedTo4; | |
| #else | |
| static constexpr AlignmentStrategy kFieldAlignment = kAlignedToValueSize; | |
| #endif | |
| // Whether 1 or 2 byte-sized arguments or return values are passed extended | |
| // to 4 bytes. | |
| static constexpr ExtensionStrategy kReturnRegisterExtension = kExtendedTo4; | |
| static constexpr ExtensionStrategy kArgumentRegisterExtension = kExtendedTo4; | |
| static constexpr ExtensionStrategy kArgumentStackExtension = kExtendedTo4; | |
| static constexpr Register kReturnReg = R0; | |
| static constexpr Register kSecondReturnReg = R1; | |
| static constexpr FpuRegister kReturnFpuReg = Q0; | |
| static constexpr Register kPointerToReturnStructRegisterReturn = kReturnReg; | |
| // We choose these to avoid overlap between themselves and reserved registers. | |
| static constexpr Register kFirstNonArgumentRegister = R8; | |
| static constexpr Register kSecondNonArgumentRegister = R9; | |
| static constexpr Register kFfiAnyNonAbiRegister = R4; | |
| static constexpr Register kStackPointerRegister = SPREG; | |
| COMPILE_ASSERT( | |
| ((R(kFirstNonArgumentRegister) | R(kSecondNonArgumentRegister)) & | |
| (kArgumentRegisters | R(kPointerToReturnStructRegisterCall))) == 0); | |
| }; | |
| // Register based calling convention used for Dart functions. | |
| // | |
| // See |compiler::ComputeCallingConvention| for more details. | |
| struct DartCallingConvention { | |
| static constexpr Register kCpuRegistersForArgs[] = {R1, R2, R3, R8}; | |
| static constexpr FpuRegister kFpuRegistersForArgs[] = {Q0, Q1, Q2, Q3}; | |
| }; | |
| #undef 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) | |
| kSpecialCondition = 15, // special condition (refer to section A3.2.1) | |
| 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); | |
| ASSERT(c != AL); | |
| ASSERT(c != kSpecialCondition); | |
| ASSERT(c != kInvalidCondition); | |
| return static_cast<Condition>(c ^ 1); | |
| } | |
| // Opcodes for Data-processing instructions (instructions with a type 0 and 1) | |
| // as defined in section A3.4 | |
| enum Opcode { | |
| kNoOperand = -1, | |
| AND = 0, // Logical AND | |
| EOR = 1, // Logical Exclusive OR | |
| SUB = 2, // Subtract | |
| RSB = 3, // Reverse Subtract | |
| ADD = 4, // Add | |
| ADC = 5, // Add with Carry | |
| SBC = 6, // Subtract with Carry | |
| RSC = 7, // Reverse Subtract with Carry | |
| TST = 8, // Test | |
| TEQ = 9, // Test Equivalence | |
| CMP = 10, // Compare | |
| CMN = 11, // Compare Negated | |
| ORR = 12, // Logical (inclusive) OR | |
| MOV = 13, // Move | |
| BIC = 14, // Bit Clear | |
| MVN = 15, // Move Not | |
| kMaxOperand = 16 | |
| }; | |
| // Shifter types for Data-processing operands as defined in section A5.1.2. | |
| 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 | |
| }; | |
| // 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 { | |
| kConditionShift = 28, | |
| kConditionBits = 4, | |
| kTypeShift = 25, | |
| kTypeBits = 3, | |
| kLinkShift = 24, | |
| kLinkBits = 1, | |
| kUShift = 23, | |
| kUBits = 1, | |
| kOpcodeShift = 21, | |
| kOpcodeBits = 4, | |
| kSShift = 20, | |
| kSBits = 1, | |
| kRnShift = 16, | |
| kRnBits = 4, | |
| kRdShift = 12, | |
| kRdBits = 4, | |
| kRsShift = 8, | |
| kRsBits = 4, | |
| kRmShift = 0, | |
| kRmBits = 4, | |
| // Immediate instruction fields encoding. | |
| kRotateShift = 8, | |
| kRotateBits = 4, | |
| kImmed8Shift = 0, | |
| kImmed8Bits = 8, | |
| // Shift instruction register fields encodings. | |
| kShiftImmShift = 7, | |
| kShiftRegisterShift = 8, | |
| kShiftImmBits = 5, | |
| kShiftShift = 5, | |
| kShiftBits = 2, | |
| // Load/store instruction offset field encoding. | |
| kOffset12Shift = 0, | |
| kOffset12Bits = 12, | |
| kOffset12Mask = 0x00000fff, | |
| // Mul instruction register field encodings. | |
| kMulRdShift = 16, | |
| kMulRdBits = 4, | |
| kMulRnShift = 12, | |
| kMulRnBits = 4, | |
| // ldrex/strex register field encodings. | |
| kLdrExRnShift = 16, | |
| kLdrExRtShift = 12, | |
| kStrExRnShift = 16, | |
| kStrExRdShift = 12, | |
| kStrExRtShift = 0, | |
| // Media operation field encodings. | |
| kMediaOp1Shift = 20, | |
| kMediaOp1Bits = 5, | |
| kMediaOp2Shift = 5, | |
| kMediaOp2Bits = 3, | |
| // udiv/sdiv instruction register field encodings. | |
| kDivRdShift = 16, | |
| kDivRdBits = 4, | |
| kDivRmShift = 8, | |
| kDivRmBits = 4, | |
| kDivRnShift = 0, | |
| kDivRnBits = 4, | |
| // sbfx/ubfx instruction register and immediate field encodings. | |
| kBitFieldExtractWidthShift = 16, | |
| kBitFieldExtractWidthBits = 5, | |
| kBitFieldExtractLSBShift = 7, | |
| kBitFieldExtractLSBBits = 5, | |
| kBitFieldExtractRnShift = 0, | |
| kBitFieldExtractRnBits = 4, | |
| // MRC instruction offset field encoding. | |
| kCRmShift = 0, | |
| kCRmBits = 4, | |
| kOpc2Shift = 5, | |
| kOpc2Bits = 3, | |
| kCoprocShift = 8, | |
| kCoprocBits = 4, | |
| kCRnShift = 16, | |
| kCRnBits = 4, | |
| kOpc1Shift = 21, | |
| kOpc1Bits = 3, | |
| kBranchOffsetMask = 0x00ffffff | |
| }; | |
| enum ScaleFactor { | |
| TIMES_1 = 0, | |
| TIMES_2 = 1, | |
| TIMES_4 = 2, | |
| TIMES_8 = 3, | |
| TIMES_16 = 4, | |
| // Don't use (dart::)kWordSizeLog2, as this needs to work for crossword as | |
| // well. If this is included, we know the target is 32 bit. | |
| #if defined(TARGET_ARCH_IS_32_BIT) | |
| // Used for Smi-boxed indices. | |
| TIMES_HALF_WORD_SIZE = kInt32SizeLog2 - 1, | |
| // Used for unboxed indices. | |
| TIMES_WORD_SIZE = kInt32SizeLog2, | |
| #else | |
| #error "Unexpected word size" | |
| #endif | |
| #if !defined(DART_COMPRESSED_POINTERS) | |
| TIMES_COMPRESSED_WORD_SIZE = TIMES_WORD_SIZE, | |
| #else | |
| #error Cannot compress ARM32 | |
| #endif | |
| // Used for Smi-boxed indices. | |
| TIMES_COMPRESSED_HALF_WORD_SIZE = TIMES_COMPRESSED_WORD_SIZE - 1, | |
| }; | |
| // 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 constexpr int32_t kNopInstruction = // nop | |
| ((AL << kConditionShift) | (0x32 << 20) | (0xf << 12)); | |
| static constexpr int32_t kBreakPointCode = 0xdeb0; // For breakpoint. | |
| static constexpr int32_t kSimulatorBreakCode = | |
| 0xdeb2; // For breakpoint in sim. | |
| static constexpr int32_t kSimulatorRedirectCode = 0xca11; // For redirection. | |
| // Breakpoint instruction filling assembler code buffers in debug mode. | |
| static constexpr int32_t kBreakPointInstruction = // bkpt(0xdeb0) | |
| ((AL << kConditionShift) | (0x12 << 20) | (0xdeb << 8) | (0x7 << 4)); | |
| // 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. bkpt(0). | |
| static constexpr int32_t kSimulatorBreakpointInstruction = | |
| // svc #kBreakpointSvcCode | |
| ((AL << kConditionShift) | (0xf << 24) | kSimulatorBreakCode); | |
| // Runtime call redirection instruction used by the simulator. | |
| static constexpr int32_t kSimulatorRedirectInstruction = | |
| ((AL << kConditionShift) | (0xf << 24) | kSimulatorRedirectCode); | |
| // 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; | |
| } | |
| // 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); | |
| } | |
| // Accessors for the different named fields used in the ARM encoding. | |
| // The naming of these accessor corresponds to figure A3-1. | |
| // Generally applicable fields | |
| inline Condition ConditionField() const { | |
| return static_cast<Condition>(Bits(kConditionShift, kConditionBits)); | |
| } | |
| inline int TypeField() const { return Bits(kTypeShift, kTypeBits); } | |
| inline int SubtypeField() const { return Bit(4); } | |
| inline Register RnField() const { | |
| return static_cast<Register>(Bits(kRnShift, kRnBits)); | |
| } | |
| inline Register RdField() const { | |
| return static_cast<Register>(Bits(kRdShift, kRdBits)); | |
| } | |
| // Fields used in Data processing instructions | |
| inline Opcode OpcodeField() const { | |
| return static_cast<Opcode>(Bits(kOpcodeShift, kOpcodeBits)); | |
| } | |
| inline int SField() const { return Bits(kSShift, kSBits); } | |
| // with register | |
| inline Register RmField() const { | |
| return static_cast<Register>(Bits(kRmShift, kRmBits)); | |
| } | |
| inline Shift ShiftField() const { | |
| return static_cast<Shift>(Bits(kShiftShift, kShiftBits)); | |
| } | |
| inline int RegShiftField() const { return Bit(4); } | |
| inline Register RsField() const { | |
| return static_cast<Register>(Bits(kRsShift, kRsBits)); | |
| } | |
| inline int ShiftAmountField() const { | |
| return Bits(kShiftImmShift, kShiftImmBits); | |
| } | |
| // with immediate | |
| inline int RotateField() const { return Bits(kRotateShift, kRotateBits); } | |
| inline int Immed8Field() const { return Bits(kImmed8Shift, kImmed8Bits); } | |
| // Fields used in Load/Store instructions | |
| inline int PUField() const { return Bits(23, 2); } | |
| inline int BField() const { return Bit(22); } | |
| inline int WField() const { return Bit(21); } | |
| inline int LField() const { return Bit(20); } | |
| // with register uses same fields as Data processing instructions above | |
| // with immediate | |
| inline int Offset12Field() const { | |
| return Bits(kOffset12Shift, kOffset12Bits); | |
| } | |
| // multiple | |
| inline int RlistField() const { return Bits(0, 16); } | |
| // extra loads and stores | |
| inline int SignField() const { return Bit(6); } | |
| inline int HField() const { return Bit(5); } | |
| inline int ImmedHField() const { return Bits(8, 4); } | |
| inline int ImmedLField() const { return Bits(0, 4); } | |
| // Fields used in Branch instructions | |
| inline int LinkField() const { return Bits(kLinkShift, kLinkBits); } | |
| inline int32_t SImmed24Field() const { | |
| uint32_t bits = InstructionBits(); | |
| return static_cast<int32_t>(bits << 8) >> 8; | |
| } | |
| // Fields used in Supervisor Call instructions | |
| inline uint32_t SvcField() const { return Bits(0, 24); } | |
| // Field used in Breakpoint instruction | |
| inline uint16_t BkptField() const { | |
| return ((Bits(8, 12) << 4) | Bits(0, 4)); | |
| } | |
| // Field used in 16-bit immediate move instructions | |
| inline uint16_t MovwField() const { | |
| return ((Bits(16, 4) << 12) | Bits(0, 12)); | |
| } | |
| // Field used in VFP float immediate move instruction | |
| inline float ImmFloatField() const { | |
| uint32_t imm32 = (Bit(19) << 31) | (((1 << 5) - Bit(18)) << 25) | | |
| (Bits(16, 2) << 23) | (Bits(0, 4) << 19); | |
| return bit_cast<float, uint32_t>(imm32); | |
| } | |
| // Field used in VFP double immediate move instruction | |
| inline double ImmDoubleField() const { | |
| uint64_t imm64 = (Bit(19) * (1LL << 63)) | (((1LL << 8) - Bit(18)) << 54) | | |
| (Bits(16, 2) * (1LL << 52)) | (Bits(0, 4) * (1LL << 48)); | |
| return bit_cast<double, uint64_t>(imm64); | |
| } | |
| // Shared fields used in media instructions. | |
| inline int MediaOp1Field() const { | |
| return static_cast<Register>(Bits(kMediaOp1Shift, kMediaOp1Bits)); | |
| } | |
| inline int MediaOp2Field() const { | |
| return static_cast<Register>(Bits(kMediaOp2Shift, kMediaOp2Bits)); | |
| } | |
| // Fields used in division instructions. | |
| inline bool IsDivUnsigned() const { return Bit(21) == 0b1; } | |
| inline Register DivRdField() const { | |
| return static_cast<Register>(Bits(kDivRdShift, kDivRdBits)); | |
| } | |
| inline Register DivRmField() const { | |
| return static_cast<Register>(Bits(kDivRmShift, kDivRmBits)); | |
| } | |
| inline Register DivRnField() const { | |
| return static_cast<Register>(Bits(kDivRnShift, kDivRnBits)); | |
| } | |
| // Fields used in bit field extract instructions. | |
| inline bool IsBitFieldExtractSignExtended() const { return Bit(22) == 0; } | |
| inline uint8_t BitFieldExtractWidthField() const { | |
| return Bits(kBitFieldExtractWidthShift, kBitFieldExtractWidthBits); | |
| } | |
| inline uint8_t BitFieldExtractLSBField() const { | |
| return Bits(kBitFieldExtractLSBShift, kBitFieldExtractLSBBits); | |
| } | |
| inline Register BitFieldExtractRnField() const { | |
| return static_cast<Register>( | |
| Bits(kBitFieldExtractRnShift, kBitFieldExtractRnBits)); | |
| } | |
| // Test for data processing instructions of type 0 or 1. | |
| // See "ARM Architecture Reference Manual ARMv7-A and ARMv7-R edition", | |
| // section A5.1 "ARM instruction set encoding". | |
| inline bool IsDataProcessing() const { | |
| ASSERT(ConditionField() != kSpecialCondition); | |
| ASSERT(Bits(26, 2) == 0); // Type 0 or 1. | |
| return ((Bits(20, 5) & 0x19) != 0x10) && | |
| ((Bit(25) == 1) || // Data processing immediate. | |
| (Bit(4) == 0) || // Data processing register. | |
| (Bit(7) == 0)); // Data processing register-shifted register. | |
| } | |
| // Tests for special encodings of type 0 instructions (extra loads and stores, | |
| // as well as multiplications, synchronization primitives, and miscellaneous). | |
| // Can only be called for a type 0 or 1 instruction. | |
| inline bool IsMiscellaneous() const { | |
| ASSERT(Bits(26, 2) == 0); // Type 0 or 1. | |
| return ((Bit(25) == 0) && ((Bits(20, 5) & 0x19) == 0x10) && (Bit(7) == 0)); | |
| } | |
| inline bool IsMultiplyOrSyncPrimitive() const { | |
| ASSERT(Bits(26, 2) == 0); // Type 0 or 1. | |
| return ((Bit(25) == 0) && (Bits(4, 4) == 9)); | |
| } | |
| // Test for Supervisor Call instruction. | |
| inline bool IsSvc() const { | |
| return ((InstructionBits() & 0x0f000000) == 0x0f000000); | |
| } | |
| // Test for Breakpoint instruction. | |
| inline bool IsBkpt() const { | |
| return ((InstructionBits() & 0x0ff000f0) == 0x01200070); | |
| } | |
| // VFP register fields. | |
| inline SRegister SnField() const { | |
| return static_cast<SRegister>((Bits(kRnShift, kRnBits) << 1) + Bit(7)); | |
| } | |
| inline SRegister SdField() const { | |
| return static_cast<SRegister>((Bits(kRdShift, kRdBits) << 1) + Bit(22)); | |
| } | |
| inline SRegister SmField() const { | |
| return static_cast<SRegister>((Bits(kRmShift, kRmBits) << 1) + Bit(5)); | |
| } | |
| inline DRegister DnField() const { | |
| return static_cast<DRegister>(Bits(kRnShift, kRnBits) + (Bit(7) << 4)); | |
| } | |
| inline DRegister DdField() const { | |
| return static_cast<DRegister>(Bits(kRdShift, kRdBits) + (Bit(22) << 4)); | |
| } | |
| inline DRegister DmField() const { | |
| return static_cast<DRegister>(Bits(kRmShift, kRmBits) + (Bit(5) << 4)); | |
| } | |
| inline QRegister QnField() const { | |
| const intptr_t bits = Bits(kRnShift, kRnBits) + (Bit(7) << 4); | |
| return static_cast<QRegister>(bits >> 1); | |
| } | |
| inline QRegister QdField() const { | |
| const intptr_t bits = Bits(kRdShift, kRdBits) + (Bit(22) << 4); | |
| return static_cast<QRegister>(bits >> 1); | |
| } | |
| inline QRegister QmField() const { | |
| const intptr_t bits = Bits(kRmShift, kRmBits) + (Bit(5) << 4); | |
| return static_cast<QRegister>(bits >> 1); | |
| } | |
| // Test for VFP data processing or single transfer instructions of type 7. | |
| inline bool IsVFPDataProcessingOrSingleTransfer() const { | |
| ASSERT(ConditionField() != kSpecialCondition); | |
| ASSERT(TypeField() == 7); | |
| return ((Bit(24) == 0) && (Bits(9, 3) == 5)); | |
| // Bit(4) == 0: Data Processing | |
| // Bit(4) == 1: 8, 16, or 32-bit Transfer between ARM Core and VFP | |
| } | |
| // Test for VFP 64-bit transfer instructions of type 6. | |
| inline bool IsVFPDoubleTransfer() const { | |
| ASSERT(ConditionField() != kSpecialCondition); | |
| ASSERT(TypeField() == 6); | |
| return ((Bits(21, 4) == 2) && (Bits(9, 3) == 5) && | |
| ((Bits(4, 4) & 0xd) == 1)); | |
| } | |
| // Test for VFP load and store instructions of type 6. | |
| inline bool IsVFPLoadStore() const { | |
| ASSERT(ConditionField() != kSpecialCondition); | |
| ASSERT(TypeField() == 6); | |
| return ((Bits(20, 5) & 0x12) == 0x10) && (Bits(9, 3) == 5); | |
| } | |
| // Test for VFP multiple load and store instructions of type 6. | |
| inline bool IsVFPMultipleLoadStore() const { | |
| ASSERT(ConditionField() != kSpecialCondition); | |
| ASSERT(TypeField() == 6); | |
| int32_t puw = (PUField() << 1) | Bit(21); // don't care about D bit | |
| return (Bits(9, 3) == 5) && ((puw == 2) || (puw == 3) || (puw == 5)); | |
| } | |
| inline bool IsSIMDDataProcessing() const { | |
| ASSERT(ConditionField() == kSpecialCondition); | |
| return (Bits(25, 3) == 1); | |
| } | |
| inline bool IsSIMDLoadStore() const { | |
| ASSERT(ConditionField() == kSpecialCondition); | |
| return (Bits(24, 4) == 4) && (Bit(20) == 0); | |
| } | |
| // Tests for media instructions of type 3. | |
| inline bool IsMedia() const { | |
| ASSERT_EQUAL(TypeField(), 3); | |
| return SubtypeField() == 1; | |
| } | |
| inline bool IsDivision() const { | |
| ASSERT(ConditionField() != kSpecialCondition); | |
| ASSERT(IsMedia()); | |
| // B21 determines whether the division is signed or unsigned. | |
| return (((MediaOp1Field() & 0b11101) == 0b10001) && | |
| (MediaOp2Field() == 0b000)); | |
| } | |
| inline bool IsRbit() const { | |
| ASSERT(ConditionField() != kSpecialCondition); | |
| ASSERT(IsMedia()); | |
| // B19-B16 and B11-B8 are always set for rbit. | |
| return ((MediaOp1Field() == 0b01111) && (MediaOp2Field() == 0b001) && | |
| (Bits(8, 4) == 0b1111) && (Bits(16, 4) == 0b1111)); | |
| } | |
| inline bool IsBitFieldExtract() const { | |
| ASSERT(ConditionField() != kSpecialCondition); | |
| ASSERT(IsMedia()); | |
| // B22 determines whether extracted value is sign extended or not, and | |
| // op bits B20 and B7 are part of the width and LSB fields, respectively. | |
| return ((MediaOp1Field() & 0b11010) == 0b11010) && | |
| ((MediaOp2Field() & 0b011) == 0b10); | |
| } | |
| // Special accessors that test for existence of a value. | |
| inline bool HasS() const { return SField() == 1; } | |
| inline bool HasB() const { return BField() == 1; } | |
| inline bool HasW() const { return WField() == 1; } | |
| inline bool HasL() const { return LField() == 1; } | |
| inline bool HasSign() const { return SignField() == 1; } | |
| inline bool HasH() const { return HField() == 1; } | |
| inline bool HasLink() const { return LinkField() == 1; } | |
| // 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); | |
| }; | |
| // Floating-point reciprocal estimate and step (see pages A2-85 and A2-86 of | |
| // ARM Architecture Reference Manual ARMv7-A edition). | |
| float ReciprocalEstimate(float op); | |
| float ReciprocalStep(float op1, float op2); | |
| // Floating-point reciprocal square root estimate and step (see pages A2-87 to | |
| // A2-90 of ARM Architecture Reference Manual ARMv7-A edition). | |
| float ReciprocalSqrtEstimate(float op); | |
| float ReciprocalSqrtStep(float op1, float op2); | |
| constexpr uword kBreakInstructionFiller = 0xE1200070; // bkpt #0 | |
| constexpr uword kDMB_ISH = 0xf57ff05b; // dmb ish | |
| constexpr uword kDMB_ISHST = 0xf57ff05a; // dmb ishst | |
| struct LinkRegister { | |
| const int32_t code = LR; | |
| }; | |
| 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()) | |
| // Prioritize code size over performance. | |
| const intptr_t kPreferredLoopAlignment = 1; | |
| } // namespace dart | |
| #endif // RUNTIME_VM_CONSTANTS_ARM_H_ |