// 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.h" #include "src/codegen/reglist.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 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) R(x28) #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 constexpr int kRegListSizeInBits = sizeof(RegList) * kBitsPerByte; const int kNumRegs = kNumberOfRegisters; // Registers x0-x17 are caller-saved. const int kNumJSCallerSaved = 18; const RegList kJSCallerSaved = 0x3ffff; // Number of registers for which space is reserved in safepoints. Must be a // multiple of eight. // TODO(all): Refine this number. const int kNumSafepointRegisters = 32; // Define the list of registers actually saved at safepoints. // Note that the number of saved registers may be smaller than the reserved // space, i.e. kNumSafepointSavedRegisters <= kNumSafepointRegisters. #define kSafepointSavedRegisters CPURegList::GetSafepointSavedRegisters().list() #define kNumSafepointSavedRegisters \ CPURegList::GetSafepointSavedRegisters().Count() // 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 { kRegister, kVRegister, kNoRegister }; static constexpr CPURegister no_reg() { return CPURegister{0, 0, kNoRegister}; } template static constexpr CPURegister Create() { static_assert(IsValid(code, size, type), "Cannot create invalid registers"); return CPURegister{code, size, type}; } static 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(IsValid()); return reg_size_; } int SizeInBytes() const { DCHECK(IsValid()); DCHECK_EQ(SizeInBits() % 8, 0); return reg_size_ / 8; } bool Is8Bits() const { DCHECK(IsValid()); return reg_size_ == 8; } bool Is16Bits() const { DCHECK(IsValid()); return reg_size_ == 16; } bool Is32Bits() const { DCHECK(IsValid()); return reg_size_ == 32; } bool Is64Bits() const { DCHECK(IsValid()); return reg_size_ == 64; } bool Is128Bits() const { DCHECK(IsValid()); return reg_size_ == 128; } bool IsValid() const { return reg_type_ != kNoRegister; } bool IsNone() const { return reg_type_ == kNoRegister; } bool Is(const CPURegister& other) const { return Aliases(other) && (reg_size_ == other.reg_size_); } bool Aliases(const CPURegister& other) const { return (reg_code_ == other.reg_code_) && (reg_type_ == other.reg_type_); } 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; bool is(const CPURegister& other) const { return Is(other); } bool is_valid() const { return IsValid(); } protected: int 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); class Register : public CPURegister { public: static constexpr Register no_reg() { return Register(CPURegister::no_reg()); } template static constexpr Register Create() { return Register(CPURegister::Create()); } static 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 Register from_code(int code) { // Always return an X register. return Register::Create(code, kXRegSizeInBits); } template static Register from_code() { // Always return an X register. return Register::Create(); } static const char* GetSpecialRegisterName(int code) { return (code == kSPRegInternalCode) ? "sp" : "UNKNOWN"; } private: constexpr explicit Register(const CPURegister& r) : CPURegister(r) {} }; ASSERT_TRIVIALLY_COPYABLE(Register); constexpr bool kPadArguments = true; constexpr bool kSimpleFPAliasing = true; 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(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); } template static constexpr VRegister Create() { static_assert(IsValidLaneCount(lane_count), "Invalid lane count"); return VRegister(CPURegister::Create(), lane_count); } static 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); } 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 VRegister from_code(int code) { // Always return a D register. return VRegister::Create(code, kDRegSizeInBits); } private: int 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); // No*Reg is used to indicate an unused argument, or an error case. Note that // these all compare equal (using the Is() method). 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); // 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. ALIAS_REGISTER(VRegister, fp_fixed1, d28); ALIAS_REGISTER(VRegister, fp_fixed2, d29); // 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. 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; // ----------------------------------------------------------------------------- // Lists of registers. class V8_EXPORT_PRIVATE CPURegList { public: CPURegList() = default; template explicit CPURegList(CPURegister reg0, CPURegisters... regs) : list_(CPURegister::ListOf(reg0, regs...)), size_(reg0.SizeInBits()), type_(reg0.type()) { DCHECK(AreSameSizeAndType(reg0, regs...)); DCHECK(IsValid()); } CPURegList(CPURegister::RegisterType type, int size, RegList list) : list_(list), size_(size), type_(type) { DCHECK(IsValid()); } CPURegList(CPURegister::RegisterType type, int size, int first_reg, int last_reg) : size_(size), type_(type) { DCHECK( ((type == CPURegister::kRegister) && (last_reg < kNumberOfRegisters)) || ((type == CPURegister::kVRegister) && (last_reg < kNumberOfVRegisters))); DCHECK(last_reg >= first_reg); list_ = (1ULL << (last_reg + 1)) - 1; list_ &= ~((1ULL << first_reg) - 1); DCHECK(IsValid()); } CPURegister::RegisterType type() const { DCHECK(IsValid()); return type_; } RegList list() const { DCHECK(IsValid()); return list_; } inline void set_list(RegList new_list) { DCHECK(IsValid()); list_ = new_list; } // Combine another CPURegList into this one. Registers that already exist in // this list are left unchanged. The type and size of the registers in the // 'other' list must match those in this list. void Combine(const CPURegList& other); // Remove every register in the other CPURegList from this one. Registers that // do not exist in this list are ignored. The type of the registers in the // 'other' list must match those in this list. void Remove(const CPURegList& other); // Variants of Combine and Remove which take CPURegisters. void Combine(const CPURegister& other); void Remove(const CPURegister& other1, const CPURegister& other2 = NoCPUReg, const CPURegister& other3 = NoCPUReg, const CPURegister& other4 = NoCPUReg); // Variants of Combine and Remove which take a single register by its code; // the type and size of the register is inferred from this list. void Combine(int code); void Remove(int code); // Remove all callee-saved registers from the list. This can be useful when // preparing registers for an AAPCS64 function call, for example. void RemoveCalleeSaved(); // Align the list to 16 bytes. void Align(); CPURegister PopLowestIndex(); CPURegister PopHighestIndex(); // AAPCS64 callee-saved registers. static CPURegList GetCalleeSaved(int size = kXRegSizeInBits); static CPURegList GetCalleeSavedV(int size = kDRegSizeInBits); // AAPCS64 caller-saved registers. Note that this includes lr. // TODO(all): Determine how we handle d8-d15 being callee-saved, but the top // 64-bits being caller-saved. static CPURegList GetCallerSaved(int size = kXRegSizeInBits); static CPURegList GetCallerSavedV(int size = kDRegSizeInBits); // Registers saved as safepoints. static CPURegList GetSafepointSavedRegisters(); bool IsEmpty() const { DCHECK(IsValid()); return list_ == 0; } bool IncludesAliasOf(const CPURegister& other1, const CPURegister& other2 = NoCPUReg, const CPURegister& other3 = NoCPUReg, const CPURegister& other4 = NoCPUReg) const { DCHECK(IsValid()); RegList list = 0; if (!other1.IsNone() && (other1.type() == type_)) list |= other1.bit(); if (!other2.IsNone() && (other2.type() == type_)) list |= other2.bit(); if (!other3.IsNone() && (other3.type() == type_)) list |= other3.bit(); if (!other4.IsNone() && (other4.type() == type_)) list |= other4.bit(); return (list_ & list) != 0; } int Count() const { DCHECK(IsValid()); return CountSetBits(list_, kRegListSizeInBits); } int RegisterSizeInBits() const { DCHECK(IsValid()); return size_; } int RegisterSizeInBytes() const { int size_in_bits = RegisterSizeInBits(); DCHECK_EQ(size_in_bits % kBitsPerByte, 0); return size_in_bits / kBitsPerByte; } int TotalSizeInBytes() const { DCHECK(IsValid()); return RegisterSizeInBytes() * Count(); } private: RegList list_; int size_; CPURegister::RegisterType type_; bool IsValid() const { constexpr RegList kValidRegisters{0x8000000ffffffff}; constexpr RegList kValidVRegisters{0x0000000ffffffff}; switch (type_) { case CPURegister::kRegister: return (list_ & kValidRegisters) == list_; case CPURegister::kVRegister: return (list_ & kValidVRegisters) == list_; case CPURegister::kNoRegister: return list_ == 0; default: UNREACHABLE(); } } }; // AAPCS64 callee-saved registers. #define kCalleeSaved CPURegList::GetCalleeSaved() #define kCalleeSavedV CPURegList::GetCalleeSavedV() // AAPCS64 caller-saved registers. Note that this includes lr. #define kCallerSaved CPURegList::GetCallerSaved() #define kCallerSavedV CPURegList::GetCallerSavedV() // 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 kSpeculationPoisonRegister = x23; 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; } // namespace internal } // namespace v8 #endif // V8_CODEGEN_ARM64_REGISTER_ARM64_H_