mirror of
https://github.com/FEX-Emu/vixl.git
synced 2024-11-23 14:40:17 +00:00
Aarch64: Extract registers and operands in separate files.
Also clean `#include` directives of `assembler-aarch64.h`. Change-Id: Ic269216eba1c8c09318a92465365b5cf4a8dbd68
This commit is contained in:
parent
d3832965c6
commit
def50a5485
@ -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)
|
||||
|
@ -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.
|
||||
|
||||
|
||||
|
@ -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 {
|
||||
|
469
src/aarch64/operands-aarch64.cc
Normal file
469
src/aarch64/operands-aarch64.cc
Normal 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
|
842
src/aarch64/operands-aarch64.h
Normal file
842
src/aarch64/operands-aarch64.h
Normal 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_
|
@ -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"
|
||||
|
Loading…
Reference in New Issue
Block a user