// Copyright 2018 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef V8_CODEGEN_ARM_REGISTER_ARM_H_ #define V8_CODEGEN_ARM_REGISTER_ARM_H_ #include "src/codegen/register.h" #include "src/codegen/reglist.h" namespace v8 { namespace internal { // clang-format off #define GENERAL_REGISTERS(V) \ V(r0) V(r1) V(r2) V(r3) V(r4) V(r5) V(r6) V(r7) \ V(r8) V(r9) V(r10) V(fp) V(ip) V(sp) V(lr) V(pc) #define ALLOCATABLE_GENERAL_REGISTERS(V) \ V(r0) V(r1) V(r2) V(r3) V(r4) V(r5) V(r6) V(r7) \ V(r8) V(r9) #define FLOAT_REGISTERS(V) \ V(s0) V(s1) V(s2) V(s3) V(s4) V(s5) V(s6) V(s7) \ V(s8) V(s9) V(s10) V(s11) V(s12) V(s13) V(s14) V(s15) \ V(s16) V(s17) V(s18) V(s19) V(s20) V(s21) V(s22) V(s23) \ V(s24) V(s25) V(s26) V(s27) V(s28) V(s29) V(s30) V(s31) #define LOW_DOUBLE_REGISTERS(V) \ V(d0) V(d1) V(d2) V(d3) V(d4) V(d5) V(d6) V(d7) \ V(d8) V(d9) V(d10) V(d11) V(d12) V(d13) V(d14) V(d15) #define NON_LOW_DOUBLE_REGISTERS(V) \ V(d16) V(d17) V(d18) V(d19) V(d20) V(d21) V(d22) V(d23) \ V(d24) V(d25) V(d26) V(d27) V(d28) V(d29) V(d30) V(d31) #define DOUBLE_REGISTERS(V) \ LOW_DOUBLE_REGISTERS(V) NON_LOW_DOUBLE_REGISTERS(V) #define SIMD128_REGISTERS(V) \ V(q0) V(q1) V(q2) V(q3) V(q4) V(q5) V(q6) V(q7) \ V(q8) V(q9) V(q10) V(q11) V(q12) V(q13) V(q14) V(q15) #define ALLOCATABLE_DOUBLE_REGISTERS(V) \ V(d0) V(d1) V(d2) V(d3) V(d4) V(d5) V(d6) V(d7) \ V(d8) V(d9) V(d10) V(d11) V(d12) \ V(d16) V(d17) V(d18) V(d19) V(d20) V(d21) V(d22) V(d23) \ V(d24) V(d25) V(d26) V(d27) V(d28) V(d29) V(d30) V(d31) #define ALLOCATABLE_NO_VFP32_DOUBLE_REGISTERS(V) \ V(d0) V(d1) V(d2) V(d3) V(d4) V(d5) V(d6) V(d7) \ V(d8) V(d9) V(d10) V(d11) V(d12) V(d15) #define C_REGISTERS(V) \ V(cr0) V(cr1) V(cr2) V(cr3) V(cr4) V(cr5) V(cr6) V(cr7) \ V(cr8) V(cr9) V(cr10) V(cr11) V(cr12) V(cr15) // clang-format on // The ARM ABI does not specify the usage of register r9, which may be reserved // as the static base or thread register on some platforms, in which case we // leave it alone. Adjust the value of kR9Available accordingly: const int kR9Available = 1; // 1 if available to us, 0 if reserved // Register list in load/store instructions // Note that the bit values must match those used in actual instruction encoding // Caller-saved/arguments registers const RegList kJSCallerSaved = 1 << 0 | // r0 a1 1 << 1 | // r1 a2 1 << 2 | // r2 a3 1 << 3; // r3 a4 const int kNumJSCallerSaved = 4; // Callee-saved registers preserved when switching from C to JavaScript const RegList kCalleeSaved = 1 << 4 | // r4 v1 1 << 5 | // r5 v2 1 << 6 | // r6 v3 1 << 7 | // r7 v4 (cp in JavaScript code) 1 << 8 | // r8 v5 (pp in JavaScript code) kR9Available << 9 | // r9 v6 1 << 10 | // r10 v7 1 << 11; // r11 v8 (fp in JavaScript code) // When calling into C++ (only for C++ calls that can't cause a GC). // The call code will take care of lr, fp, etc. const RegList kCallerSaved = 1 << 0 | // r0 1 << 1 | // r1 1 << 2 | // r2 1 << 3 | // r3 1 << 9; // r9 const int kNumCalleeSaved = 7 + kR9Available; // Double registers d8 to d15 are callee-saved. const int kNumDoubleCalleeSaved = 8; enum RegisterCode { #define REGISTER_CODE(R) kRegCode_##R, GENERAL_REGISTERS(REGISTER_CODE) #undef REGISTER_CODE kRegAfterLast }; class Register : public RegisterBase { friend class RegisterBase; explicit constexpr Register(int code) : RegisterBase(code) {} }; ASSERT_TRIVIALLY_COPYABLE(Register); static_assert(sizeof(Register) == sizeof(int), "Register can efficiently be passed by value"); // r7: context register #define DECLARE_REGISTER(R) \ constexpr Register R = Register::from_code(kRegCode_##R); GENERAL_REGISTERS(DECLARE_REGISTER) #undef DECLARE_REGISTER constexpr Register no_reg = Register::no_reg(); constexpr bool kPadArguments = false; constexpr bool kSimpleFPAliasing = false; constexpr bool kSimdMaskRegisters = false; enum SwVfpRegisterCode { #define REGISTER_CODE(R) kSwVfpCode_##R, FLOAT_REGISTERS(REGISTER_CODE) #undef REGISTER_CODE kSwVfpAfterLast }; // Representation of a list of non-overlapping VFP registers. This list // represents the data layout of VFP registers as a bitfield: // S registers cover 1 bit // D registers cover 2 bits // Q registers cover 4 bits // // This way, we make sure no registers in the list ever overlap. However, a list // may represent multiple different sets of registers, // e.g. [d0 s2 s3] <=> [s0 s1 d1]. using VfpRegList = uint64_t; // Single word VFP register. class SwVfpRegister : public RegisterBase { public: static constexpr int kSizeInBytes = 4; static void split_code(int reg_code, int* vm, int* m) { DCHECK(from_code(reg_code).is_valid()); *m = reg_code & 0x1; *vm = reg_code >> 1; } void split_code(int* vm, int* m) const { split_code(code(), vm, m); } VfpRegList ToVfpRegList() const { DCHECK(is_valid()); // Each bit in the list corresponds to a S register. return uint64_t{0x1} << code(); } private: friend class RegisterBase; explicit constexpr SwVfpRegister(int code) : RegisterBase(code) {} }; ASSERT_TRIVIALLY_COPYABLE(SwVfpRegister); static_assert(sizeof(SwVfpRegister) == sizeof(int), "SwVfpRegister can efficiently be passed by value"); using FloatRegister = SwVfpRegister; enum DoubleRegisterCode { #define REGISTER_CODE(R) kDoubleCode_##R, DOUBLE_REGISTERS(REGISTER_CODE) #undef REGISTER_CODE kDoubleAfterLast }; // Double word VFP register. class DwVfpRegister : public RegisterBase { public: static constexpr int kSizeInBytes = 8; // This function differs from kNumRegisters by returning the number of double // registers supported by the current CPU, while kNumRegisters always returns // 32. inline static int SupportedRegisterCount(); static void split_code(int reg_code, int* vm, int* m) { DCHECK(from_code(reg_code).is_valid()); *m = (reg_code & 0x10) >> 4; *vm = reg_code & 0x0F; } void split_code(int* vm, int* m) const { split_code(code(), vm, m); } VfpRegList ToVfpRegList() const { DCHECK(is_valid()); // A D register overlaps two S registers. return uint64_t{0x3} << (code() * 2); } private: friend class RegisterBase; friend class LowDwVfpRegister; explicit constexpr DwVfpRegister(int code) : RegisterBase(code) {} }; ASSERT_TRIVIALLY_COPYABLE(DwVfpRegister); static_assert(sizeof(DwVfpRegister) == sizeof(int), "DwVfpRegister can efficiently be passed by value"); using DoubleRegister = DwVfpRegister; // Double word VFP register d0-15. class LowDwVfpRegister : public RegisterBase { public: constexpr operator DwVfpRegister() const { return DwVfpRegister(code()); } SwVfpRegister low() const { return SwVfpRegister::from_code(code() * 2); } SwVfpRegister high() const { return SwVfpRegister::from_code(code() * 2 + 1); } VfpRegList ToVfpRegList() const { DCHECK(is_valid()); // A D register overlaps two S registers. return uint64_t{0x3} << (code() * 2); } private: friend class RegisterBase; explicit constexpr LowDwVfpRegister(int code) : RegisterBase(code) {} }; enum Simd128RegisterCode { #define REGISTER_CODE(R) kSimd128Code_##R, SIMD128_REGISTERS(REGISTER_CODE) #undef REGISTER_CODE kSimd128AfterLast }; // Quad word NEON register. class QwNeonRegister : public RegisterBase { public: static void split_code(int reg_code, int* vm, int* m) { DCHECK(from_code(reg_code).is_valid()); int encoded_code = reg_code << 1; *m = (encoded_code & 0x10) >> 4; *vm = encoded_code & 0x0F; } void split_code(int* vm, int* m) const { split_code(code(), vm, m); } DwVfpRegister low() const { return DwVfpRegister::from_code(code() * 2); } DwVfpRegister high() const { return DwVfpRegister::from_code(code() * 2 + 1); } VfpRegList ToVfpRegList() const { DCHECK(is_valid()); // A Q register overlaps four S registers. return uint64_t{0xf} << (code() * 4); } private: friend class RegisterBase; explicit constexpr QwNeonRegister(int code) : RegisterBase(code) {} }; using QuadRegister = QwNeonRegister; using Simd128Register = QwNeonRegister; enum CRegisterCode { #define REGISTER_CODE(R) kCCode_##R, C_REGISTERS(REGISTER_CODE) #undef REGISTER_CODE kCAfterLast }; // Coprocessor register class CRegister : public RegisterBase { friend class RegisterBase; explicit constexpr CRegister(int code) : RegisterBase(code) {} }; // Support for the VFP registers s0 to s31 (d0 to d15). // Note that "s(N):s(N+1)" is the same as "d(N/2)". #define DECLARE_FLOAT_REGISTER(R) \ constexpr SwVfpRegister R = SwVfpRegister::from_code(kSwVfpCode_##R); FLOAT_REGISTERS(DECLARE_FLOAT_REGISTER) #undef DECLARE_FLOAT_REGISTER #define DECLARE_LOW_DOUBLE_REGISTER(R) \ constexpr LowDwVfpRegister R = LowDwVfpRegister::from_code(kDoubleCode_##R); LOW_DOUBLE_REGISTERS(DECLARE_LOW_DOUBLE_REGISTER) #undef DECLARE_LOW_DOUBLE_REGISTER #define DECLARE_DOUBLE_REGISTER(R) \ constexpr DwVfpRegister R = DwVfpRegister::from_code(kDoubleCode_##R); NON_LOW_DOUBLE_REGISTERS(DECLARE_DOUBLE_REGISTER) #undef DECLARE_DOUBLE_REGISTER constexpr DwVfpRegister no_dreg = DwVfpRegister::no_reg(); #define DECLARE_SIMD128_REGISTER(R) \ constexpr Simd128Register R = Simd128Register::from_code(kSimd128Code_##R); SIMD128_REGISTERS(DECLARE_SIMD128_REGISTER) #undef DECLARE_SIMD128_REGISTER // Aliases for double registers. constexpr LowDwVfpRegister kFirstCalleeSavedDoubleReg = d8; constexpr LowDwVfpRegister kLastCalleeSavedDoubleReg = d15; constexpr LowDwVfpRegister kDoubleRegZero = d13; constexpr CRegister no_creg = CRegister::no_reg(); #define DECLARE_C_REGISTER(R) \ constexpr CRegister R = CRegister::from_code(kCCode_##R); C_REGISTERS(DECLARE_C_REGISTER) #undef DECLARE_C_REGISTER // Define {RegisterName} methods for the register types. DEFINE_REGISTER_NAMES(Register, GENERAL_REGISTERS) DEFINE_REGISTER_NAMES(SwVfpRegister, FLOAT_REGISTERS) DEFINE_REGISTER_NAMES(DwVfpRegister, DOUBLE_REGISTERS) DEFINE_REGISTER_NAMES(LowDwVfpRegister, LOW_DOUBLE_REGISTERS) DEFINE_REGISTER_NAMES(QwNeonRegister, SIMD128_REGISTERS) DEFINE_REGISTER_NAMES(CRegister, C_REGISTERS) // Give alias names to registers for calling conventions. constexpr Register kReturnRegister0 = r0; constexpr Register kReturnRegister1 = r1; constexpr Register kReturnRegister2 = r2; constexpr Register kJSFunctionRegister = r1; constexpr Register kContextRegister = r7; constexpr Register kAllocateSizeRegister = r1; constexpr Register kSpeculationPoisonRegister = r9; constexpr Register kInterpreterAccumulatorRegister = r0; constexpr Register kInterpreterBytecodeOffsetRegister = r5; constexpr Register kInterpreterBytecodeArrayRegister = r6; constexpr Register kInterpreterDispatchTableRegister = r8; constexpr Register kJavaScriptCallArgCountRegister = r0; constexpr Register kJavaScriptCallCodeStartRegister = r2; constexpr Register kJavaScriptCallTargetRegister = kJSFunctionRegister; constexpr Register kJavaScriptCallNewTargetRegister = r3; constexpr Register kJavaScriptCallExtraArg1Register = r2; constexpr Register kOffHeapTrampolineRegister = ip; constexpr Register kRuntimeCallFunctionRegister = r1; constexpr Register kRuntimeCallArgCountRegister = r0; constexpr Register kRuntimeCallArgvRegister = r2; constexpr Register kWasmInstanceRegister = r3; constexpr Register kWasmCompileLazyFuncIndexRegister = r4; // Give alias names to registers constexpr Register cp = r7; // JavaScript context pointer. constexpr Register kRootRegister = r10; // Roots array pointer. constexpr DoubleRegister kFPReturnRegister0 = d0; } // namespace internal } // namespace v8 #endif // V8_CODEGEN_ARM_REGISTER_ARM_H_