| // 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 "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(__ARM_ARCH_7A__) |
| #define VFPv3_D32 |
| #elif defined(TARGET_ARCH_ARM) && !defined(HOST_ARCH_ARM) |
| // If we're running in the simulator, use all 32. |
| #define VFPv3_D32 |
| #else |
| #define VFPv3_D16 |
| #endif |
| #if defined(VFPv3_D16) == defined(VFPv3_D32) |
| #error "Exactly one of VFPv3_D16 or VFPv3_D32 can be defined at a time." |
| #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 |
| R7 = 7, // iOS FP |
| R8 = 8, |
| R9 = 9, |
| R10 = 10, // THR |
| R11 = 11, // Linux/Android/Windows FP |
| 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 ARGS_DESC_REG = R4; |
| const Register CODE_REG = R6; |
| 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 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 = R4; |
| static const Register kScratchReg = R9; |
| static const Register kSubTypeArgumentReg = R3; |
| static const Register kSuperTypeArgumentReg = R8; |
| |
| // Must be pushed/popped whenever generic type arguments are being checked as |
| // they overlap with registers in TypeTestABI. |
| static const intptr_t kSavedTypeArgumentRegisters = |
| (1 << kSubTypeArgumentReg) | (1 << kSuperTypeArgumentReg); |
| |
| 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 = R9; |
| |
| static const intptr_t kInternalRegisters = (1 << kInstanceCidOrSignatureReg); |
| }; |
| |
| // 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 be saved by the caller if the |
| // original value is needed after the call. |
| static const Register kSubtypeTestCacheResultReg = kSubtypeTestCacheReg; |
| |
| // 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 = R3; |
| 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 = Q0; |
| static const Register kTempReg = R1; |
| static const Register kResultReg = R0; |
| }; |
| |
| // ABI for DoubleToIntegerStub. |
| struct DoubleToIntegerStubABI { |
| static const FpuRegister kInputReg = Q0; |
| 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. |
| |
| // 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); |
| #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 = 8; |
| // CPU registers available to Dart allocator. |
| constexpr RegList kDartAvailableCpuRegs = |
| kAllCpuRegistersList & ~kReservedCpuRegisters; |
| constexpr int kNumberOfDartAvailableCpuRegs = |
| kNumberOfCpuRegisters - kNumberOfReservedCpuRegisters; |
| 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 const intptr_t kArgumentRegisters = kAbiArgumentCpuRegs; |
| static const Register ArgumentRegisters[]; |
| static const intptr_t kNumArgRegs = 4; |
| static const Register kPointerToReturnStructRegisterCall = R0; |
| |
| static const intptr_t kFpuArgumentRegisters = 0; |
| |
| static const FpuRegister FpuArgumentRegisters[]; |
| static const intptr_t kNumFpuArgRegs = 4; |
| static const DRegister FpuDArgumentRegisters[]; |
| static const intptr_t kNumDFpuArgRegs = 8; |
| static const SRegister FpuSArgumentRegisters[]; |
| static const 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 = |
| kAlignedToWordSizeBut8AlignedTo8; |
| |
| // How stack arguments are aligned. |
| static constexpr AlignmentStrategy kArgumentStackAlignment = |
| kAlignedToWordSizeBut8AlignedTo8; |
| |
| // 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); |
| }; |
| |
| #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 |
| }; |
| |
| // The class Instr enables access to individual fields defined in the ARM |
| // architecture instruction set encoding as described in figure A3-1. |
| // |
| // Example: Test whether the instruction at ptr sets the condition code bits. |
| // |
| // bool InstructionSetsConditionCodes(byte* ptr) { |
| // Instr* instr = Instr::At(ptr); |
| // int type = instr->TypeField(); |
| // return ((type == 0) || (type == 1)) && instr->HasS(); |
| // } |
| // |
| class Instr { |
| public: |
| enum { kInstrSize = 4, kInstrSizeLog2 = 2, kPCReadOffset = 8 }; |
| |
| static const int32_t kNopInstruction = // nop |
| ((AL << kConditionShift) | (0x32 << 20) | (0xf << 12)); |
| |
| 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 = // 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 const int32_t kSimulatorBreakpointInstruction = |
| // svc #kBreakpointSvcCode |
| ((AL << kConditionShift) | (0xf << 24) | kSimulatorBreakCode); |
| |
| // Runtime call redirection instruction used by the simulator. |
| static const 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 kDataMemoryBarrier = 0xf57ff050 | 0xb; // dmb ish |
| |
| 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()) |
| |
| } // namespace dart |
| |
| #endif // RUNTIME_VM_CONSTANTS_ARM_H_ |