Aarch64: Extract registers and operands in separate files.

Also clean `#include` directives of `assembler-aarch64.h`.

Change-Id: Ic269216eba1c8c09318a92465365b5cf4a8dbd68
This commit is contained in:
Alexandre Rames 2016-07-06 09:33:10 +01:00
parent d3832965c6
commit def50a5485
6 changed files with 1313 additions and 1247 deletions

View File

@ -33,444 +33,6 @@
namespace vixl {
namespace aarch64 {
// CPURegList utilities.
CPURegister CPURegList::PopLowestIndex() {
if (IsEmpty()) {
return NoCPUReg;
}
int index = CountTrailingZeros(list_);
VIXL_ASSERT((1 << index) & list_);
Remove(index);
return CPURegister(index, size_, type_);
}
CPURegister CPURegList::PopHighestIndex() {
VIXL_ASSERT(IsValid());
if (IsEmpty()) {
return NoCPUReg;
}
int index = CountLeadingZeros(list_);
index = kRegListSizeInBits - 1 - index;
VIXL_ASSERT((1 << index) & list_);
Remove(index);
return CPURegister(index, size_, type_);
}
bool CPURegList::IsValid() const {
if ((type_ == CPURegister::kRegister) || (type_ == CPURegister::kVRegister)) {
bool is_valid = true;
// Try to create a CPURegister for each element in the list.
for (int i = 0; i < kRegListSizeInBits; i++) {
if (((list_ >> i) & 1) != 0) {
is_valid &= CPURegister(i, size_, type_).IsValid();
}
}
return is_valid;
} else if (type_ == CPURegister::kNoRegister) {
// We can't use IsEmpty here because that asserts IsValid().
return list_ == 0;
} else {
return false;
}
}
void CPURegList::RemoveCalleeSaved() {
if (GetType() == CPURegister::kRegister) {
Remove(GetCalleeSaved(GetRegisterSizeInBits()));
} else if (GetType() == CPURegister::kVRegister) {
Remove(GetCalleeSavedV(GetRegisterSizeInBits()));
} else {
VIXL_ASSERT(GetType() == CPURegister::kNoRegister);
VIXL_ASSERT(IsEmpty());
// The list must already be empty, so do nothing.
}
}
CPURegList CPURegList::Union(const CPURegList& list_1,
const CPURegList& list_2,
const CPURegList& list_3) {
return Union(list_1, Union(list_2, list_3));
}
CPURegList CPURegList::Union(const CPURegList& list_1,
const CPURegList& list_2,
const CPURegList& list_3,
const CPURegList& list_4) {
return Union(Union(list_1, list_2), Union(list_3, list_4));
}
CPURegList CPURegList::Intersection(const CPURegList& list_1,
const CPURegList& list_2,
const CPURegList& list_3) {
return Intersection(list_1, Intersection(list_2, list_3));
}
CPURegList CPURegList::Intersection(const CPURegList& list_1,
const CPURegList& list_2,
const CPURegList& list_3,
const CPURegList& list_4) {
return Intersection(Intersection(list_1, list_2),
Intersection(list_3, list_4));
}
CPURegList CPURegList::GetCalleeSaved(unsigned size) {
return CPURegList(CPURegister::kRegister, size, 19, 29);
}
CPURegList CPURegList::GetCalleeSavedV(unsigned size) {
return CPURegList(CPURegister::kVRegister, size, 8, 15);
}
CPURegList CPURegList::GetCallerSaved(unsigned size) {
// Registers x0-x18 and lr (x30) are caller-saved.
CPURegList list = CPURegList(CPURegister::kRegister, size, 0, 18);
// Do not use lr directly to avoid initialisation order fiasco bugs for users.
list.Combine(Register(30, kXRegSize));
return list;
}
CPURegList CPURegList::GetCallerSavedV(unsigned size) {
// Registers d0-d7 and d16-d31 are caller-saved.
CPURegList list = CPURegList(CPURegister::kVRegister, size, 0, 7);
list.Combine(CPURegList(CPURegister::kVRegister, size, 16, 31));
return list;
}
const CPURegList kCalleeSaved = CPURegList::GetCalleeSaved();
const CPURegList kCalleeSavedV = CPURegList::GetCalleeSavedV();
const CPURegList kCallerSaved = CPURegList::GetCallerSaved();
const CPURegList kCallerSavedV = CPURegList::GetCallerSavedV();
// Registers.
#define WREG(n) w##n,
const Register Register::wregisters[] = {REGISTER_CODE_LIST(WREG)};
#undef WREG
#define XREG(n) x##n,
const Register Register::xregisters[] = {REGISTER_CODE_LIST(XREG)};
#undef XREG
#define BREG(n) b##n,
const VRegister VRegister::bregisters[] = {REGISTER_CODE_LIST(BREG)};
#undef BREG
#define HREG(n) h##n,
const VRegister VRegister::hregisters[] = {REGISTER_CODE_LIST(HREG)};
#undef HREG
#define SREG(n) s##n,
const VRegister VRegister::sregisters[] = {REGISTER_CODE_LIST(SREG)};
#undef SREG
#define DREG(n) d##n,
const VRegister VRegister::dregisters[] = {REGISTER_CODE_LIST(DREG)};
#undef DREG
#define QREG(n) q##n,
const VRegister VRegister::qregisters[] = {REGISTER_CODE_LIST(QREG)};
#undef QREG
#define VREG(n) v##n,
const VRegister VRegister::vregisters[] = {REGISTER_CODE_LIST(VREG)};
#undef VREG
const Register& Register::GetWRegFromCode(unsigned code) {
if (code == kSPRegInternalCode) {
return wsp;
} else {
VIXL_ASSERT(code < kNumberOfRegisters);
return wregisters[code];
}
}
const Register& Register::GetXRegFromCode(unsigned code) {
if (code == kSPRegInternalCode) {
return sp;
} else {
VIXL_ASSERT(code < kNumberOfRegisters);
return xregisters[code];
}
}
const VRegister& VRegister::GetBRegFromCode(unsigned code) {
VIXL_ASSERT(code < kNumberOfVRegisters);
return bregisters[code];
}
const VRegister& VRegister::GetHRegFromCode(unsigned code) {
VIXL_ASSERT(code < kNumberOfVRegisters);
return hregisters[code];
}
const VRegister& VRegister::GetSRegFromCode(unsigned code) {
VIXL_ASSERT(code < kNumberOfVRegisters);
return sregisters[code];
}
const VRegister& VRegister::GetDRegFromCode(unsigned code) {
VIXL_ASSERT(code < kNumberOfVRegisters);
return dregisters[code];
}
const VRegister& VRegister::GetQRegFromCode(unsigned code) {
VIXL_ASSERT(code < kNumberOfVRegisters);
return qregisters[code];
}
const VRegister& VRegister::GetVRegFromCode(unsigned code) {
VIXL_ASSERT(code < kNumberOfVRegisters);
return vregisters[code];
}
const Register& CPURegister::W() const {
VIXL_ASSERT(IsValidRegister());
return Register::GetWRegFromCode(code_);
}
const Register& CPURegister::X() const {
VIXL_ASSERT(IsValidRegister());
return Register::GetXRegFromCode(code_);
}
const VRegister& CPURegister::B() const {
VIXL_ASSERT(IsValidVRegister());
return VRegister::GetBRegFromCode(code_);
}
const VRegister& CPURegister::H() const {
VIXL_ASSERT(IsValidVRegister());
return VRegister::GetHRegFromCode(code_);
}
const VRegister& CPURegister::S() const {
VIXL_ASSERT(IsValidVRegister());
return VRegister::GetSRegFromCode(code_);
}
const VRegister& CPURegister::D() const {
VIXL_ASSERT(IsValidVRegister());
return VRegister::GetDRegFromCode(code_);
}
const VRegister& CPURegister::Q() const {
VIXL_ASSERT(IsValidVRegister());
return VRegister::GetQRegFromCode(code_);
}
const VRegister& CPURegister::V() const {
VIXL_ASSERT(IsValidVRegister());
return VRegister::GetVRegFromCode(code_);
}
// Operand.
Operand::Operand(int64_t immediate)
: immediate_(immediate),
reg_(NoReg),
shift_(NO_SHIFT),
extend_(NO_EXTEND),
shift_amount_(0) {}
Operand::Operand(Register reg, Shift shift, unsigned shift_amount)
: reg_(reg),
shift_(shift),
extend_(NO_EXTEND),
shift_amount_(shift_amount) {
VIXL_ASSERT(shift != MSL);
VIXL_ASSERT(reg.Is64Bits() || (shift_amount < kWRegSize));
VIXL_ASSERT(reg.Is32Bits() || (shift_amount < kXRegSize));
VIXL_ASSERT(!reg.IsSP());
}
Operand::Operand(Register reg, Extend extend, unsigned shift_amount)
: reg_(reg),
shift_(NO_SHIFT),
extend_(extend),
shift_amount_(shift_amount) {
VIXL_ASSERT(reg.IsValid());
VIXL_ASSERT(shift_amount <= 4);
VIXL_ASSERT(!reg.IsSP());
// Extend modes SXTX and UXTX require a 64-bit register.
VIXL_ASSERT(reg.Is64Bits() || ((extend != SXTX) && (extend != UXTX)));
}
bool Operand::IsImmediate() const { return reg_.Is(NoReg); }
bool Operand::IsPlainRegister() const {
return reg_.IsValid() &&
(((shift_ == NO_SHIFT) && (extend_ == NO_EXTEND)) ||
// No-op shifts.
((shift_ != NO_SHIFT) && (shift_amount_ == 0)) ||
// No-op extend operations.
((extend_ == UXTX) || (extend_ == SXTX) ||
(reg_.IsW() && ((extend_ == UXTW) || (extend_ == SXTW)))));
}
bool Operand::IsShiftedRegister() const {
return reg_.IsValid() && (shift_ != NO_SHIFT);
}
bool Operand::IsExtendedRegister() const {
return reg_.IsValid() && (extend_ != NO_EXTEND);
}
bool Operand::IsZero() const {
if (IsImmediate()) {
return GetImmediate() == 0;
} else {
return GetRegister().IsZero();
}
}
Operand Operand::ToExtendedRegister() const {
VIXL_ASSERT(IsShiftedRegister());
VIXL_ASSERT((shift_ == LSL) && (shift_amount_ <= 4));
return Operand(reg_, reg_.Is64Bits() ? UXTX : UXTW, shift_amount_);
}
// MemOperand
MemOperand::MemOperand(Register base, int64_t offset, AddrMode addrmode)
: base_(base), regoffset_(NoReg), offset_(offset), addrmode_(addrmode) {
VIXL_ASSERT(base.Is64Bits() && !base.IsZero());
}
MemOperand::MemOperand(Register base,
Register regoffset,
Extend extend,
unsigned shift_amount)
: base_(base),
regoffset_(regoffset),
offset_(0),
addrmode_(Offset),
shift_(NO_SHIFT),
extend_(extend),
shift_amount_(shift_amount) {
VIXL_ASSERT(base.Is64Bits() && !base.IsZero());
VIXL_ASSERT(!regoffset.IsSP());
VIXL_ASSERT((extend == UXTW) || (extend == SXTW) || (extend == SXTX));
// SXTX extend mode requires a 64-bit offset register.
VIXL_ASSERT(regoffset.Is64Bits() || (extend != SXTX));
}
MemOperand::MemOperand(Register base,
Register regoffset,
Shift shift,
unsigned shift_amount)
: base_(base),
regoffset_(regoffset),
offset_(0),
addrmode_(Offset),
shift_(shift),
extend_(NO_EXTEND),
shift_amount_(shift_amount) {
VIXL_ASSERT(base.Is64Bits() && !base.IsZero());
VIXL_ASSERT(regoffset.Is64Bits() && !regoffset.IsSP());
VIXL_ASSERT(shift == LSL);
}
MemOperand::MemOperand(Register base, const Operand& offset, AddrMode addrmode)
: base_(base), regoffset_(NoReg), addrmode_(addrmode) {
VIXL_ASSERT(base.Is64Bits() && !base.IsZero());
if (offset.IsImmediate()) {
offset_ = offset.GetImmediate();
} else if (offset.IsShiftedRegister()) {
VIXL_ASSERT((addrmode == Offset) || (addrmode == PostIndex));
regoffset_ = offset.GetRegister();
shift_ = offset.GetShift();
shift_amount_ = offset.GetShiftAmount();
extend_ = NO_EXTEND;
offset_ = 0;
// These assertions match those in the shifted-register constructor.
VIXL_ASSERT(regoffset_.Is64Bits() && !regoffset_.IsSP());
VIXL_ASSERT(shift_ == LSL);
} else {
VIXL_ASSERT(offset.IsExtendedRegister());
VIXL_ASSERT(addrmode == Offset);
regoffset_ = offset.GetRegister();
extend_ = offset.GetExtend();
shift_amount_ = offset.GetShiftAmount();
shift_ = NO_SHIFT;
offset_ = 0;
// These assertions match those in the extended-register constructor.
VIXL_ASSERT(!regoffset_.IsSP());
VIXL_ASSERT((extend_ == UXTW) || (extend_ == SXTW) || (extend_ == SXTX));
VIXL_ASSERT((regoffset_.Is64Bits() || (extend_ != SXTX)));
}
}
bool MemOperand::IsImmediateOffset() const {
return (addrmode_ == Offset) && regoffset_.Is(NoReg);
}
bool MemOperand::IsRegisterOffset() const {
return (addrmode_ == Offset) && !regoffset_.Is(NoReg);
}
bool MemOperand::IsPreIndex() const { return addrmode_ == PreIndex; }
bool MemOperand::IsPostIndex() const { return addrmode_ == PostIndex; }
void MemOperand::AddOffset(int64_t offset) {
VIXL_ASSERT(IsImmediateOffset());
offset_ += offset;
}
RawLiteral::RawLiteral(size_t size,
LiteralPool* literal_pool,
DeletionPolicy deletion_policy)

View File

@ -32,817 +32,11 @@
#include "invalset-vixl.h"
#include "utils-vixl.h"
#include "aarch64/instructions-aarch64.h"
#include "aarch64/operands-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(); }
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 IsSameSizeAndType(const CPURegister& other) const {
return (size_ == other.size_) && (type_ == other.type_);
}
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[];
};
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 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 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();
}
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 Register w##N(N, kWRegSize); \
const Register x##N(N, kXRegSize);
REGISTER_CODE_LIST(DEFINE_REGISTERS)
#undef DEFINE_REGISTERS
const Register wsp(kSPRegInternalCode, kWRegSize);
const Register sp(kSPRegInternalCode, kXRegSize);
#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);
REGISTER_CODE_LIST(DEFINE_VREGISTERS)
#undef DEFINE_VREGISTERS
// Registers aliases.
const Register ip0 = x16;
const Register ip1 = x17;
const Register lr = x30;
const Register xzr = x31;
const Register 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);
// 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:
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);
private:
Register base_;
Register regoffset_;
int64_t offset_;
AddrMode addrmode_;
Shift shift_;
Extend extend_;
unsigned shift_amount_;
};
class LabelTestHelper; // Forward declaration.

View File

@ -30,9 +30,9 @@
#include "globals-vixl.h"
#include "utils-vixl.h"
#include "aarch64/assembler-aarch64.h"
#include "aarch64/decoder-aarch64.h"
#include "aarch64/instructions-aarch64.h"
#include "aarch64/operands-aarch64.h"
namespace vixl {
namespace aarch64 {

View File

@ -0,0 +1,469 @@
// 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.
#include "operands-aarch64.h"
namespace vixl {
namespace aarch64 {
// CPURegList utilities.
CPURegister CPURegList::PopLowestIndex() {
if (IsEmpty()) {
return NoCPUReg;
}
int index = CountTrailingZeros(list_);
VIXL_ASSERT((1 << index) & list_);
Remove(index);
return CPURegister(index, size_, type_);
}
CPURegister CPURegList::PopHighestIndex() {
VIXL_ASSERT(IsValid());
if (IsEmpty()) {
return NoCPUReg;
}
int index = CountLeadingZeros(list_);
index = kRegListSizeInBits - 1 - index;
VIXL_ASSERT((1 << index) & list_);
Remove(index);
return CPURegister(index, size_, type_);
}
bool CPURegList::IsValid() const {
if ((type_ == CPURegister::kRegister) || (type_ == CPURegister::kVRegister)) {
bool is_valid = true;
// Try to create a CPURegister for each element in the list.
for (int i = 0; i < kRegListSizeInBits; i++) {
if (((list_ >> i) & 1) != 0) {
is_valid &= CPURegister(i, size_, type_).IsValid();
}
}
return is_valid;
} else if (type_ == CPURegister::kNoRegister) {
// We can't use IsEmpty here because that asserts IsValid().
return list_ == 0;
} else {
return false;
}
}
void CPURegList::RemoveCalleeSaved() {
if (GetType() == CPURegister::kRegister) {
Remove(GetCalleeSaved(GetRegisterSizeInBits()));
} else if (GetType() == CPURegister::kVRegister) {
Remove(GetCalleeSavedV(GetRegisterSizeInBits()));
} else {
VIXL_ASSERT(GetType() == CPURegister::kNoRegister);
VIXL_ASSERT(IsEmpty());
// The list must already be empty, so do nothing.
}
}
CPURegList CPURegList::Union(const CPURegList& list_1,
const CPURegList& list_2,
const CPURegList& list_3) {
return Union(list_1, Union(list_2, list_3));
}
CPURegList CPURegList::Union(const CPURegList& list_1,
const CPURegList& list_2,
const CPURegList& list_3,
const CPURegList& list_4) {
return Union(Union(list_1, list_2), Union(list_3, list_4));
}
CPURegList CPURegList::Intersection(const CPURegList& list_1,
const CPURegList& list_2,
const CPURegList& list_3) {
return Intersection(list_1, Intersection(list_2, list_3));
}
CPURegList CPURegList::Intersection(const CPURegList& list_1,
const CPURegList& list_2,
const CPURegList& list_3,
const CPURegList& list_4) {
return Intersection(Intersection(list_1, list_2),
Intersection(list_3, list_4));
}
CPURegList CPURegList::GetCalleeSaved(unsigned size) {
return CPURegList(CPURegister::kRegister, size, 19, 29);
}
CPURegList CPURegList::GetCalleeSavedV(unsigned size) {
return CPURegList(CPURegister::kVRegister, size, 8, 15);
}
CPURegList CPURegList::GetCallerSaved(unsigned size) {
// Registers x0-x18 and lr (x30) are caller-saved.
CPURegList list = CPURegList(CPURegister::kRegister, size, 0, 18);
// Do not use lr directly to avoid initialisation order fiasco bugs for users.
list.Combine(Register(30, kXRegSize));
return list;
}
CPURegList CPURegList::GetCallerSavedV(unsigned size) {
// Registers d0-d7 and d16-d31 are caller-saved.
CPURegList list = CPURegList(CPURegister::kVRegister, size, 0, 7);
list.Combine(CPURegList(CPURegister::kVRegister, size, 16, 31));
return list;
}
const CPURegList kCalleeSaved = CPURegList::GetCalleeSaved();
const CPURegList kCalleeSavedV = CPURegList::GetCalleeSavedV();
const CPURegList kCallerSaved = CPURegList::GetCallerSaved();
const CPURegList kCallerSavedV = CPURegList::GetCallerSavedV();
// Registers.
#define WREG(n) w##n,
const Register Register::wregisters[] = {REGISTER_CODE_LIST(WREG)};
#undef WREG
#define XREG(n) x##n,
const Register Register::xregisters[] = {REGISTER_CODE_LIST(XREG)};
#undef XREG
#define BREG(n) b##n,
const VRegister VRegister::bregisters[] = {REGISTER_CODE_LIST(BREG)};
#undef BREG
#define HREG(n) h##n,
const VRegister VRegister::hregisters[] = {REGISTER_CODE_LIST(HREG)};
#undef HREG
#define SREG(n) s##n,
const VRegister VRegister::sregisters[] = {REGISTER_CODE_LIST(SREG)};
#undef SREG
#define DREG(n) d##n,
const VRegister VRegister::dregisters[] = {REGISTER_CODE_LIST(DREG)};
#undef DREG
#define QREG(n) q##n,
const VRegister VRegister::qregisters[] = {REGISTER_CODE_LIST(QREG)};
#undef QREG
#define VREG(n) v##n,
const VRegister VRegister::vregisters[] = {REGISTER_CODE_LIST(VREG)};
#undef VREG
const Register& Register::GetWRegFromCode(unsigned code) {
if (code == kSPRegInternalCode) {
return wsp;
} else {
VIXL_ASSERT(code < kNumberOfRegisters);
return wregisters[code];
}
}
const Register& Register::GetXRegFromCode(unsigned code) {
if (code == kSPRegInternalCode) {
return sp;
} else {
VIXL_ASSERT(code < kNumberOfRegisters);
return xregisters[code];
}
}
const VRegister& VRegister::GetBRegFromCode(unsigned code) {
VIXL_ASSERT(code < kNumberOfVRegisters);
return bregisters[code];
}
const VRegister& VRegister::GetHRegFromCode(unsigned code) {
VIXL_ASSERT(code < kNumberOfVRegisters);
return hregisters[code];
}
const VRegister& VRegister::GetSRegFromCode(unsigned code) {
VIXL_ASSERT(code < kNumberOfVRegisters);
return sregisters[code];
}
const VRegister& VRegister::GetDRegFromCode(unsigned code) {
VIXL_ASSERT(code < kNumberOfVRegisters);
return dregisters[code];
}
const VRegister& VRegister::GetQRegFromCode(unsigned code) {
VIXL_ASSERT(code < kNumberOfVRegisters);
return qregisters[code];
}
const VRegister& VRegister::GetVRegFromCode(unsigned code) {
VIXL_ASSERT(code < kNumberOfVRegisters);
return vregisters[code];
}
const Register& CPURegister::W() const {
VIXL_ASSERT(IsValidRegister());
return Register::GetWRegFromCode(code_);
}
const Register& CPURegister::X() const {
VIXL_ASSERT(IsValidRegister());
return Register::GetXRegFromCode(code_);
}
const VRegister& CPURegister::B() const {
VIXL_ASSERT(IsValidVRegister());
return VRegister::GetBRegFromCode(code_);
}
const VRegister& CPURegister::H() const {
VIXL_ASSERT(IsValidVRegister());
return VRegister::GetHRegFromCode(code_);
}
const VRegister& CPURegister::S() const {
VIXL_ASSERT(IsValidVRegister());
return VRegister::GetSRegFromCode(code_);
}
const VRegister& CPURegister::D() const {
VIXL_ASSERT(IsValidVRegister());
return VRegister::GetDRegFromCode(code_);
}
const VRegister& CPURegister::Q() const {
VIXL_ASSERT(IsValidVRegister());
return VRegister::GetQRegFromCode(code_);
}
const VRegister& CPURegister::V() const {
VIXL_ASSERT(IsValidVRegister());
return VRegister::GetVRegFromCode(code_);
}
// Operand.
Operand::Operand(int64_t immediate)
: immediate_(immediate),
reg_(NoReg),
shift_(NO_SHIFT),
extend_(NO_EXTEND),
shift_amount_(0) {}
Operand::Operand(Register reg, Shift shift, unsigned shift_amount)
: reg_(reg),
shift_(shift),
extend_(NO_EXTEND),
shift_amount_(shift_amount) {
VIXL_ASSERT(shift != MSL);
VIXL_ASSERT(reg.Is64Bits() || (shift_amount < kWRegSize));
VIXL_ASSERT(reg.Is32Bits() || (shift_amount < kXRegSize));
VIXL_ASSERT(!reg.IsSP());
}
Operand::Operand(Register reg, Extend extend, unsigned shift_amount)
: reg_(reg),
shift_(NO_SHIFT),
extend_(extend),
shift_amount_(shift_amount) {
VIXL_ASSERT(reg.IsValid());
VIXL_ASSERT(shift_amount <= 4);
VIXL_ASSERT(!reg.IsSP());
// Extend modes SXTX and UXTX require a 64-bit register.
VIXL_ASSERT(reg.Is64Bits() || ((extend != SXTX) && (extend != UXTX)));
}
bool Operand::IsImmediate() const { return reg_.Is(NoReg); }
bool Operand::IsPlainRegister() const {
return reg_.IsValid() &&
(((shift_ == NO_SHIFT) && (extend_ == NO_EXTEND)) ||
// No-op shifts.
((shift_ != NO_SHIFT) && (shift_amount_ == 0)) ||
// No-op extend operations.
((extend_ == UXTX) || (extend_ == SXTX) ||
(reg_.IsW() && ((extend_ == UXTW) || (extend_ == SXTW)))));
}
bool Operand::IsShiftedRegister() const {
return reg_.IsValid() && (shift_ != NO_SHIFT);
}
bool Operand::IsExtendedRegister() const {
return reg_.IsValid() && (extend_ != NO_EXTEND);
}
bool Operand::IsZero() const {
if (IsImmediate()) {
return GetImmediate() == 0;
} else {
return GetRegister().IsZero();
}
}
Operand Operand::ToExtendedRegister() const {
VIXL_ASSERT(IsShiftedRegister());
VIXL_ASSERT((shift_ == LSL) && (shift_amount_ <= 4));
return Operand(reg_, reg_.Is64Bits() ? UXTX : UXTW, shift_amount_);
}
// MemOperand
MemOperand::MemOperand(Register base, int64_t offset, AddrMode addrmode)
: base_(base), regoffset_(NoReg), offset_(offset), addrmode_(addrmode) {
VIXL_ASSERT(base.Is64Bits() && !base.IsZero());
}
MemOperand::MemOperand(Register base,
Register regoffset,
Extend extend,
unsigned shift_amount)
: base_(base),
regoffset_(regoffset),
offset_(0),
addrmode_(Offset),
shift_(NO_SHIFT),
extend_(extend),
shift_amount_(shift_amount) {
VIXL_ASSERT(base.Is64Bits() && !base.IsZero());
VIXL_ASSERT(!regoffset.IsSP());
VIXL_ASSERT((extend == UXTW) || (extend == SXTW) || (extend == SXTX));
// SXTX extend mode requires a 64-bit offset register.
VIXL_ASSERT(regoffset.Is64Bits() || (extend != SXTX));
}
MemOperand::MemOperand(Register base,
Register regoffset,
Shift shift,
unsigned shift_amount)
: base_(base),
regoffset_(regoffset),
offset_(0),
addrmode_(Offset),
shift_(shift),
extend_(NO_EXTEND),
shift_amount_(shift_amount) {
VIXL_ASSERT(base.Is64Bits() && !base.IsZero());
VIXL_ASSERT(regoffset.Is64Bits() && !regoffset.IsSP());
VIXL_ASSERT(shift == LSL);
}
MemOperand::MemOperand(Register base, const Operand& offset, AddrMode addrmode)
: base_(base), regoffset_(NoReg), addrmode_(addrmode) {
VIXL_ASSERT(base.Is64Bits() && !base.IsZero());
if (offset.IsImmediate()) {
offset_ = offset.GetImmediate();
} else if (offset.IsShiftedRegister()) {
VIXL_ASSERT((addrmode == Offset) || (addrmode == PostIndex));
regoffset_ = offset.GetRegister();
shift_ = offset.GetShift();
shift_amount_ = offset.GetShiftAmount();
extend_ = NO_EXTEND;
offset_ = 0;
// These assertions match those in the shifted-register constructor.
VIXL_ASSERT(regoffset_.Is64Bits() && !regoffset_.IsSP());
VIXL_ASSERT(shift_ == LSL);
} else {
VIXL_ASSERT(offset.IsExtendedRegister());
VIXL_ASSERT(addrmode == Offset);
regoffset_ = offset.GetRegister();
extend_ = offset.GetExtend();
shift_amount_ = offset.GetShiftAmount();
shift_ = NO_SHIFT;
offset_ = 0;
// These assertions match those in the extended-register constructor.
VIXL_ASSERT(!regoffset_.IsSP());
VIXL_ASSERT((extend_ == UXTW) || (extend_ == SXTW) || (extend_ == SXTX));
VIXL_ASSERT((regoffset_.Is64Bits() || (extend_ != SXTX)));
}
}
bool MemOperand::IsImmediateOffset() const {
return (addrmode_ == Offset) && regoffset_.Is(NoReg);
}
bool MemOperand::IsRegisterOffset() const {
return (addrmode_ == Offset) && !regoffset_.Is(NoReg);
}
bool MemOperand::IsPreIndex() const { return addrmode_ == PreIndex; }
bool MemOperand::IsPostIndex() const { return addrmode_ == PostIndex; }
void MemOperand::AddOffset(int64_t offset) {
VIXL_ASSERT(IsImmediateOffset());
offset_ += offset;
}
}
} // namespace vixl::aarch64

View File

@ -0,0 +1,842 @@
// 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 "aarch64/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(); }
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 IsSameSizeAndType(const CPURegister& other) const {
return (size_ == other.size_) && (type_ == other.type_);
}
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[];
};
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 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 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();
}
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 Register w##N(N, kWRegSize); \
const Register x##N(N, kXRegSize);
REGISTER_CODE_LIST(DEFINE_REGISTERS)
#undef DEFINE_REGISTERS
const Register wsp(kSPRegInternalCode, kWRegSize);
const Register sp(kSPRegInternalCode, kXRegSize);
#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);
REGISTER_CODE_LIST(DEFINE_VREGISTERS)
#undef DEFINE_VREGISTERS
// Registers aliases.
const Register ip0 = x16;
const Register ip1 = x17;
const Register lr = x30;
const Register xzr = x31;
const Register 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);
// 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:
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);
private:
Register base_;
Register regoffset_;
int64_t offset_;
AddrMode addrmode_;
Shift shift_;
Extend extend_;
unsigned shift_amount_;
};
}
} // namespace vixl::aarch64
#endif // VIXL_AARCH64_OPERANDS_AARCH64_H_

View File

@ -30,7 +30,6 @@
#include "globals-vixl.h"
#include "utils-vixl.h"
#include "aarch64/assembler-aarch64.h"
#include "aarch64/disasm-aarch64.h"
#include "aarch64/instructions-aarch64.h"
#include "aarch64/instrument-aarch64.h"