mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-30 09:35:40 +00:00
994 lines
32 KiB
C++
994 lines
32 KiB
C++
// 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<RegList>(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 <int size_in_bits>
|
|
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<kXRegSize> XRegister;
|
|
typedef internal::FixedSizeRegister<kWRegSize> 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:
|
|
// #<immediate>
|
|
// where <immediate> 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, {<shift> #<shift_amount>}
|
|
// where <shift> is one of {LSL, LSR, ASR, ROR}.
|
|
// <shift_amount> 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, {<extend> {#<shift_amount>}}
|
|
// where <extend> is one of {UXTB, UXTH, UXTW, UXTX, SXTB, SXTH, SXTW, SXTX}.
|
|
// <shift_amount> 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_
|