// Copyright 2016, VIXL authors // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // * Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // * Neither the name of ARM Limited nor the names of its contributors may be // used to endorse or promote products derived from this software without // specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef VIXL_AARCH64_OPERANDS_AARCH64_H_ #define VIXL_AARCH64_OPERANDS_AARCH64_H_ #include "instructions-aarch64.h" namespace vixl { namespace aarch64 { typedef uint64_t RegList; static const int kRegListSizeInBits = sizeof(RegList) * 8; // Registers. // Some CPURegister methods can return Register or VRegister types, so we need // to declare them in advance. class Register; class VRegister; class CPURegister { public: enum RegisterType { // The kInvalid value is used to detect uninitialized static instances, // which are always zero-initialized before any constructors are called. kInvalid = 0, kRegister, kVRegister, kFPRegister = kVRegister, kNoRegister }; CPURegister() : code_(0), size_(0), type_(kNoRegister) { VIXL_ASSERT(!IsValid()); VIXL_ASSERT(IsNone()); } CPURegister(unsigned code, unsigned size, RegisterType type) : code_(code), size_(size), type_(type) { VIXL_ASSERT(IsValidOrNone()); } unsigned GetCode() const { VIXL_ASSERT(IsValid()); return code_; } VIXL_DEPRECATED("GetCode", unsigned code() const) { return GetCode(); } RegisterType GetType() const { VIXL_ASSERT(IsValidOrNone()); return type_; } VIXL_DEPRECATED("GetType", RegisterType type() const) { return GetType(); } RegList GetBit() const { VIXL_ASSERT(code_ < (sizeof(RegList) * 8)); return IsValid() ? (static_cast(1) << code_) : 0; } VIXL_DEPRECATED("GetBit", RegList Bit() const) { return GetBit(); } int GetSizeInBytes() const { VIXL_ASSERT(IsValid()); VIXL_ASSERT(size_ % 8 == 0); return size_ / 8; } VIXL_DEPRECATED("GetSizeInBytes", int SizeInBytes() const) { return GetSizeInBytes(); } int GetSizeInBits() const { VIXL_ASSERT(IsValid()); return size_; } VIXL_DEPRECATED("GetSizeInBits", unsigned size() const) { return GetSizeInBits(); } VIXL_DEPRECATED("GetSizeInBits", int SizeInBits() const) { return GetSizeInBits(); } bool Is8Bits() const { VIXL_ASSERT(IsValid()); return size_ == 8; } bool Is16Bits() const { VIXL_ASSERT(IsValid()); return size_ == 16; } bool Is32Bits() const { VIXL_ASSERT(IsValid()); return size_ == 32; } bool Is64Bits() const { VIXL_ASSERT(IsValid()); return size_ == 64; } bool Is128Bits() const { VIXL_ASSERT(IsValid()); return size_ == 128; } bool IsValid() const { if (IsValidRegister() || IsValidVRegister()) { VIXL_ASSERT(!IsNone()); return true; } else { // This assert is hit when the register has not been properly initialized. // One cause for this can be an initialisation order fiasco. See // https://isocpp.org/wiki/faq/ctors#static-init-order for some details. VIXL_ASSERT(IsNone()); return false; } } bool IsValidRegister() const { return IsRegister() && ((size_ == kWRegSize) || (size_ == kXRegSize)) && ((code_ < kNumberOfRegisters) || (code_ == kSPRegInternalCode)); } bool IsValidVRegister() const { return IsVRegister() && ((size_ == kBRegSize) || (size_ == kHRegSize) || (size_ == kSRegSize) || (size_ == kDRegSize) || (size_ == kQRegSize)) && (code_ < kNumberOfVRegisters); } bool IsValidFPRegister() const { return IsFPRegister() && (code_ < kNumberOfVRegisters); } bool IsNone() const { // kNoRegister types should always have size 0 and code 0. VIXL_ASSERT((type_ != kNoRegister) || (code_ == 0)); VIXL_ASSERT((type_ != kNoRegister) || (size_ == 0)); return type_ == kNoRegister; } bool Aliases(const CPURegister& other) const { VIXL_ASSERT(IsValidOrNone() && other.IsValidOrNone()); return (code_ == other.code_) && (type_ == other.type_); } bool Is(const CPURegister& other) const { VIXL_ASSERT(IsValidOrNone() && other.IsValidOrNone()); return Aliases(other) && (size_ == other.size_); } bool IsZero() const { VIXL_ASSERT(IsValid()); return IsRegister() && (code_ == kZeroRegCode); } bool IsSP() const { VIXL_ASSERT(IsValid()); return IsRegister() && (code_ == kSPRegInternalCode); } bool IsRegister() const { return type_ == kRegister; } bool IsVRegister() const { return type_ == kVRegister; } bool IsFPRegister() const { return IsS() || IsD(); } bool IsW() const { return IsValidRegister() && Is32Bits(); } bool IsX() const { return IsValidRegister() && 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(); } // Semantic type for sdot and udot instructions. bool IsS4B() const { return IsS(); } const VRegister& S4B() const { return S(); } const Register& W() const; const Register& X() const; const VRegister& V() const; const VRegister& B() const; const VRegister& H() const; const VRegister& S() const; const VRegister& D() const; const VRegister& Q() const; bool IsSameType(const CPURegister& other) const { return type_ == other.type_; } bool IsSameSizeAndType(const CPURegister& other) const { return (size_ == other.size_) && IsSameType(other); } protected: unsigned code_; int size_; RegisterType type_; private: bool IsValidOrNone() const { return IsValid() || IsNone(); } }; class Register : public CPURegister { public: Register() : CPURegister() {} explicit Register(const CPURegister& other) : CPURegister(other.GetCode(), other.GetSizeInBits(), other.GetType()) { VIXL_ASSERT(IsValidRegister()); } Register(unsigned code, unsigned size) : CPURegister(code, size, kRegister) {} bool IsValid() const { VIXL_ASSERT(IsRegister() || IsNone()); return IsValidRegister(); } static const Register& GetWRegFromCode(unsigned code); VIXL_DEPRECATED("GetWRegFromCode", static const Register& WRegFromCode(unsigned code)) { return GetWRegFromCode(code); } static const Register& GetXRegFromCode(unsigned code); VIXL_DEPRECATED("GetXRegFromCode", static const Register& XRegFromCode(unsigned code)) { return GetXRegFromCode(code); } private: static const Register wregisters[]; static const Register xregisters[]; }; namespace internal { template class FixedSizeRegister : public Register { public: FixedSizeRegister() : Register() {} explicit FixedSizeRegister(unsigned code) : Register(code, size_in_bits) { VIXL_ASSERT(IsValidRegister()); } explicit FixedSizeRegister(const Register& other) : Register(other.GetCode(), size_in_bits) { VIXL_ASSERT(other.GetSizeInBits() == size_in_bits); VIXL_ASSERT(IsValidRegister()); } explicit FixedSizeRegister(const CPURegister& other) : Register(other.GetCode(), other.GetSizeInBits()) { VIXL_ASSERT(other.GetType() == kRegister); VIXL_ASSERT(other.GetSizeInBits() == size_in_bits); VIXL_ASSERT(IsValidRegister()); } bool IsValid() const { return Register::IsValid() && (GetSizeInBits() == size_in_bits); } }; } // namespace internal typedef internal::FixedSizeRegister XRegister; typedef internal::FixedSizeRegister WRegister; class VRegister : public CPURegister { public: VRegister() : CPURegister(), lanes_(1) {} explicit VRegister(const CPURegister& other) : CPURegister(other.GetCode(), other.GetSizeInBits(), other.GetType()), lanes_(1) { VIXL_ASSERT(IsValidVRegister()); VIXL_ASSERT(IsPowerOf2(lanes_) && (lanes_ <= 16)); } VRegister(unsigned code, unsigned size, unsigned lanes = 1) : CPURegister(code, size, kVRegister), lanes_(lanes) { VIXL_ASSERT(IsPowerOf2(lanes_) && (lanes_ <= 16)); } VRegister(unsigned code, VectorFormat format) : CPURegister(code, RegisterSizeInBitsFromFormat(format), kVRegister), lanes_(IsVectorFormat(format) ? LaneCountFromFormat(format) : 1) { VIXL_ASSERT(IsPowerOf2(lanes_) && (lanes_ <= 16)); } bool IsValid() const { VIXL_ASSERT(IsVRegister() || IsNone()); return IsValidVRegister(); } static const VRegister& GetBRegFromCode(unsigned code); VIXL_DEPRECATED("GetBRegFromCode", static const VRegister& BRegFromCode(unsigned code)) { return GetBRegFromCode(code); } static const VRegister& GetHRegFromCode(unsigned code); VIXL_DEPRECATED("GetHRegFromCode", static const VRegister& HRegFromCode(unsigned code)) { return GetHRegFromCode(code); } static const VRegister& GetSRegFromCode(unsigned code); VIXL_DEPRECATED("GetSRegFromCode", static const VRegister& SRegFromCode(unsigned code)) { return GetSRegFromCode(code); } static const VRegister& GetDRegFromCode(unsigned code); VIXL_DEPRECATED("GetDRegFromCode", static const VRegister& DRegFromCode(unsigned code)) { return GetDRegFromCode(code); } static const VRegister& GetQRegFromCode(unsigned code); VIXL_DEPRECATED("GetQRegFromCode", static const VRegister& QRegFromCode(unsigned code)) { return GetQRegFromCode(code); } static const VRegister& GetVRegFromCode(unsigned code); VIXL_DEPRECATED("GetVRegFromCode", static const VRegister& VRegFromCode(unsigned code)) { return GetVRegFromCode(code); } VRegister V8B() const { return VRegister(code_, kDRegSize, 8); } VRegister V16B() const { return VRegister(code_, kQRegSize, 16); } VRegister V2H() const { return VRegister(code_, kSRegSize, 2); } VRegister V4H() const { return VRegister(code_, kDRegSize, 4); } VRegister V8H() const { return VRegister(code_, kQRegSize, 8); } VRegister V2S() const { return VRegister(code_, kDRegSize, 2); } VRegister V4S() const { return VRegister(code_, kQRegSize, 4); } VRegister V2D() const { return VRegister(code_, kQRegSize, 2); } VRegister V1D() const { return VRegister(code_, kDRegSize, 1); } bool Is8B() const { return (Is64Bits() && (lanes_ == 8)); } bool Is16B() const { return (Is128Bits() && (lanes_ == 16)); } bool Is2H() const { return (Is32Bits() && (lanes_ == 2)); } bool Is4H() const { return (Is64Bits() && (lanes_ == 4)); } bool Is8H() const { return (Is128Bits() && (lanes_ == 8)); } bool Is2S() const { return (Is64Bits() && (lanes_ == 2)); } bool Is4S() const { return (Is128Bits() && (lanes_ == 4)); } bool Is1D() const { return (Is64Bits() && (lanes_ == 1)); } bool Is2D() const { return (Is128Bits() && (lanes_ == 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 { VIXL_ASSERT(!(Is8Bits() && IsVector())); return Is8Bits(); } bool Is1H() const { VIXL_ASSERT(!(Is16Bits() && IsVector())); return Is16Bits(); } bool Is1S() const { VIXL_ASSERT(!(Is32Bits() && IsVector())); return Is32Bits(); } // Semantic type for sdot and udot instructions. bool Is1S4B() const { return Is1S(); } bool IsLaneSizeB() const { return GetLaneSizeInBits() == kBRegSize; } bool IsLaneSizeH() const { return GetLaneSizeInBits() == kHRegSize; } bool IsLaneSizeS() const { return GetLaneSizeInBits() == kSRegSize; } bool IsLaneSizeD() const { return GetLaneSizeInBits() == kDRegSize; } int GetLanes() const { return lanes_; } VIXL_DEPRECATED("GetLanes", int lanes() const) { return GetLanes(); } bool IsScalar() const { return lanes_ == 1; } bool IsVector() const { return lanes_ > 1; } bool IsSameFormat(const VRegister& other) const { return (size_ == other.size_) && (lanes_ == other.lanes_); } unsigned GetLaneSizeInBytes() const { return GetSizeInBytes() / lanes_; } VIXL_DEPRECATED("GetLaneSizeInBytes", unsigned LaneSizeInBytes() const) { return GetLaneSizeInBytes(); } unsigned GetLaneSizeInBits() const { return GetLaneSizeInBytes() * 8; } VIXL_DEPRECATED("GetLaneSizeInBits", unsigned LaneSizeInBits() const) { return GetLaneSizeInBits(); } private: static const VRegister bregisters[]; static const VRegister hregisters[]; static const VRegister sregisters[]; static const VRegister dregisters[]; static const VRegister qregisters[]; static const VRegister vregisters[]; int lanes_; }; // Backward compatibility for FPRegisters. typedef VRegister FPRegister; // 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. const Register NoReg; const VRegister NoVReg; const FPRegister NoFPReg; // For backward compatibility. const CPURegister NoCPUReg; #define DEFINE_REGISTERS(N) \ const WRegister w##N(N); \ const XRegister x##N(N); AARCH64_REGISTER_CODE_LIST(DEFINE_REGISTERS) #undef DEFINE_REGISTERS const WRegister wsp(kSPRegInternalCode); const XRegister sp(kSPRegInternalCode); #define DEFINE_VREGISTERS(N) \ const VRegister b##N(N, kBRegSize); \ const VRegister h##N(N, kHRegSize); \ const VRegister s##N(N, kSRegSize); \ const VRegister d##N(N, kDRegSize); \ const VRegister q##N(N, kQRegSize); \ const VRegister v##N(N, kQRegSize); AARCH64_REGISTER_CODE_LIST(DEFINE_VREGISTERS) #undef DEFINE_VREGISTERS // Register aliases. const XRegister ip0 = x16; const XRegister ip1 = x17; const XRegister lr = x30; const XRegister xzr = x31; const WRegister wzr = w31; // 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). bool AreSameSizeAndType(const CPURegister& reg1, const CPURegister& reg2, const CPURegister& reg3 = NoCPUReg, const CPURegister& reg4 = NoCPUReg, const CPURegister& reg5 = NoCPUReg, const CPURegister& reg6 = NoCPUReg, const CPURegister& reg7 = NoCPUReg, const CPURegister& reg8 = NoCPUReg); // AreEven returns true if all of the specified registers have even register // indices. Arguments set to NoReg are ignored, as are any subsequent // arguments. At least one argument (reg1) must be valid (not NoCPUReg). bool AreEven(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); // AreConsecutive returns true if all of the specified registers are // consecutive in the register file. Arguments set to NoReg are ignored, as are // any subsequent arguments. At least one argument (reg1) must be valid // (not NoCPUReg). bool AreConsecutive(const CPURegister& reg1, const CPURegister& reg2, const CPURegister& reg3 = NoCPUReg, const CPURegister& reg4 = NoCPUReg); // AreSameFormat returns true if all of the specified VRegisters have the same // vector format. Arguments set to NoReg 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 set to NoReg are ignored, as are // any subsequent arguments. At least one argument (reg1) must be valid // (not NoVReg). bool AreConsecutive(const VRegister& reg1, const VRegister& reg2, const VRegister& reg3 = NoVReg, const VRegister& reg4 = NoVReg); // Lists of registers. class CPURegList { public: explicit CPURegList(CPURegister reg1, CPURegister reg2 = NoCPUReg, CPURegister reg3 = NoCPUReg, CPURegister reg4 = NoCPUReg) : list_(reg1.GetBit() | reg2.GetBit() | reg3.GetBit() | reg4.GetBit()), size_(reg1.GetSizeInBits()), type_(reg1.GetType()) { VIXL_ASSERT(AreSameSizeAndType(reg1, reg2, reg3, reg4)); VIXL_ASSERT(IsValid()); } CPURegList(CPURegister::RegisterType type, unsigned size, RegList list) : list_(list), size_(size), type_(type) { VIXL_ASSERT(IsValid()); } CPURegList(CPURegister::RegisterType type, unsigned size, unsigned first_reg, unsigned last_reg) : size_(size), type_(type) { VIXL_ASSERT( ((type == CPURegister::kRegister) && (last_reg < kNumberOfRegisters)) || ((type == CPURegister::kVRegister) && (last_reg < kNumberOfVRegisters))); VIXL_ASSERT(last_reg >= first_reg); list_ = (UINT64_C(1) << (last_reg + 1)) - 1; list_ &= ~((UINT64_C(1) << first_reg) - 1); VIXL_ASSERT(IsValid()); } CPURegister::RegisterType GetType() const { VIXL_ASSERT(IsValid()); return type_; } VIXL_DEPRECATED("GetType", CPURegister::RegisterType type() const) { return GetType(); } // 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) { VIXL_ASSERT(IsValid()); VIXL_ASSERT(other.GetType() == type_); VIXL_ASSERT(other.GetRegisterSizeInBits() == size_); list_ |= other.GetList(); } // Remove every register in the other CPURegList from this one. Registers that // do not exist in this list are ignored. The type and size of the registers // in the 'other' list must match those in this list. void Remove(const CPURegList& other) { VIXL_ASSERT(IsValid()); VIXL_ASSERT(other.GetType() == type_); VIXL_ASSERT(other.GetRegisterSizeInBits() == size_); list_ &= ~other.GetList(); } // Variants of Combine and Remove which take a single register. void Combine(const CPURegister& other) { VIXL_ASSERT(other.GetType() == type_); VIXL_ASSERT(other.GetSizeInBits() == size_); Combine(other.GetCode()); } void Remove(const CPURegister& other) { VIXL_ASSERT(other.GetType() == type_); VIXL_ASSERT(other.GetSizeInBits() == size_); Remove(other.GetCode()); } // 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) { VIXL_ASSERT(IsValid()); VIXL_ASSERT(CPURegister(code, size_, type_).IsValid()); list_ |= (UINT64_C(1) << code); } void Remove(int code) { VIXL_ASSERT(IsValid()); VIXL_ASSERT(CPURegister(code, size_, type_).IsValid()); list_ &= ~(UINT64_C(1) << code); } static CPURegList Union(const CPURegList& list_1, const CPURegList& list_2) { VIXL_ASSERT(list_1.type_ == list_2.type_); VIXL_ASSERT(list_1.size_ == list_2.size_); return CPURegList(list_1.type_, list_1.size_, list_1.list_ | list_2.list_); } static CPURegList Union(const CPURegList& list_1, const CPURegList& list_2, const CPURegList& list_3); static CPURegList Union(const CPURegList& list_1, const CPURegList& list_2, const CPURegList& list_3, const CPURegList& list_4); static CPURegList Intersection(const CPURegList& list_1, const CPURegList& list_2) { VIXL_ASSERT(list_1.type_ == list_2.type_); VIXL_ASSERT(list_1.size_ == list_2.size_); return CPURegList(list_1.type_, list_1.size_, list_1.list_ & list_2.list_); } static CPURegList Intersection(const CPURegList& list_1, const CPURegList& list_2, const CPURegList& list_3); static CPURegList Intersection(const CPURegList& list_1, const CPURegList& list_2, const CPURegList& list_3, const CPURegList& list_4); bool Overlaps(const CPURegList& other) const { return (type_ == other.type_) && ((list_ & other.list_) != 0); } RegList GetList() const { VIXL_ASSERT(IsValid()); return list_; } VIXL_DEPRECATED("GetList", RegList list() const) { return GetList(); } void SetList(RegList new_list) { VIXL_ASSERT(IsValid()); list_ = new_list; } VIXL_DEPRECATED("SetList", void set_list(RegList new_list)) { return SetList(new_list); } // Remove all callee-saved registers from the list. This can be useful when // preparing registers for an AAPCS64 function call, for example. void RemoveCalleeSaved(); CPURegister PopLowestIndex(); CPURegister PopHighestIndex(); // AAPCS64 callee-saved registers. static CPURegList GetCalleeSaved(unsigned size = kXRegSize); static CPURegList GetCalleeSavedV(unsigned size = kDRegSize); // 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(unsigned size = kXRegSize); static CPURegList GetCallerSavedV(unsigned size = kDRegSize); bool IsEmpty() const { VIXL_ASSERT(IsValid()); return list_ == 0; } bool IncludesAliasOf(const CPURegister& other) const { VIXL_ASSERT(IsValid()); return (type_ == other.GetType()) && ((other.GetBit() & list_) != 0); } bool IncludesAliasOf(int code) const { VIXL_ASSERT(IsValid()); return ((code & list_) != 0); } int GetCount() const { VIXL_ASSERT(IsValid()); return CountSetBits(list_); } VIXL_DEPRECATED("GetCount", int Count()) const { return GetCount(); } int GetRegisterSizeInBits() const { VIXL_ASSERT(IsValid()); return size_; } VIXL_DEPRECATED("GetRegisterSizeInBits", int RegisterSizeInBits() const) { return GetRegisterSizeInBits(); } int GetRegisterSizeInBytes() const { int size_in_bits = GetRegisterSizeInBits(); VIXL_ASSERT((size_in_bits % 8) == 0); return size_in_bits / 8; } VIXL_DEPRECATED("GetRegisterSizeInBytes", int RegisterSizeInBytes() const) { return GetRegisterSizeInBytes(); } unsigned GetTotalSizeInBytes() const { VIXL_ASSERT(IsValid()); return GetRegisterSizeInBytes() * GetCount(); } VIXL_DEPRECATED("GetTotalSizeInBytes", unsigned TotalSizeInBytes() const) { return GetTotalSizeInBytes(); } private: RegList list_; int size_; CPURegister::RegisterType type_; bool IsValid() const; }; // AAPCS64 callee-saved registers. extern const CPURegList kCalleeSaved; extern const CPURegList kCalleeSavedV; // AAPCS64 caller-saved registers. Note that this includes lr. extern const CPURegList kCallerSaved; extern const CPURegList kCallerSavedV; // Operand. class Operand { public: // # // where is int64_t. // This is allowed to be an implicit constructor because Operand is // a wrapper class that doesn't normally perform any type conversion. Operand(int64_t immediate = 0); // NOLINT(runtime/explicit) // rm, { #} // where is one of {LSL, LSR, ASR, ROR}. // is uint6_t. // This is allowed to be an implicit constructor because Operand is // a wrapper class that doesn't normally perform any type conversion. Operand(Register reg, Shift shift = LSL, unsigned shift_amount = 0); // NOLINT(runtime/explicit) // rm, { {#}} // where is one of {UXTB, UXTH, UXTW, UXTX, SXTB, SXTH, SXTW, SXTX}. // is uint2_t. explicit Operand(Register reg, Extend extend, unsigned shift_amount = 0); bool IsImmediate() const; bool IsPlainRegister() const; bool IsShiftedRegister() const; bool IsExtendedRegister() const; bool IsZero() const; // This returns an LSL shift (<= 4) operand as an equivalent extend operand, // which helps in the encoding of instructions that use the stack pointer. Operand ToExtendedRegister() const; int64_t GetImmediate() const { VIXL_ASSERT(IsImmediate()); return immediate_; } VIXL_DEPRECATED("GetImmediate", int64_t immediate() const) { return GetImmediate(); } int64_t GetEquivalentImmediate() const { return IsZero() ? 0 : GetImmediate(); } Register GetRegister() const { VIXL_ASSERT(IsShiftedRegister() || IsExtendedRegister()); return reg_; } VIXL_DEPRECATED("GetRegister", Register reg() const) { return GetRegister(); } Register GetBaseRegister() const { return GetRegister(); } Shift GetShift() const { VIXL_ASSERT(IsShiftedRegister()); return shift_; } VIXL_DEPRECATED("GetShift", Shift shift() const) { return GetShift(); } Extend GetExtend() const { VIXL_ASSERT(IsExtendedRegister()); return extend_; } VIXL_DEPRECATED("GetExtend", Extend extend() const) { return GetExtend(); } unsigned GetShiftAmount() const { VIXL_ASSERT(IsShiftedRegister() || IsExtendedRegister()); return shift_amount_; } VIXL_DEPRECATED("GetShiftAmount", unsigned shift_amount() const) { return GetShiftAmount(); } private: int64_t immediate_; Register reg_; Shift shift_; Extend extend_; unsigned shift_amount_; }; // MemOperand represents the addressing mode of a load or store instruction. class MemOperand { public: // Creates an invalid `MemOperand`. MemOperand(); explicit MemOperand(Register base, int64_t offset = 0, AddrMode addrmode = Offset); MemOperand(Register base, Register regoffset, Shift shift = LSL, unsigned shift_amount = 0); MemOperand(Register base, Register regoffset, Extend extend, unsigned shift_amount = 0); MemOperand(Register base, const Operand& offset, AddrMode addrmode = Offset); const Register& GetBaseRegister() const { return base_; } VIXL_DEPRECATED("GetBaseRegister", const Register& base() const) { return GetBaseRegister(); } const Register& GetRegisterOffset() const { return regoffset_; } VIXL_DEPRECATED("GetRegisterOffset", const Register& regoffset() const) { return GetRegisterOffset(); } int64_t GetOffset() const { return offset_; } VIXL_DEPRECATED("GetOffset", int64_t offset() const) { return GetOffset(); } AddrMode GetAddrMode() const { return addrmode_; } VIXL_DEPRECATED("GetAddrMode", AddrMode addrmode() const) { return GetAddrMode(); } Shift GetShift() const { return shift_; } VIXL_DEPRECATED("GetShift", Shift shift() const) { return GetShift(); } Extend GetExtend() const { return extend_; } VIXL_DEPRECATED("GetExtend", Extend extend() const) { return GetExtend(); } unsigned GetShiftAmount() const { return shift_amount_; } VIXL_DEPRECATED("GetShiftAmount", unsigned shift_amount() const) { return GetShiftAmount(); } bool IsImmediateOffset() const; bool IsRegisterOffset() const; bool IsPreIndex() const; bool IsPostIndex() const; void AddOffset(int64_t offset); bool IsValid() const { return base_.IsValid() && ((addrmode_ == Offset) || (addrmode_ == PreIndex) || (addrmode_ == PostIndex)) && ((shift_ == NO_SHIFT) || (extend_ == NO_EXTEND)) && ((offset_ == 0) || !regoffset_.IsValid()); } bool Equals(const MemOperand& other) const { return base_.Is(other.base_) && regoffset_.Is(other.regoffset_) && (offset_ == other.offset_) && (addrmode_ == other.addrmode_) && (shift_ == other.shift_) && (extend_ == other.extend_) && (shift_amount_ == other.shift_amount_); } private: Register base_; Register regoffset_; int64_t offset_; AddrMode addrmode_; Shift shift_; Extend extend_; unsigned shift_amount_; }; // This an abstraction that can represent a register or memory location. The // `MacroAssembler` provides helpers to move data between generic operands. class GenericOperand { public: GenericOperand() { VIXL_ASSERT(!IsValid()); } GenericOperand(const CPURegister& reg); // NOLINT(runtime/explicit) GenericOperand(const MemOperand& mem_op, size_t mem_op_size = 0); // NOLINT(runtime/explicit) bool IsValid() const { return cpu_register_.IsValid() != mem_op_.IsValid(); } bool Equals(const GenericOperand& other) const; bool IsCPURegister() const { VIXL_ASSERT(IsValid()); return cpu_register_.IsValid(); } bool IsRegister() const { return IsCPURegister() && cpu_register_.IsRegister(); } bool IsVRegister() const { return IsCPURegister() && cpu_register_.IsVRegister(); } bool IsSameCPURegisterType(const GenericOperand& other) { return IsCPURegister() && other.IsCPURegister() && GetCPURegister().IsSameType(other.GetCPURegister()); } bool IsMemOperand() const { VIXL_ASSERT(IsValid()); return mem_op_.IsValid(); } CPURegister GetCPURegister() const { VIXL_ASSERT(IsCPURegister()); return cpu_register_; } MemOperand GetMemOperand() const { VIXL_ASSERT(IsMemOperand()); return mem_op_; } size_t GetMemOperandSizeInBytes() const { VIXL_ASSERT(IsMemOperand()); return mem_op_size_; } size_t GetSizeInBytes() const { return IsCPURegister() ? cpu_register_.GetSizeInBytes() : GetMemOperandSizeInBytes(); } size_t GetSizeInBits() const { return GetSizeInBytes() * kBitsPerByte; } private: CPURegister cpu_register_; MemOperand mem_op_; // The size of the memory region pointed to, in bytes. // We only support sizes up to X/D register sizes. size_t mem_op_size_; }; } } // namespace vixl::aarch64 #endif // VIXL_AARCH64_OPERANDS_AARCH64_H_