blob: ebdcee3742356a73639d334b5352e6a8e91ef3f1 [file] [log] [blame] [edit]
// 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 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 kContextReg = R2;
static constexpr Register kInstantiatorTypeArgsReg = 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}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 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 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())
// Prioritize code size over performance.
const intptr_t kPreferredLoopAlignment = 1;
} // namespace dart
#endif // RUNTIME_VM_CONSTANTS_ARM_H_