// 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_ARM64_REGISTER_ARM64_H_ #define V8_CODEGEN_ARM64_REGISTER_ARM64_H_ #include "src/codegen/arm64/utils-arm64.h" #include "src/codegen/register-base.h" #include "src/common/globals.h" namespace v8 { namespace internal { // ----------------------------------------------------------------------------- // Registers. // clang-format off #define GENERAL_REGISTER_CODE_LIST(R) \ R(0) R(1) R(2) R(3) R(4) R(5) R(6) R(7) \ R(8) R(9) R(10) R(11) R(12) R(13) R(14) R(15) \ R(16) R(17) R(18) R(19) R(20) R(21) R(22) R(23) \ R(24) R(25) R(26) R(27) R(28) R(29) R(30) R(31) #define GENERAL_REGISTERS(R) \ R(x0) R(x1) R(x2) R(x3) R(x4) R(x5) R(x6) R(x7) \ R(x8) R(x9) R(x10) R(x11) R(x12) R(x13) R(x14) R(x15) \ R(x16) R(x17) R(x18) R(x19) R(x20) R(x21) R(x22) R(x23) \ R(x24) R(x25) R(x26) R(x27) R(x28) R(x29) R(x30) R(x31) // x18 is the platform register and is reserved for the use of platform ABIs. // It is known to be reserved by the OS at least on Windows and iOS. #define ALWAYS_ALLOCATABLE_GENERAL_REGISTERS(R) \ R(x0) R(x1) R(x2) R(x3) R(x4) R(x5) R(x6) R(x7) \ R(x8) R(x9) R(x10) R(x11) R(x12) R(x13) R(x14) R(x15) \ R(x19) R(x20) R(x21) R(x22) R(x23) R(x24) R(x25) \ R(x27) #ifdef V8_COMPRESS_POINTERS_IN_SHARED_CAGE #define MAYBE_ALLOCATABLE_GENERAL_REGISTERS(R) #else #define MAYBE_ALLOCATABLE_GENERAL_REGISTERS(R) R(x28) #endif #define ALLOCATABLE_GENERAL_REGISTERS(V) \ ALWAYS_ALLOCATABLE_GENERAL_REGISTERS(V) \ MAYBE_ALLOCATABLE_GENERAL_REGISTERS(V) #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 DOUBLE_REGISTERS(R) \ R(d0) R(d1) R(d2) R(d3) R(d4) R(d5) R(d6) R(d7) \ R(d8) R(d9) R(d10) R(d11) R(d12) R(d13) R(d14) R(d15) \ R(d16) R(d17) R(d18) R(d19) R(d20) R(d21) R(d22) R(d23) \ R(d24) R(d25) R(d26) R(d27) R(d28) R(d29) R(d30) R(d31) #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) \ V(q16) V(q17) V(q18) V(q19) V(q20) V(q21) V(q22) V(q23) \ V(q24) V(q25) V(q26) V(q27) V(q28) V(q29) V(q30) V(q31) #define VECTOR_REGISTERS(V) \ V(v0) V(v1) V(v2) V(v3) V(v4) V(v5) V(v6) V(v7) \ V(v8) V(v9) V(v10) V(v11) V(v12) V(v13) V(v14) V(v15) \ V(v16) V(v17) V(v18) V(v19) V(v20) V(v21) V(v22) V(v23) \ V(v24) V(v25) V(v26) V(v27) V(v28) V(v29) V(v30) V(v31) // Register d29 could be allocated, but we keep an even length list here, in // order to make stack alignment easier for save and restore. #define ALLOCATABLE_DOUBLE_REGISTERS(R) \ R(d0) R(d1) R(d2) R(d3) R(d4) R(d5) R(d6) R(d7) \ R(d8) R(d9) R(d10) R(d11) R(d12) R(d13) R(d14) R(d16) \ R(d17) R(d18) R(d19) R(d20) R(d21) R(d22) R(d23) R(d24) \ R(d25) R(d26) R(d27) R(d28) // clang-format on // Some CPURegister methods can return Register and VRegister types, so we // need to declare them in advance. class Register; class VRegister; enum RegisterCode { #define REGISTER_CODE(R) kRegCode_##R, GENERAL_REGISTERS(REGISTER_CODE) #undef REGISTER_CODE kRegAfterLast }; class CPURegister : public RegisterBase { public: enum RegisterType : int8_t { kRegister, kVRegister, kNoRegister }; static constexpr CPURegister no_reg() { return CPURegister{kCode_no_reg, 0, kNoRegister}; } static constexpr CPURegister Create(int code, int size, RegisterType type) { DCHECK(IsValid(code, size, type)); return CPURegister{code, size, type}; } RegisterType type() const { return reg_type_; } int SizeInBits() const { DCHECK(is_valid()); return reg_size_; } int SizeInBytes() const { DCHECK(is_valid()); DCHECK_EQ(SizeInBits() % 8, 0); return reg_size_ / 8; } bool Is8Bits() const { DCHECK(is_valid()); return reg_size_ == 8; } bool Is16Bits() const { DCHECK(is_valid()); return reg_size_ == 16; } bool Is32Bits() const { DCHECK(is_valid()); return reg_size_ == 32; } bool Is64Bits() const { DCHECK(is_valid()); return reg_size_ == 64; } bool Is128Bits() const { DCHECK(is_valid()); return reg_size_ == 128; } bool IsNone() const { return reg_type_ == kNoRegister; } constexpr bool Aliases(const CPURegister& other) const { return RegisterBase::operator==(other) && reg_type_ == other.reg_type_; } constexpr bool operator==(const CPURegister& other) const { return RegisterBase::operator==(other) && reg_size_ == other.reg_size_ && reg_type_ == other.reg_type_; } constexpr bool operator!=(const CPURegister& other) const { return !operator==(other); } bool IsZero() const; bool IsSP() const; bool IsRegister() const { return reg_type_ == kRegister; } bool IsVRegister() const { return reg_type_ == kVRegister; } bool IsFPRegister() const { return IsS() || IsD(); } bool IsW() const { return IsRegister() && Is32Bits(); } bool IsX() const { return IsRegister() && Is64Bits(); } // These assertions ensure that the size and type of the register are as // described. They do not consider the number of lanes that make up a vector. // So, for example, Is8B() implies IsD(), and Is1D() implies IsD, but IsD() // does not imply Is1D() or Is8B(). // Check the number of lanes, ie. the format of the vector, using methods such // as Is8B(), Is1D(), etc. in the VRegister class. bool IsV() const { return IsVRegister(); } bool IsB() const { return IsV() && Is8Bits(); } bool IsH() const { return IsV() && Is16Bits(); } bool IsS() const { return IsV() && Is32Bits(); } bool IsD() const { return IsV() && Is64Bits(); } bool IsQ() const { return IsV() && Is128Bits(); } Register Reg() const; VRegister VReg() const; Register X() const; Register W() const; VRegister V() const; VRegister B() const; VRegister H() const; VRegister D() const; VRegister S() const; VRegister Q() const; bool IsSameSizeAndType(const CPURegister& other) const; protected: uint8_t reg_size_; RegisterType reg_type_; #if defined(V8_OS_WIN) && !defined(__clang__) // MSVC has problem to parse template base class as friend class. friend RegisterBase; #else friend class RegisterBase; #endif constexpr CPURegister(int code, int size, RegisterType type) : RegisterBase(code), reg_size_(size), reg_type_(type) {} static constexpr bool IsValidRegister(int code, int size) { return (size == kWRegSizeInBits || size == kXRegSizeInBits) && (code < kNumberOfRegisters || code == kSPRegInternalCode); } static constexpr bool IsValidVRegister(int code, int size) { return (size == kBRegSizeInBits || size == kHRegSizeInBits || size == kSRegSizeInBits || size == kDRegSizeInBits || size == kQRegSizeInBits) && code < kNumberOfVRegisters; } static constexpr bool IsValid(int code, int size, RegisterType type) { return (type == kRegister && IsValidRegister(code, size)) || (type == kVRegister && IsValidVRegister(code, size)); } static constexpr bool IsNone(int code, int size, RegisterType type) { return type == kNoRegister && code == 0 && size == 0; } }; ASSERT_TRIVIALLY_COPYABLE(CPURegister); static_assert(sizeof(CPURegister) <= sizeof(int), "CPURegister can efficiently be passed by value"); class Register : public CPURegister { public: static constexpr Register no_reg() { return Register(CPURegister::no_reg()); } static constexpr Register Create(int code, int size) { return Register(CPURegister::Create(code, size, CPURegister::kRegister)); } static Register XRegFromCode(unsigned code); static Register WRegFromCode(unsigned code); static constexpr Register from_code(int code) { // Always return an X register. return Register::Create(code, kXRegSizeInBits); } static const char* GetSpecialRegisterName(int code) { return (code == kSPRegInternalCode) ? "sp" : "UNKNOWN"; } private: constexpr explicit Register(const CPURegister& r) : CPURegister(r) {} }; ASSERT_TRIVIALLY_COPYABLE(Register); static_assert(sizeof(Register) <= sizeof(int), "Register can efficiently be passed by value"); // Stack frame alignment and padding. constexpr int ArgumentPaddingSlots(int argument_count) { // Stack frames are aligned to 16 bytes. constexpr int kStackFrameAlignment = 16; constexpr int alignment_mask = kStackFrameAlignment / kSystemPointerSize - 1; return argument_count & alignment_mask; } constexpr AliasingKind kFPAliasing = AliasingKind::kOverlap; constexpr bool kSimdMaskRegisters = false; enum DoubleRegisterCode { #define REGISTER_CODE(R) kDoubleCode_##R, DOUBLE_REGISTERS(REGISTER_CODE) #undef REGISTER_CODE kDoubleAfterLast }; // Functions for handling NEON vector format information. enum VectorFormat { kFormatUndefined = 0xffffffff, kFormat8B = NEON_8B, kFormat16B = NEON_16B, kFormat4H = NEON_4H, kFormat8H = NEON_8H, kFormat2S = NEON_2S, kFormat4S = NEON_4S, kFormat1D = NEON_1D, kFormat2D = NEON_2D, // Scalar formats. We add the scalar bit to distinguish between scalar and // vector enumerations; the bit is always set in the encoding of scalar ops // and always clear for vector ops. Although kFormatD and kFormat1D appear // to be the same, their meaning is subtly different. The first is a scalar // operation, the second a vector operation that only affects one lane. kFormatB = NEON_B | NEONScalar, kFormatH = NEON_H | NEONScalar, kFormatS = NEON_S | NEONScalar, kFormatD = NEON_D | NEONScalar }; VectorFormat VectorFormatHalfWidth(VectorFormat vform); VectorFormat VectorFormatDoubleWidth(VectorFormat vform); VectorFormat VectorFormatDoubleLanes(VectorFormat vform); VectorFormat VectorFormatHalfLanes(VectorFormat vform); VectorFormat ScalarFormatFromLaneSize(int lanesize); VectorFormat VectorFormatHalfWidthDoubleLanes(VectorFormat vform); VectorFormat VectorFormatFillQ(int laneSize); VectorFormat VectorFormatFillQ(VectorFormat vform); VectorFormat ScalarFormatFromFormat(VectorFormat vform); V8_EXPORT_PRIVATE unsigned RegisterSizeInBitsFromFormat(VectorFormat vform); unsigned RegisterSizeInBytesFromFormat(VectorFormat vform); int LaneSizeInBytesFromFormat(VectorFormat vform); unsigned LaneSizeInBitsFromFormat(VectorFormat vform); int LaneSizeInBytesLog2FromFormat(VectorFormat vform); V8_EXPORT_PRIVATE int LaneCountFromFormat(VectorFormat vform); int MaxLaneCountFromFormat(VectorFormat vform); V8_EXPORT_PRIVATE bool IsVectorFormat(VectorFormat vform); int64_t MaxIntFromFormat(VectorFormat vform); int64_t MinIntFromFormat(VectorFormat vform); uint64_t MaxUintFromFormat(VectorFormat vform); class VRegister : public CPURegister { public: static constexpr VRegister no_reg() { return VRegister(CPURegister::no_reg(), 0); } static constexpr VRegister Create(int code, int size, int lane_count = 1) { DCHECK(IsValidLaneCount(lane_count)); return VRegister(CPURegister::Create(code, size, CPURegister::kVRegister), lane_count); } static VRegister Create(int reg_code, VectorFormat format) { int reg_size = RegisterSizeInBitsFromFormat(format); int reg_count = IsVectorFormat(format) ? LaneCountFromFormat(format) : 1; return VRegister::Create(reg_code, reg_size, reg_count); } static VRegister BRegFromCode(unsigned code); static VRegister HRegFromCode(unsigned code); static VRegister SRegFromCode(unsigned code); static VRegister DRegFromCode(unsigned code); static VRegister QRegFromCode(unsigned code); static VRegister VRegFromCode(unsigned code); VRegister V8B() const { return VRegister::Create(code(), kDRegSizeInBits, 8); } VRegister V16B() const { return VRegister::Create(code(), kQRegSizeInBits, 16); } VRegister V4H() const { return VRegister::Create(code(), kDRegSizeInBits, 4); } VRegister V8H() const { return VRegister::Create(code(), kQRegSizeInBits, 8); } VRegister V2S() const { return VRegister::Create(code(), kDRegSizeInBits, 2); } VRegister V4S() const { return VRegister::Create(code(), kQRegSizeInBits, 4); } VRegister V2D() const { return VRegister::Create(code(), kQRegSizeInBits, 2); } VRegister V1D() const { return VRegister::Create(code(), kDRegSizeInBits, 1); } VRegister Format(VectorFormat f) const { return VRegister::Create(code(), f); } bool Is8B() const { return (Is64Bits() && (lane_count_ == 8)); } bool Is16B() const { return (Is128Bits() && (lane_count_ == 16)); } bool Is4H() const { return (Is64Bits() && (lane_count_ == 4)); } bool Is8H() const { return (Is128Bits() && (lane_count_ == 8)); } bool Is2S() const { return (Is64Bits() && (lane_count_ == 2)); } bool Is4S() const { return (Is128Bits() && (lane_count_ == 4)); } bool Is1D() const { return (Is64Bits() && (lane_count_ == 1)); } bool Is2D() const { return (Is128Bits() && (lane_count_ == 2)); } // For consistency, we assert the number of lanes of these scalar registers, // even though there are no vectors of equivalent total size with which they // could alias. bool Is1B() const { DCHECK(!(Is8Bits() && IsVector())); return Is8Bits(); } bool Is1H() const { DCHECK(!(Is16Bits() && IsVector())); return Is16Bits(); } bool Is1S() const { DCHECK(!(Is32Bits() && IsVector())); return Is32Bits(); } bool IsLaneSizeB() const { return LaneSizeInBits() == kBRegSizeInBits; } bool IsLaneSizeH() const { return LaneSizeInBits() == kHRegSizeInBits; } bool IsLaneSizeS() const { return LaneSizeInBits() == kSRegSizeInBits; } bool IsLaneSizeD() const { return LaneSizeInBits() == kDRegSizeInBits; } bool IsScalar() const { return lane_count_ == 1; } bool IsVector() const { return lane_count_ > 1; } bool IsSameFormat(const VRegister& other) const { return (reg_size_ == other.reg_size_) && (lane_count_ == other.lane_count_); } int LaneCount() const { return lane_count_; } unsigned LaneSizeInBytes() const { return SizeInBytes() / lane_count_; } unsigned LaneSizeInBits() const { return LaneSizeInBytes() * 8; } static constexpr int kMaxNumRegisters = kNumberOfVRegisters; STATIC_ASSERT(kMaxNumRegisters == kDoubleAfterLast); static constexpr VRegister from_code(int code) { // Always return a D register. return VRegister::Create(code, kDRegSizeInBits); } private: int8_t lane_count_; constexpr explicit VRegister(const CPURegister& r, int lane_count) : CPURegister(r), lane_count_(lane_count) {} static constexpr bool IsValidLaneCount(int lane_count) { return base::bits::IsPowerOfTwo(lane_count) && lane_count <= 16; } }; ASSERT_TRIVIALLY_COPYABLE(VRegister); static_assert(sizeof(VRegister) <= sizeof(int), "VRegister can efficiently be passed by value"); // No*Reg is used to indicate an unused argument, or an error case. Note that // these all compare equal. The Register and VRegister variants are provided for // convenience. constexpr Register NoReg = Register::no_reg(); constexpr VRegister NoVReg = VRegister::no_reg(); constexpr CPURegister NoCPUReg = CPURegister::no_reg(); constexpr Register no_reg = NoReg; constexpr VRegister no_dreg = NoVReg; #define DEFINE_REGISTER(register_class, name, ...) \ constexpr register_class name = register_class::Create(__VA_ARGS__) #define ALIAS_REGISTER(register_class, alias, name) \ constexpr register_class alias = name #define DEFINE_REGISTERS(N) \ DEFINE_REGISTER(Register, w##N, N, kWRegSizeInBits); \ DEFINE_REGISTER(Register, x##N, N, kXRegSizeInBits); GENERAL_REGISTER_CODE_LIST(DEFINE_REGISTERS) #undef DEFINE_REGISTERS DEFINE_REGISTER(Register, wsp, kSPRegInternalCode, kWRegSizeInBits); DEFINE_REGISTER(Register, sp, kSPRegInternalCode, kXRegSizeInBits); #define DEFINE_VREGISTERS(N) \ DEFINE_REGISTER(VRegister, b##N, N, kBRegSizeInBits); \ DEFINE_REGISTER(VRegister, h##N, N, kHRegSizeInBits); \ DEFINE_REGISTER(VRegister, s##N, N, kSRegSizeInBits); \ DEFINE_REGISTER(VRegister, d##N, N, kDRegSizeInBits); \ DEFINE_REGISTER(VRegister, q##N, N, kQRegSizeInBits); \ DEFINE_REGISTER(VRegister, v##N, N, kQRegSizeInBits); GENERAL_REGISTER_CODE_LIST(DEFINE_VREGISTERS) #undef DEFINE_VREGISTERS #undef DEFINE_REGISTER // Registers aliases. ALIAS_REGISTER(VRegister, v8_, v8); // Avoid conflicts with namespace v8. ALIAS_REGISTER(Register, ip0, x16); ALIAS_REGISTER(Register, ip1, x17); ALIAS_REGISTER(Register, wip0, w16); ALIAS_REGISTER(Register, wip1, w17); // Root register. ALIAS_REGISTER(Register, kRootRegister, x26); ALIAS_REGISTER(Register, rr, x26); // Pointer cage base register. #ifdef V8_COMPRESS_POINTERS_IN_SHARED_CAGE ALIAS_REGISTER(Register, kPtrComprCageBaseRegister, x28); #else ALIAS_REGISTER(Register, kPtrComprCageBaseRegister, kRootRegister); #endif // Context pointer register. ALIAS_REGISTER(Register, cp, x27); ALIAS_REGISTER(Register, fp, x29); ALIAS_REGISTER(Register, lr, x30); ALIAS_REGISTER(Register, xzr, x31); ALIAS_REGISTER(Register, wzr, w31); // Register used for padding stack slots. ALIAS_REGISTER(Register, padreg, x31); // Keeps the 0 double value. ALIAS_REGISTER(VRegister, fp_zero, d15); // MacroAssembler fixed V Registers. // d29 is not part of ALLOCATABLE_DOUBLE_REGISTERS, so use 27 and 28. ALIAS_REGISTER(VRegister, fp_fixed1, d27); ALIAS_REGISTER(VRegister, fp_fixed2, d28); // MacroAssembler scratch V registers. ALIAS_REGISTER(VRegister, fp_scratch, d30); ALIAS_REGISTER(VRegister, fp_scratch1, d30); ALIAS_REGISTER(VRegister, fp_scratch2, d31); #undef ALIAS_REGISTER // AreAliased returns true if any of the named registers overlap. Arguments set // to NoReg are ignored. The system stack pointer may be specified. V8_EXPORT_PRIVATE bool AreAliased( const CPURegister& reg1, const CPURegister& reg2, const CPURegister& reg3 = NoReg, const CPURegister& reg4 = NoReg, const CPURegister& reg5 = NoReg, const CPURegister& reg6 = NoReg, const CPURegister& reg7 = NoReg, const CPURegister& reg8 = NoReg); // AreSameSizeAndType returns true if all of the specified registers have the // same size, and are of the same type. The system stack pointer may be // specified. Arguments set to NoReg are ignored, as are any subsequent // arguments. At least one argument (reg1) must be valid (not NoCPUReg). V8_EXPORT_PRIVATE bool AreSameSizeAndType( const CPURegister& reg1, const CPURegister& reg2 = NoCPUReg, const CPURegister& reg3 = NoCPUReg, const CPURegister& reg4 = NoCPUReg, const CPURegister& reg5 = NoCPUReg, const CPURegister& reg6 = NoCPUReg, const CPURegister& reg7 = NoCPUReg, const CPURegister& reg8 = NoCPUReg); // AreSameFormat returns true if all of the specified VRegisters have the same // vector format. Arguments set to NoVReg are ignored, as are any subsequent // arguments. At least one argument (reg1) must be valid (not NoVReg). bool AreSameFormat(const VRegister& reg1, const VRegister& reg2, const VRegister& reg3 = NoVReg, const VRegister& reg4 = NoVReg); // AreConsecutive returns true if all of the specified VRegisters are // consecutive in the register file. Arguments may be set to NoVReg, and if so, // subsequent arguments must also be NoVReg. At least one argument (reg1) must // be valid (not NoVReg). V8_EXPORT_PRIVATE bool AreConsecutive(const VRegister& reg1, const VRegister& reg2, const VRegister& reg3 = NoVReg, const VRegister& reg4 = NoVReg); using FloatRegister = VRegister; using DoubleRegister = VRegister; using Simd128Register = VRegister; // Define a {RegisterName} method for {Register} and {VRegister}. DEFINE_REGISTER_NAMES(Register, GENERAL_REGISTERS) DEFINE_REGISTER_NAMES(VRegister, VECTOR_REGISTERS) // Give alias names to registers for calling conventions. constexpr Register kReturnRegister0 = x0; constexpr Register kReturnRegister1 = x1; constexpr Register kReturnRegister2 = x2; constexpr Register kJSFunctionRegister = x1; constexpr Register kContextRegister = cp; constexpr Register kAllocateSizeRegister = x1; constexpr Register kInterpreterAccumulatorRegister = x0; constexpr Register kInterpreterBytecodeOffsetRegister = x19; constexpr Register kInterpreterBytecodeArrayRegister = x20; constexpr Register kInterpreterDispatchTableRegister = x21; constexpr Register kJavaScriptCallArgCountRegister = x0; constexpr Register kJavaScriptCallCodeStartRegister = x2; constexpr Register kJavaScriptCallTargetRegister = kJSFunctionRegister; constexpr Register kJavaScriptCallNewTargetRegister = x3; constexpr Register kJavaScriptCallExtraArg1Register = x2; constexpr Register kOffHeapTrampolineRegister = ip0; constexpr Register kRuntimeCallFunctionRegister = x1; constexpr Register kRuntimeCallArgCountRegister = x0; constexpr Register kRuntimeCallArgvRegister = x11; constexpr Register kWasmInstanceRegister = x7; constexpr Register kWasmCompileLazyFuncIndexRegister = x8; constexpr DoubleRegister kFPReturnRegister0 = d0; } // namespace internal } // namespace v8 #endif // V8_CODEGEN_ARM64_REGISTER_ARM64_H_