1360 lines
43 KiB
C++
1360 lines
43 KiB
C++
// Copyright 2017, 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_AARCH32_INSTRUCTIONS_AARCH32_H_
|
|
#define VIXL_AARCH32_INSTRUCTIONS_AARCH32_H_
|
|
|
|
extern "C" {
|
|
#include <stdint.h>
|
|
}
|
|
|
|
#include <algorithm>
|
|
#include <ostream>
|
|
|
|
#include "code-buffer-vixl.h"
|
|
#include "utils-vixl.h"
|
|
#include "aarch32/constants-aarch32.h"
|
|
|
|
#ifdef __arm__
|
|
#define HARDFLOAT __attribute__((noinline, pcs("aapcs-vfp")))
|
|
#else
|
|
#define HARDFLOAT __attribute__((noinline))
|
|
#endif
|
|
|
|
namespace vixl {
|
|
namespace aarch32 {
|
|
|
|
class Operand;
|
|
class SOperand;
|
|
class DOperand;
|
|
class QOperand;
|
|
class MemOperand;
|
|
class AlignedMemOperand;
|
|
|
|
enum AddrMode { Offset = 0, PreIndex = 1, PostIndex = 2 };
|
|
|
|
class CPURegister {
|
|
public:
|
|
enum RegisterType {
|
|
kNoRegister = 0,
|
|
kRRegister = 1,
|
|
kSRegister = 2,
|
|
kDRegister = 3,
|
|
kQRegister = 4
|
|
};
|
|
|
|
private:
|
|
static const int kCodeBits = 5;
|
|
static const int kTypeBits = 4;
|
|
static const int kSizeBits = 8;
|
|
static const int kCodeShift = 0;
|
|
static const int kTypeShift = kCodeShift + kCodeBits;
|
|
static const int kSizeShift = kTypeShift + kTypeBits;
|
|
static const uint32_t kCodeMask = ((1 << kCodeBits) - 1) << kCodeShift;
|
|
static const uint32_t kTypeMask = ((1 << kTypeBits) - 1) << kTypeShift;
|
|
static const uint32_t kSizeMask = ((1 << kSizeBits) - 1) << kSizeShift;
|
|
uint32_t value_;
|
|
|
|
public:
|
|
CPURegister(RegisterType type, uint32_t code, int size)
|
|
: value_((type << kTypeShift) | (code << kCodeShift) |
|
|
(size << kSizeShift)) {
|
|
#ifdef VIXL_DEBUG
|
|
switch (type) {
|
|
case kNoRegister:
|
|
break;
|
|
case kRRegister:
|
|
VIXL_ASSERT(code < kNumberOfRegisters);
|
|
VIXL_ASSERT(size == kRegSizeInBits);
|
|
break;
|
|
case kSRegister:
|
|
VIXL_ASSERT(code < kNumberOfSRegisters);
|
|
VIXL_ASSERT(size == kSRegSizeInBits);
|
|
break;
|
|
case kDRegister:
|
|
VIXL_ASSERT(code < kMaxNumberOfDRegisters);
|
|
VIXL_ASSERT(size == kDRegSizeInBits);
|
|
break;
|
|
case kQRegister:
|
|
VIXL_ASSERT(code < kNumberOfQRegisters);
|
|
VIXL_ASSERT(size == kQRegSizeInBits);
|
|
break;
|
|
default:
|
|
VIXL_UNREACHABLE();
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
RegisterType GetType() const {
|
|
return static_cast<RegisterType>((value_ & kTypeMask) >> kTypeShift);
|
|
}
|
|
bool IsRegister() const { return GetType() == kRRegister; }
|
|
bool IsS() const { return GetType() == kSRegister; }
|
|
bool IsD() const { return GetType() == kDRegister; }
|
|
bool IsQ() const { return GetType() == kQRegister; }
|
|
bool IsVRegister() const { return IsS() || IsD() || IsQ(); }
|
|
bool IsFPRegister() const { return IsS() || IsD(); }
|
|
uint32_t GetCode() const { return (value_ & kCodeMask) >> kCodeShift; }
|
|
uint32_t GetReg() const { return value_; }
|
|
int GetSizeInBits() const { return (value_ & kSizeMask) >> kSizeShift; }
|
|
int GetRegSizeInBytes() const {
|
|
return (GetType() == kNoRegister) ? 0 : (GetSizeInBits() / 8);
|
|
}
|
|
bool Is64Bits() const { return GetSizeInBits() == 64; }
|
|
bool Is128Bits() const { return GetSizeInBits() == 128; }
|
|
bool IsSameFormat(CPURegister reg) {
|
|
return (value_ & ~kCodeMask) == (reg.value_ & ~kCodeMask);
|
|
}
|
|
bool Is(CPURegister ref) const { return GetReg() == ref.GetReg(); }
|
|
bool IsValid() const { return GetType() != kNoRegister; }
|
|
};
|
|
|
|
class Register : public CPURegister {
|
|
public:
|
|
Register() : CPURegister(kNoRegister, 0, kRegSizeInBits) {}
|
|
explicit Register(uint32_t code)
|
|
: CPURegister(kRRegister, code % kNumberOfRegisters, kRegSizeInBits) {
|
|
VIXL_ASSERT(GetCode() < kNumberOfRegisters);
|
|
}
|
|
bool Is(Register ref) const { return GetCode() == ref.GetCode(); }
|
|
bool IsLow() const { return GetCode() < kNumberOfT32LowRegisters; }
|
|
bool IsLR() const { return GetCode() == kLrCode; }
|
|
bool IsPC() const { return GetCode() == kPcCode; }
|
|
bool IsSP() const { return GetCode() == kSpCode; }
|
|
};
|
|
|
|
std::ostream& operator<<(std::ostream& os, const Register reg);
|
|
|
|
class RegisterOrAPSR_nzcv {
|
|
uint32_t code_;
|
|
|
|
public:
|
|
explicit RegisterOrAPSR_nzcv(uint32_t code) : code_(code) {
|
|
VIXL_ASSERT(code_ < kNumberOfRegisters);
|
|
}
|
|
bool IsAPSR_nzcv() const { return code_ == kPcCode; }
|
|
uint32_t GetCode() const { return code_; }
|
|
Register AsRegister() const {
|
|
VIXL_ASSERT(!IsAPSR_nzcv());
|
|
return Register(code_);
|
|
}
|
|
};
|
|
|
|
const RegisterOrAPSR_nzcv APSR_nzcv(kPcCode);
|
|
|
|
inline std::ostream& operator<<(std::ostream& os,
|
|
const RegisterOrAPSR_nzcv reg) {
|
|
if (reg.IsAPSR_nzcv()) return os << "APSR_nzcv";
|
|
return os << reg.AsRegister();
|
|
}
|
|
|
|
class SRegister;
|
|
class DRegister;
|
|
class QRegister;
|
|
|
|
class VRegister : public CPURegister {
|
|
public:
|
|
VRegister() : CPURegister(kNoRegister, 0, 0) {}
|
|
VRegister(RegisterType type, uint32_t code, int size)
|
|
: CPURegister(type, code, size) {}
|
|
|
|
SRegister S() const;
|
|
DRegister D() const;
|
|
QRegister Q() const;
|
|
};
|
|
|
|
class SRegister : public VRegister {
|
|
public:
|
|
SRegister() : VRegister(kNoRegister, 0, kSRegSizeInBits) {}
|
|
explicit SRegister(uint32_t code)
|
|
: VRegister(kSRegister, code, kSRegSizeInBits) {}
|
|
uint32_t Encode(int single_bit_field, int four_bit_field_lowest_bit) const {
|
|
if (four_bit_field_lowest_bit == 0) {
|
|
return ((GetCode() & 0x1) << single_bit_field) |
|
|
((GetCode() & 0x1e) >> 1);
|
|
}
|
|
return ((GetCode() & 0x1) << single_bit_field) |
|
|
((GetCode() & 0x1e) << (four_bit_field_lowest_bit - 1));
|
|
}
|
|
};
|
|
|
|
inline unsigned ExtractSRegister(uint32_t instr,
|
|
int single_bit_field,
|
|
int four_bit_field_lowest_bit) {
|
|
VIXL_ASSERT(single_bit_field > 0);
|
|
if (four_bit_field_lowest_bit == 0) {
|
|
return ((instr << 1) & 0x1e) | ((instr >> single_bit_field) & 0x1);
|
|
}
|
|
return ((instr >> (four_bit_field_lowest_bit - 1)) & 0x1e) |
|
|
((instr >> single_bit_field) & 0x1);
|
|
}
|
|
|
|
inline std::ostream& operator<<(std::ostream& os, const SRegister reg) {
|
|
return os << "s" << reg.GetCode();
|
|
}
|
|
|
|
class DRegister : public VRegister {
|
|
public:
|
|
DRegister() : VRegister(kNoRegister, 0, kDRegSizeInBits) {}
|
|
explicit DRegister(uint32_t code)
|
|
: VRegister(kDRegister, code, kDRegSizeInBits) {}
|
|
SRegister GetLane(uint32_t lane) const {
|
|
uint32_t lane_count = kDRegSizeInBits / kSRegSizeInBits;
|
|
VIXL_ASSERT(lane < lane_count);
|
|
VIXL_ASSERT(GetCode() * lane_count < kNumberOfSRegisters);
|
|
return SRegister(GetCode() * lane_count + lane);
|
|
}
|
|
uint32_t Encode(int single_bit_field, int four_bit_field_lowest_bit) const {
|
|
VIXL_ASSERT(single_bit_field >= 4);
|
|
return ((GetCode() & 0x10) << (single_bit_field - 4)) |
|
|
((GetCode() & 0xf) << four_bit_field_lowest_bit);
|
|
}
|
|
};
|
|
|
|
inline unsigned ExtractDRegister(uint32_t instr,
|
|
int single_bit_field,
|
|
int four_bit_field_lowest_bit) {
|
|
VIXL_ASSERT(single_bit_field >= 4);
|
|
return ((instr >> (single_bit_field - 4)) & 0x10) |
|
|
((instr >> four_bit_field_lowest_bit) & 0xf);
|
|
}
|
|
|
|
inline std::ostream& operator<<(std::ostream& os, const DRegister reg) {
|
|
return os << "d" << reg.GetCode();
|
|
}
|
|
|
|
enum DataTypeType {
|
|
kDataTypeS = 0x100,
|
|
kDataTypeU = 0x200,
|
|
kDataTypeF = 0x300,
|
|
kDataTypeI = 0x400,
|
|
kDataTypeP = 0x500,
|
|
kDataTypeUntyped = 0x600
|
|
};
|
|
const int kDataTypeSizeMask = 0x0ff;
|
|
const int kDataTypeTypeMask = 0x100;
|
|
enum DataTypeValue {
|
|
kDataTypeValueInvalid = 0x000,
|
|
kDataTypeValueNone = 0x001, // value used when dt is ignored.
|
|
S8 = kDataTypeS | 8,
|
|
S16 = kDataTypeS | 16,
|
|
S32 = kDataTypeS | 32,
|
|
S64 = kDataTypeS | 64,
|
|
U8 = kDataTypeU | 8,
|
|
U16 = kDataTypeU | 16,
|
|
U32 = kDataTypeU | 32,
|
|
U64 = kDataTypeU | 64,
|
|
F16 = kDataTypeF | 16,
|
|
F32 = kDataTypeF | 32,
|
|
F64 = kDataTypeF | 64,
|
|
I8 = kDataTypeI | 8,
|
|
I16 = kDataTypeI | 16,
|
|
I32 = kDataTypeI | 32,
|
|
I64 = kDataTypeI | 64,
|
|
P8 = kDataTypeP | 8,
|
|
P64 = kDataTypeP | 64,
|
|
Untyped8 = kDataTypeUntyped | 8,
|
|
Untyped16 = kDataTypeUntyped | 16,
|
|
Untyped32 = kDataTypeUntyped | 32,
|
|
Untyped64 = kDataTypeUntyped | 64
|
|
};
|
|
|
|
class DataType {
|
|
DataTypeValue value_;
|
|
|
|
public:
|
|
explicit DataType(uint32_t size)
|
|
: value_(static_cast<DataTypeValue>(kDataTypeUntyped | size)) {
|
|
VIXL_ASSERT((size == 8) || (size == 16) || (size == 32) || (size == 64));
|
|
}
|
|
// Users should be able to use "S8", "S6" and so forth to instantiate this
|
|
// class.
|
|
DataType(DataTypeValue value) : value_(value) {} // NOLINT(runtime/explicit)
|
|
DataTypeValue GetValue() const { return value_; }
|
|
DataTypeType GetType() const {
|
|
return static_cast<DataTypeType>(value_ & kDataTypeTypeMask);
|
|
}
|
|
uint32_t GetSize() const { return value_ & kDataTypeSizeMask; }
|
|
bool IsSize(uint32_t size) const {
|
|
return (value_ & kDataTypeSizeMask) == size;
|
|
}
|
|
const char* GetName() const;
|
|
bool Is(DataType type) const { return value_ == type.value_; }
|
|
bool Is(DataTypeValue value) const { return value_ == value; }
|
|
bool Is(DataTypeType type) const { return GetType() == type; }
|
|
bool IsNoneOr(DataTypeValue value) const {
|
|
return (value_ == value) || (value_ == kDataTypeValueNone);
|
|
}
|
|
bool Is(DataTypeType type, uint32_t size) const {
|
|
return value_ == static_cast<DataTypeValue>(type | size);
|
|
}
|
|
bool IsNoneOr(DataTypeType type, uint32_t size) const {
|
|
return Is(type, size) || Is(kDataTypeValueNone);
|
|
}
|
|
};
|
|
|
|
inline std::ostream& operator<<(std::ostream& os, DataType dt) {
|
|
return os << dt.GetName();
|
|
}
|
|
|
|
class DRegisterLane : public DRegister {
|
|
uint32_t lane_;
|
|
|
|
public:
|
|
DRegisterLane(DRegister reg, uint32_t lane)
|
|
: DRegister(reg.GetCode()), lane_(lane) {}
|
|
DRegisterLane(uint32_t code, uint32_t lane) : DRegister(code), lane_(lane) {}
|
|
uint32_t GetLane() const { return lane_; }
|
|
uint32_t EncodeX(DataType dt,
|
|
int single_bit_field,
|
|
int four_bit_field_lowest_bit) const {
|
|
VIXL_ASSERT(single_bit_field >= 4);
|
|
uint32_t value = lane_ << ((dt.GetSize() == 16) ? 3 : 4) | GetCode();
|
|
return ((value & 0x10) << (single_bit_field - 4)) |
|
|
((value & 0xf) << four_bit_field_lowest_bit);
|
|
}
|
|
};
|
|
|
|
inline unsigned ExtractDRegisterAndLane(uint32_t instr,
|
|
DataType dt,
|
|
int single_bit_field,
|
|
int four_bit_field_lowest_bit,
|
|
int* lane) {
|
|
VIXL_ASSERT(single_bit_field >= 4);
|
|
uint32_t value = ((instr >> (single_bit_field - 4)) & 0x10) |
|
|
((instr >> four_bit_field_lowest_bit) & 0xf);
|
|
if (dt.GetSize() == 16) {
|
|
*lane = value >> 3;
|
|
return value & 0x7;
|
|
}
|
|
*lane = value >> 4;
|
|
return value & 0xf;
|
|
}
|
|
|
|
inline std::ostream& operator<<(std::ostream& os, const DRegisterLane lane) {
|
|
os << "d" << lane.GetCode() << "[";
|
|
if (lane.GetLane() == static_cast<uint32_t>(-1)) return os << "??]";
|
|
return os << lane.GetLane() << "]";
|
|
}
|
|
|
|
class QRegister : public VRegister {
|
|
public:
|
|
QRegister() : VRegister(kNoRegister, 0, kQRegSizeInBits) {}
|
|
explicit QRegister(uint32_t code)
|
|
: VRegister(kQRegister, code, kQRegSizeInBits) {}
|
|
uint32_t Encode(int offset) { return GetCode() << offset; }
|
|
DRegister GetDLane(uint32_t lane) const {
|
|
uint32_t lane_count = kQRegSizeInBits / kDRegSizeInBits;
|
|
VIXL_ASSERT(lane < lane_count);
|
|
return DRegister(GetCode() * lane_count + lane);
|
|
}
|
|
DRegister GetLowDRegister() const { return DRegister(GetCode() * 2); }
|
|
DRegister GetHighDRegister() const { return DRegister(1 + GetCode() * 2); }
|
|
SRegister GetSLane(uint32_t lane) const {
|
|
uint32_t lane_count = kQRegSizeInBits / kSRegSizeInBits;
|
|
VIXL_ASSERT(lane < lane_count);
|
|
VIXL_ASSERT(GetCode() * lane_count < kNumberOfSRegisters);
|
|
return SRegister(GetCode() * lane_count + lane);
|
|
}
|
|
uint32_t Encode(int single_bit_field, int four_bit_field_lowest_bit) {
|
|
// Encode "code * 2".
|
|
VIXL_ASSERT(single_bit_field >= 3);
|
|
return ((GetCode() & 0x8) << (single_bit_field - 3)) |
|
|
((GetCode() & 0x7) << (four_bit_field_lowest_bit + 1));
|
|
}
|
|
};
|
|
|
|
inline unsigned ExtractQRegister(uint32_t instr,
|
|
int single_bit_field,
|
|
int four_bit_field_lowest_bit) {
|
|
VIXL_ASSERT(single_bit_field >= 3);
|
|
return ((instr >> (single_bit_field - 3)) & 0x8) |
|
|
((instr >> (four_bit_field_lowest_bit + 1)) & 0x7);
|
|
}
|
|
|
|
inline std::ostream& operator<<(std::ostream& os, const QRegister reg) {
|
|
return os << "q" << reg.GetCode();
|
|
}
|
|
|
|
// clang-format off
|
|
#define AARCH32_REGISTER_CODE_LIST(R) \
|
|
R(0) R(1) R(2) R(3) R(4) R(5) R(6) R(7) \
|
|
R(8) R(9) R(10) R(11) R(12) R(13) R(14) R(15)
|
|
// clang-format on
|
|
#define DEFINE_REGISTER(N) const Register r##N(N);
|
|
AARCH32_REGISTER_CODE_LIST(DEFINE_REGISTER)
|
|
#undef DEFINE_REGISTER
|
|
#undef AARCH32_REGISTER_CODE_LIST
|
|
|
|
enum RegNum { kIPRegNum = 12, kSPRegNum = 13, kLRRegNum = 14, kPCRegNum = 15 };
|
|
|
|
const Register ip(kIPRegNum);
|
|
const Register sp(kSPRegNum);
|
|
const Register pc(kPCRegNum);
|
|
const Register lr(kLRRegNum);
|
|
const Register NoReg;
|
|
const VRegister NoVReg;
|
|
|
|
// clang-format off
|
|
#define SREGISTER_CODE_LIST(R) \
|
|
R(0) R(1) R(2) R(3) R(4) R(5) R(6) R(7) \
|
|
R(8) R(9) R(10) R(11) R(12) R(13) R(14) R(15) \
|
|
R(16) R(17) R(18) R(19) R(20) R(21) R(22) R(23) \
|
|
R(24) R(25) R(26) R(27) R(28) R(29) R(30) R(31)
|
|
// clang-format on
|
|
#define DEFINE_REGISTER(N) const SRegister s##N(N);
|
|
SREGISTER_CODE_LIST(DEFINE_REGISTER)
|
|
#undef DEFINE_REGISTER
|
|
#undef SREGISTER_CODE_LIST
|
|
const SRegister NoSReg;
|
|
|
|
// clang-format off
|
|
#define DREGISTER_CODE_LIST(R) \
|
|
R(0) R(1) R(2) R(3) R(4) R(5) R(6) R(7) \
|
|
R(8) R(9) R(10) R(11) R(12) R(13) R(14) R(15) \
|
|
R(16) R(17) R(18) R(19) R(20) R(21) R(22) R(23) \
|
|
R(24) R(25) R(26) R(27) R(28) R(29) R(30) R(31)
|
|
// clang-format on
|
|
#define DEFINE_REGISTER(N) const DRegister d##N(N);
|
|
DREGISTER_CODE_LIST(DEFINE_REGISTER)
|
|
#undef DEFINE_REGISTER
|
|
#undef DREGISTER_CODE_LIST
|
|
const DRegister NoDReg;
|
|
|
|
// clang-format off
|
|
#define QREGISTER_CODE_LIST(R) \
|
|
R(0) R(1) R(2) R(3) R(4) R(5) R(6) R(7) \
|
|
R(8) R(9) R(10) R(11) R(12) R(13) R(14) R(15)
|
|
// clang-format on
|
|
#define DEFINE_REGISTER(N) const QRegister q##N(N);
|
|
QREGISTER_CODE_LIST(DEFINE_REGISTER)
|
|
#undef DEFINE_REGISTER
|
|
#undef QREGISTER_CODE_LIST
|
|
const QRegister NoQReg;
|
|
|
|
class RegisterList {
|
|
public:
|
|
RegisterList() : list_(0) {}
|
|
RegisterList(Register reg) // NOLINT(runtime/explicit)
|
|
: list_(RegisterToList(reg)) {}
|
|
RegisterList(Register reg1, Register reg2)
|
|
: list_(RegisterToList(reg1) | RegisterToList(reg2)) {}
|
|
RegisterList(Register reg1, Register reg2, Register reg3)
|
|
: list_(RegisterToList(reg1) | RegisterToList(reg2) |
|
|
RegisterToList(reg3)) {}
|
|
RegisterList(Register reg1, Register reg2, Register reg3, Register reg4)
|
|
: list_(RegisterToList(reg1) | RegisterToList(reg2) |
|
|
RegisterToList(reg3) | RegisterToList(reg4)) {}
|
|
explicit RegisterList(uint32_t list) : list_(list) {}
|
|
uint32_t GetList() const { return list_; }
|
|
void SetList(uint32_t list) { list_ = list; }
|
|
bool Includes(const Register& reg) const {
|
|
return (list_ & RegisterToList(reg)) != 0;
|
|
}
|
|
void Combine(const RegisterList& other) { list_ |= other.GetList(); }
|
|
void Combine(const Register& reg) { list_ |= RegisterToList(reg); }
|
|
void Remove(const RegisterList& other) { list_ &= ~other.GetList(); }
|
|
void Remove(const Register& reg) { list_ &= ~RegisterToList(reg); }
|
|
bool Overlaps(const RegisterList& other) const {
|
|
return (list_ & other.list_) != 0;
|
|
}
|
|
bool IsR0toR7orPC() const {
|
|
// True if all the registers from the list are not from r8-r14.
|
|
return (list_ & 0x7f00) == 0;
|
|
}
|
|
bool IsR0toR7orLR() const {
|
|
// True if all the registers from the list are not from r8-r13 nor from r15.
|
|
return (list_ & 0xbf00) == 0;
|
|
}
|
|
Register GetFirstAvailableRegister() const;
|
|
bool IsEmpty() const { return list_ == 0; }
|
|
static RegisterList Union(const RegisterList& list_1,
|
|
const RegisterList& list_2) {
|
|
return RegisterList(list_1.list_ | list_2.list_);
|
|
}
|
|
static RegisterList Union(const RegisterList& list_1,
|
|
const RegisterList& list_2,
|
|
const RegisterList& list_3) {
|
|
return Union(list_1, Union(list_2, list_3));
|
|
}
|
|
static RegisterList Union(const RegisterList& list_1,
|
|
const RegisterList& list_2,
|
|
const RegisterList& list_3,
|
|
const RegisterList& list_4) {
|
|
return Union(Union(list_1, list_2), Union(list_3, list_4));
|
|
}
|
|
static RegisterList Intersection(const RegisterList& list_1,
|
|
const RegisterList& list_2) {
|
|
return RegisterList(list_1.list_ & list_2.list_);
|
|
}
|
|
static RegisterList Intersection(const RegisterList& list_1,
|
|
const RegisterList& list_2,
|
|
const RegisterList& list_3) {
|
|
return Intersection(list_1, Intersection(list_2, list_3));
|
|
}
|
|
static RegisterList Intersection(const RegisterList& list_1,
|
|
const RegisterList& list_2,
|
|
const RegisterList& list_3,
|
|
const RegisterList& list_4) {
|
|
return Intersection(Intersection(list_1, list_2),
|
|
Intersection(list_3, list_4));
|
|
}
|
|
|
|
private:
|
|
static uint32_t RegisterToList(Register reg) {
|
|
if (reg.GetType() == CPURegister::kNoRegister) {
|
|
return 0;
|
|
} else {
|
|
return UINT32_C(1) << reg.GetCode();
|
|
}
|
|
}
|
|
|
|
// Bitfield representation of all registers in the list
|
|
// (1 for r0, 2 for r1, 4 for r2, ...).
|
|
uint32_t list_;
|
|
};
|
|
|
|
inline uint32_t GetRegisterListEncoding(const RegisterList& registers,
|
|
int first,
|
|
int count) {
|
|
return (registers.GetList() >> first) & ((1 << count) - 1);
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream& os, RegisterList registers);
|
|
|
|
class VRegisterList {
|
|
public:
|
|
VRegisterList() : list_(0) {}
|
|
explicit VRegisterList(VRegister reg) : list_(RegisterToList(reg)) {}
|
|
VRegisterList(VRegister reg1, VRegister reg2)
|
|
: list_(RegisterToList(reg1) | RegisterToList(reg2)) {}
|
|
VRegisterList(VRegister reg1, VRegister reg2, VRegister reg3)
|
|
: list_(RegisterToList(reg1) | RegisterToList(reg2) |
|
|
RegisterToList(reg3)) {}
|
|
VRegisterList(VRegister reg1, VRegister reg2, VRegister reg3, VRegister reg4)
|
|
: list_(RegisterToList(reg1) | RegisterToList(reg2) |
|
|
RegisterToList(reg3) | RegisterToList(reg4)) {}
|
|
explicit VRegisterList(uint64_t list) : list_(list) {}
|
|
uint64_t GetList() const { return list_; }
|
|
void SetList(uint64_t list) { list_ = list; }
|
|
// Because differently-sized V registers overlap with one another, there is no
|
|
// way to implement a single 'Includes' function in a way that is unsurprising
|
|
// for all existing uses.
|
|
bool IncludesAllOf(const VRegister& reg) const {
|
|
return (list_ & RegisterToList(reg)) == RegisterToList(reg);
|
|
}
|
|
bool IncludesAliasOf(const VRegister& reg) const {
|
|
return (list_ & RegisterToList(reg)) != 0;
|
|
}
|
|
void Combine(const VRegisterList& other) { list_ |= other.GetList(); }
|
|
void Combine(const VRegister& reg) { list_ |= RegisterToList(reg); }
|
|
void Remove(const VRegisterList& other) { list_ &= ~other.GetList(); }
|
|
void Remove(const VRegister& reg) { list_ &= ~RegisterToList(reg); }
|
|
bool Overlaps(const VRegisterList& other) const {
|
|
return (list_ & other.list_) != 0;
|
|
}
|
|
QRegister GetFirstAvailableQRegister() const;
|
|
DRegister GetFirstAvailableDRegister() const;
|
|
SRegister GetFirstAvailableSRegister() const;
|
|
bool IsEmpty() const { return list_ == 0; }
|
|
static VRegisterList Union(const VRegisterList& list_1,
|
|
const VRegisterList& list_2) {
|
|
return VRegisterList(list_1.list_ | list_2.list_);
|
|
}
|
|
static VRegisterList Union(const VRegisterList& list_1,
|
|
const VRegisterList& list_2,
|
|
const VRegisterList& list_3) {
|
|
return Union(list_1, Union(list_2, list_3));
|
|
}
|
|
static VRegisterList Union(const VRegisterList& list_1,
|
|
const VRegisterList& list_2,
|
|
const VRegisterList& list_3,
|
|
const VRegisterList& list_4) {
|
|
return Union(Union(list_1, list_2), Union(list_3, list_4));
|
|
}
|
|
static VRegisterList Intersection(const VRegisterList& list_1,
|
|
const VRegisterList& list_2) {
|
|
return VRegisterList(list_1.list_ & list_2.list_);
|
|
}
|
|
static VRegisterList Intersection(const VRegisterList& list_1,
|
|
const VRegisterList& list_2,
|
|
const VRegisterList& list_3) {
|
|
return Intersection(list_1, Intersection(list_2, list_3));
|
|
}
|
|
static VRegisterList Intersection(const VRegisterList& list_1,
|
|
const VRegisterList& list_2,
|
|
const VRegisterList& list_3,
|
|
const VRegisterList& list_4) {
|
|
return Intersection(Intersection(list_1, list_2),
|
|
Intersection(list_3, list_4));
|
|
}
|
|
|
|
private:
|
|
static uint64_t RegisterToList(VRegister reg) {
|
|
if (reg.GetType() == CPURegister::kNoRegister) {
|
|
return 0;
|
|
} else {
|
|
switch (reg.GetSizeInBits()) {
|
|
case kQRegSizeInBits:
|
|
return UINT64_C(0xf) << (reg.GetCode() * 4);
|
|
case kDRegSizeInBits:
|
|
return UINT64_C(0x3) << (reg.GetCode() * 2);
|
|
case kSRegSizeInBits:
|
|
return UINT64_C(0x1) << reg.GetCode();
|
|
default:
|
|
VIXL_UNREACHABLE();
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Bitfield representation of all registers in the list.
|
|
// (0x3 for d0, 0xc0 for d1, 0x30 for d2, ...). We have one, two or four bits
|
|
// per register according to their size. This way we can make sure that we
|
|
// account for overlapping registers.
|
|
// A register is wholly included in this list only if all of its bits are set.
|
|
// A register is aliased by the list if at least one of its bits are set.
|
|
// The IncludesAllOf and IncludesAliasOf helpers are provided to make this
|
|
// distinction clear.
|
|
uint64_t list_;
|
|
};
|
|
|
|
class SRegisterList {
|
|
SRegister first_;
|
|
int length_;
|
|
|
|
public:
|
|
explicit SRegisterList(SRegister reg) : first_(reg.GetCode()), length_(1) {}
|
|
SRegisterList(SRegister first, int length)
|
|
: first_(first.GetCode()), length_(length) {
|
|
VIXL_ASSERT(length >= 0);
|
|
}
|
|
SRegister GetSRegister(int n) const {
|
|
VIXL_ASSERT(n >= 0);
|
|
VIXL_ASSERT(n < length_);
|
|
return SRegister((first_.GetCode() + n) % kNumberOfSRegisters);
|
|
}
|
|
const SRegister& GetFirstSRegister() const { return first_; }
|
|
SRegister GetLastSRegister() const { return GetSRegister(length_ - 1); }
|
|
int GetLength() const { return length_; }
|
|
};
|
|
|
|
std::ostream& operator<<(std::ostream& os, SRegisterList registers);
|
|
|
|
class DRegisterList {
|
|
DRegister first_;
|
|
int length_;
|
|
|
|
public:
|
|
explicit DRegisterList(DRegister reg) : first_(reg.GetCode()), length_(1) {}
|
|
DRegisterList(DRegister first, int length)
|
|
: first_(first.GetCode()), length_(length) {
|
|
VIXL_ASSERT(length >= 0);
|
|
}
|
|
DRegister GetDRegister(int n) const {
|
|
VIXL_ASSERT(n >= 0);
|
|
VIXL_ASSERT(n < length_);
|
|
return DRegister((first_.GetCode() + n) % kMaxNumberOfDRegisters);
|
|
}
|
|
const DRegister& GetFirstDRegister() const { return first_; }
|
|
DRegister GetLastDRegister() const { return GetDRegister(length_ - 1); }
|
|
int GetLength() const { return length_; }
|
|
};
|
|
|
|
std::ostream& operator<<(std::ostream& os, DRegisterList registers);
|
|
|
|
enum SpacingType { kSingle, kDouble };
|
|
|
|
enum TransferType { kMultipleLanes, kOneLane, kAllLanes };
|
|
|
|
class NeonRegisterList {
|
|
DRegister first_;
|
|
SpacingType spacing_;
|
|
TransferType type_;
|
|
int lane_;
|
|
int length_;
|
|
|
|
public:
|
|
NeonRegisterList(DRegister reg, TransferType type)
|
|
: first_(reg.GetCode()),
|
|
spacing_(kSingle),
|
|
type_(type),
|
|
lane_(-1),
|
|
length_(1) {
|
|
VIXL_ASSERT(type_ != kOneLane);
|
|
}
|
|
NeonRegisterList(DRegister reg, int lane)
|
|
: first_(reg.GetCode()),
|
|
spacing_(kSingle),
|
|
type_(kOneLane),
|
|
lane_(lane),
|
|
length_(1) {
|
|
VIXL_ASSERT((lane_ >= 0) && (lane_ < 8));
|
|
}
|
|
NeonRegisterList(DRegister first,
|
|
DRegister last,
|
|
SpacingType spacing,
|
|
TransferType type)
|
|
: first_(first.GetCode()), spacing_(spacing), type_(type), lane_(-1) {
|
|
VIXL_ASSERT(type != kOneLane);
|
|
VIXL_ASSERT(first.GetCode() <= last.GetCode());
|
|
|
|
int range = last.GetCode() - first.GetCode();
|
|
VIXL_ASSERT(IsSingleSpaced() || IsMultiple(range, 2));
|
|
length_ = (IsDoubleSpaced() ? (range / 2) : range) + 1;
|
|
|
|
VIXL_ASSERT(length_ <= 4);
|
|
}
|
|
NeonRegisterList(DRegister first,
|
|
DRegister last,
|
|
SpacingType spacing,
|
|
int lane)
|
|
: first_(first.GetCode()),
|
|
spacing_(spacing),
|
|
type_(kOneLane),
|
|
lane_(lane) {
|
|
VIXL_ASSERT((lane >= 0) && (lane < 8));
|
|
VIXL_ASSERT(first.GetCode() <= last.GetCode());
|
|
|
|
int range = last.GetCode() - first.GetCode();
|
|
VIXL_ASSERT(IsSingleSpaced() || IsMultiple(range, 2));
|
|
length_ = (IsDoubleSpaced() ? (range / 2) : range) + 1;
|
|
|
|
VIXL_ASSERT(length_ <= 4);
|
|
}
|
|
DRegister GetDRegister(int n) const {
|
|
VIXL_ASSERT(n >= 0);
|
|
VIXL_ASSERT(n < length_);
|
|
unsigned code = first_.GetCode() + (IsDoubleSpaced() ? (2 * n) : n);
|
|
VIXL_ASSERT(code < kMaxNumberOfDRegisters);
|
|
return DRegister(code);
|
|
}
|
|
const DRegister& GetFirstDRegister() const { return first_; }
|
|
DRegister GetLastDRegister() const { return GetDRegister(length_ - 1); }
|
|
int GetLength() const { return length_; }
|
|
bool IsSingleSpaced() const { return spacing_ == kSingle; }
|
|
bool IsDoubleSpaced() const { return spacing_ == kDouble; }
|
|
bool IsTransferAllLanes() const { return type_ == kAllLanes; }
|
|
bool IsTransferOneLane() const { return type_ == kOneLane; }
|
|
bool IsTransferMultipleLanes() const { return type_ == kMultipleLanes; }
|
|
int GetTransferLane() const { return lane_; }
|
|
};
|
|
|
|
std::ostream& operator<<(std::ostream& os, NeonRegisterList registers);
|
|
|
|
enum SpecialRegisterType { APSR = 0, CPSR = 0, SPSR = 1 };
|
|
|
|
class SpecialRegister {
|
|
uint32_t reg_;
|
|
|
|
public:
|
|
explicit SpecialRegister(uint32_t reg) : reg_(reg) {}
|
|
SpecialRegister(SpecialRegisterType reg) // NOLINT(runtime/explicit)
|
|
: reg_(reg) {}
|
|
uint32_t GetReg() const { return reg_; }
|
|
const char* GetName() const;
|
|
bool Is(SpecialRegister value) const { return reg_ == value.reg_; }
|
|
bool Is(uint32_t value) const { return reg_ == value; }
|
|
bool IsNot(uint32_t value) const { return reg_ != value; }
|
|
};
|
|
|
|
inline std::ostream& operator<<(std::ostream& os, SpecialRegister reg) {
|
|
return os << reg.GetName();
|
|
}
|
|
|
|
enum BankedRegisterType {
|
|
R8_usr = 0x00,
|
|
R9_usr = 0x01,
|
|
R10_usr = 0x02,
|
|
R11_usr = 0x03,
|
|
R12_usr = 0x04,
|
|
SP_usr = 0x05,
|
|
LR_usr = 0x06,
|
|
R8_fiq = 0x08,
|
|
R9_fiq = 0x09,
|
|
R10_fiq = 0x0a,
|
|
R11_fiq = 0x0b,
|
|
R12_fiq = 0x0c,
|
|
SP_fiq = 0x0d,
|
|
LR_fiq = 0x0e,
|
|
LR_irq = 0x10,
|
|
SP_irq = 0x11,
|
|
LR_svc = 0x12,
|
|
SP_svc = 0x13,
|
|
LR_abt = 0x14,
|
|
SP_abt = 0x15,
|
|
LR_und = 0x16,
|
|
SP_und = 0x17,
|
|
LR_mon = 0x1c,
|
|
SP_mon = 0x1d,
|
|
ELR_hyp = 0x1e,
|
|
SP_hyp = 0x1f,
|
|
SPSR_fiq = 0x2e,
|
|
SPSR_irq = 0x30,
|
|
SPSR_svc = 0x32,
|
|
SPSR_abt = 0x34,
|
|
SPSR_und = 0x36,
|
|
SPSR_mon = 0x3c,
|
|
SPSR_hyp = 0x3e
|
|
};
|
|
|
|
class BankedRegister {
|
|
uint32_t reg_;
|
|
|
|
public:
|
|
explicit BankedRegister(unsigned reg) : reg_(reg) {}
|
|
BankedRegister(BankedRegisterType reg) // NOLINT(runtime/explicit)
|
|
: reg_(reg) {}
|
|
uint32_t GetCode() const { return reg_; }
|
|
const char* GetName() const;
|
|
};
|
|
|
|
inline std::ostream& operator<<(std::ostream& os, BankedRegister reg) {
|
|
return os << reg.GetName();
|
|
}
|
|
|
|
enum MaskedSpecialRegisterType {
|
|
APSR_nzcvq = 0x08,
|
|
APSR_g = 0x04,
|
|
APSR_nzcvqg = 0x0c,
|
|
CPSR_c = 0x01,
|
|
CPSR_x = 0x02,
|
|
CPSR_xc = 0x03,
|
|
CPSR_s = APSR_g,
|
|
CPSR_sc = 0x05,
|
|
CPSR_sx = 0x06,
|
|
CPSR_sxc = 0x07,
|
|
CPSR_f = APSR_nzcvq,
|
|
CPSR_fc = 0x09,
|
|
CPSR_fx = 0x0a,
|
|
CPSR_fxc = 0x0b,
|
|
CPSR_fs = APSR_nzcvqg,
|
|
CPSR_fsc = 0x0d,
|
|
CPSR_fsx = 0x0e,
|
|
CPSR_fsxc = 0x0f,
|
|
SPSR_c = 0x11,
|
|
SPSR_x = 0x12,
|
|
SPSR_xc = 0x13,
|
|
SPSR_s = 0x14,
|
|
SPSR_sc = 0x15,
|
|
SPSR_sx = 0x16,
|
|
SPSR_sxc = 0x17,
|
|
SPSR_f = 0x18,
|
|
SPSR_fc = 0x19,
|
|
SPSR_fx = 0x1a,
|
|
SPSR_fxc = 0x1b,
|
|
SPSR_fs = 0x1c,
|
|
SPSR_fsc = 0x1d,
|
|
SPSR_fsx = 0x1e,
|
|
SPSR_fsxc = 0x1f
|
|
};
|
|
|
|
class MaskedSpecialRegister {
|
|
uint32_t reg_;
|
|
|
|
public:
|
|
explicit MaskedSpecialRegister(uint32_t reg) : reg_(reg) {
|
|
VIXL_ASSERT(reg <= SPSR_fsxc);
|
|
}
|
|
MaskedSpecialRegister(
|
|
MaskedSpecialRegisterType reg) // NOLINT(runtime/explicit)
|
|
: reg_(reg) {}
|
|
uint32_t GetReg() const { return reg_; }
|
|
const char* GetName() const;
|
|
bool Is(MaskedSpecialRegister value) const { return reg_ == value.reg_; }
|
|
bool Is(uint32_t value) const { return reg_ == value; }
|
|
bool IsNot(uint32_t value) const { return reg_ != value; }
|
|
};
|
|
|
|
inline std::ostream& operator<<(std::ostream& os, MaskedSpecialRegister reg) {
|
|
return os << reg.GetName();
|
|
}
|
|
|
|
enum SpecialFPRegisterType {
|
|
FPSID = 0x0,
|
|
FPSCR = 0x1,
|
|
MVFR2 = 0x5,
|
|
MVFR1 = 0x6,
|
|
MVFR0 = 0x7,
|
|
FPEXC = 0x8
|
|
};
|
|
|
|
class SpecialFPRegister {
|
|
uint32_t reg_;
|
|
|
|
public:
|
|
explicit SpecialFPRegister(uint32_t reg) : reg_(reg) {
|
|
#ifdef VIXL_DEBUG
|
|
switch (reg) {
|
|
case FPSID:
|
|
case FPSCR:
|
|
case MVFR2:
|
|
case MVFR1:
|
|
case MVFR0:
|
|
case FPEXC:
|
|
break;
|
|
default:
|
|
VIXL_UNREACHABLE();
|
|
}
|
|
#endif
|
|
}
|
|
SpecialFPRegister(SpecialFPRegisterType reg) // NOLINT(runtime/explicit)
|
|
: reg_(reg) {}
|
|
uint32_t GetReg() const { return reg_; }
|
|
const char* GetName() const;
|
|
bool Is(SpecialFPRegister value) const { return reg_ == value.reg_; }
|
|
bool Is(uint32_t value) const { return reg_ == value; }
|
|
bool IsNot(uint32_t value) const { return reg_ != value; }
|
|
};
|
|
|
|
inline std::ostream& operator<<(std::ostream& os, SpecialFPRegister reg) {
|
|
return os << reg.GetName();
|
|
}
|
|
|
|
class CRegister {
|
|
uint32_t code_;
|
|
|
|
public:
|
|
explicit CRegister(uint32_t code) : code_(code) {
|
|
VIXL_ASSERT(code < kNumberOfRegisters);
|
|
}
|
|
uint32_t GetCode() const { return code_; }
|
|
bool Is(CRegister value) const { return code_ == value.code_; }
|
|
};
|
|
|
|
inline std::ostream& operator<<(std::ostream& os, const CRegister reg) {
|
|
return os << "c" << reg.GetCode();
|
|
}
|
|
|
|
// clang-format off
|
|
#define CREGISTER_CODE_LIST(R) \
|
|
R(0) R(1) R(2) R(3) R(4) R(5) R(6) R(7) \
|
|
R(8) R(9) R(10) R(11) R(12) R(13) R(14) R(15)
|
|
// clang-format on
|
|
#define DEFINE_CREGISTER(N) const CRegister c##N(N);
|
|
CREGISTER_CODE_LIST(DEFINE_CREGISTER)
|
|
|
|
enum CoprocessorName { p10 = 10, p11 = 11, p14 = 14, p15 = 15 };
|
|
|
|
class Coprocessor {
|
|
uint32_t coproc_;
|
|
|
|
public:
|
|
explicit Coprocessor(uint32_t coproc) : coproc_(coproc) {}
|
|
Coprocessor(CoprocessorName coproc) // NOLINT(runtime/explicit)
|
|
: coproc_(static_cast<uint32_t>(coproc)) {}
|
|
bool Is(Coprocessor coproc) const { return coproc_ == coproc.coproc_; }
|
|
bool Is(CoprocessorName coproc) const { return coproc_ == coproc; }
|
|
uint32_t GetCoprocessor() const { return coproc_; }
|
|
};
|
|
|
|
inline std::ostream& operator<<(std::ostream& os, Coprocessor coproc) {
|
|
return os << "p" << coproc.GetCoprocessor();
|
|
}
|
|
|
|
enum ConditionType {
|
|
eq = 0,
|
|
ne = 1,
|
|
cs = 2,
|
|
cc = 3,
|
|
mi = 4,
|
|
pl = 5,
|
|
vs = 6,
|
|
vc = 7,
|
|
hi = 8,
|
|
ls = 9,
|
|
ge = 10,
|
|
lt = 11,
|
|
gt = 12,
|
|
le = 13,
|
|
al = 14,
|
|
hs = cs,
|
|
lo = cc
|
|
};
|
|
|
|
class Condition {
|
|
uint32_t condition_;
|
|
static const uint32_t kNever = 15;
|
|
static const uint32_t kMask = 0xf;
|
|
static const uint32_t kNone = 0x10 | al;
|
|
|
|
public:
|
|
static const Condition None() { return Condition(kNone); }
|
|
static const Condition Never() { return Condition(kNever); }
|
|
explicit Condition(uint32_t condition) : condition_(condition) {
|
|
VIXL_ASSERT(condition <= kNone);
|
|
}
|
|
// Users should be able to use "eq", "ne" and so forth to instantiate this
|
|
// class.
|
|
Condition(ConditionType condition) // NOLINT(runtime/explicit)
|
|
: condition_(condition) {}
|
|
uint32_t GetCondition() const { return condition_ & kMask; }
|
|
bool IsNone() const { return condition_ == kNone; }
|
|
const char* GetName() const;
|
|
bool Is(Condition value) const { return condition_ == value.condition_; }
|
|
bool Is(uint32_t value) const { return condition_ == value; }
|
|
bool IsNot(uint32_t value) const { return condition_ != value; }
|
|
bool IsNever() const { return condition_ == kNever; }
|
|
bool IsNotNever() const { return condition_ != kNever; }
|
|
Condition Negate() const {
|
|
VIXL_ASSERT(IsNot(al) && IsNot(kNever));
|
|
return Condition(condition_ ^ 1);
|
|
}
|
|
};
|
|
|
|
inline std::ostream& operator<<(std::ostream& os, Condition condition) {
|
|
return os << condition.GetName();
|
|
}
|
|
|
|
enum SignType { plus, minus };
|
|
|
|
class Sign {
|
|
public:
|
|
Sign() : sign_(plus) {}
|
|
Sign(SignType sign) : sign_(sign) {} // NOLINT(runtime/explicit)
|
|
const char* GetName() const { return (IsPlus() ? "" : "-"); }
|
|
bool IsPlus() const { return sign_ == plus; }
|
|
bool IsMinus() const { return sign_ == minus; }
|
|
int32_t ApplyTo(uint32_t value) { return IsPlus() ? value : -value; }
|
|
|
|
private:
|
|
SignType sign_;
|
|
};
|
|
|
|
inline std::ostream& operator<<(std::ostream& os, Sign sign) {
|
|
return os << sign.GetName();
|
|
}
|
|
|
|
enum ShiftType { LSL = 0x0, LSR = 0x1, ASR = 0x2, ROR = 0x3, RRX = 0x4 };
|
|
|
|
class Shift {
|
|
public:
|
|
Shift() : shift_(LSL) {}
|
|
Shift(ShiftType shift) : shift_(shift) {} // NOLINT(runtime/explicit)
|
|
explicit Shift(uint32_t shift) : shift_(static_cast<ShiftType>(shift)) {}
|
|
const Shift& GetShift() const { return *this; }
|
|
ShiftType GetType() const { return shift_; }
|
|
uint32_t GetValue() const { return shift_; }
|
|
const char* GetName() const;
|
|
bool IsLSL() const { return shift_ == LSL; }
|
|
bool IsLSR() const { return shift_ == LSR; }
|
|
bool IsASR() const { return shift_ == ASR; }
|
|
bool IsROR() const { return shift_ == ROR; }
|
|
bool IsRRX() const { return shift_ == RRX; }
|
|
bool Is(Shift value) const { return shift_ == value.shift_; }
|
|
bool IsNot(Shift value) const { return shift_ != value.shift_; }
|
|
bool IsValidAmount(uint32_t amount) const;
|
|
static const Shift NoShift;
|
|
|
|
protected:
|
|
void SetType(ShiftType s) { shift_ = s; }
|
|
|
|
private:
|
|
ShiftType shift_;
|
|
};
|
|
|
|
inline std::ostream& operator<<(std::ostream& os, Shift shift) {
|
|
return os << shift.GetName();
|
|
}
|
|
|
|
class ImmediateShiftOperand : public Shift {
|
|
public:
|
|
// Constructor used for assembly.
|
|
ImmediateShiftOperand(Shift shift, uint32_t amount)
|
|
: Shift(shift), amount_(amount) {
|
|
#ifdef VIXL_DEBUG
|
|
switch (shift.GetType()) {
|
|
case LSL:
|
|
VIXL_ASSERT(amount <= 31);
|
|
break;
|
|
case ROR:
|
|
VIXL_ASSERT(amount > 0);
|
|
VIXL_ASSERT(amount <= 31);
|
|
break;
|
|
case LSR:
|
|
case ASR:
|
|
VIXL_ASSERT(amount > 0);
|
|
VIXL_ASSERT(amount <= 32);
|
|
break;
|
|
case RRX:
|
|
VIXL_ASSERT(amount == 0);
|
|
break;
|
|
default:
|
|
VIXL_UNREACHABLE();
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
// Constructor used for disassembly.
|
|
ImmediateShiftOperand(int shift, int amount);
|
|
uint32_t GetAmount() const { return amount_; }
|
|
bool Is(const ImmediateShiftOperand& rhs) const {
|
|
return amount_ == (rhs.amount_) && Shift::Is(*this);
|
|
}
|
|
|
|
private:
|
|
uint32_t amount_;
|
|
};
|
|
|
|
inline std::ostream& operator<<(std::ostream& os,
|
|
ImmediateShiftOperand const& shift_operand) {
|
|
if (shift_operand.IsLSL() && shift_operand.GetAmount() == 0) return os;
|
|
if (shift_operand.IsRRX()) return os << ", rrx";
|
|
return os << ", " << shift_operand.GetName() << " #"
|
|
<< shift_operand.GetAmount();
|
|
}
|
|
|
|
class RegisterShiftOperand : public Shift {
|
|
public:
|
|
RegisterShiftOperand(ShiftType shift, Register shift_register)
|
|
: Shift(shift), shift_register_(shift_register) {
|
|
VIXL_ASSERT(!IsRRX() && shift_register_.IsValid());
|
|
}
|
|
const Register GetShiftRegister() const { return shift_register_; }
|
|
bool Is(const RegisterShiftOperand& rhs) const {
|
|
return shift_register_.Is(rhs.shift_register_) && Shift::Is(*this);
|
|
}
|
|
|
|
private:
|
|
Register shift_register_;
|
|
};
|
|
|
|
inline std::ostream& operator<<(std::ostream& s,
|
|
const RegisterShiftOperand& shift_operand) {
|
|
return s << shift_operand.GetName() << " "
|
|
<< shift_operand.GetShiftRegister();
|
|
}
|
|
|
|
enum EncodingSizeType { Best, Narrow, Wide };
|
|
|
|
class EncodingSize {
|
|
uint32_t size_;
|
|
|
|
public:
|
|
explicit EncodingSize(uint32_t size) : size_(size) {}
|
|
EncodingSize(EncodingSizeType size) // NOLINT(runtime/explicit)
|
|
: size_(size) {}
|
|
uint32_t GetSize() const { return size_; }
|
|
const char* GetName() const;
|
|
bool IsBest() const { return size_ == Best; }
|
|
bool IsNarrow() const { return size_ == Narrow; }
|
|
bool IsWide() const { return size_ == Wide; }
|
|
};
|
|
|
|
inline std::ostream& operator<<(std::ostream& os, EncodingSize size) {
|
|
return os << size.GetName();
|
|
}
|
|
|
|
enum WriteBackValue { NO_WRITE_BACK, WRITE_BACK };
|
|
|
|
class WriteBack {
|
|
WriteBackValue value_;
|
|
|
|
public:
|
|
WriteBack(WriteBackValue value) // NOLINT(runtime/explicit)
|
|
: value_(value) {}
|
|
explicit WriteBack(int value)
|
|
: value_((value == 0) ? NO_WRITE_BACK : WRITE_BACK) {}
|
|
uint32_t GetWriteBackUint32() const { return (value_ == WRITE_BACK) ? 1 : 0; }
|
|
bool DoesWriteBack() const { return value_ == WRITE_BACK; }
|
|
};
|
|
|
|
inline std::ostream& operator<<(std::ostream& os, WriteBack write_back) {
|
|
if (write_back.DoesWriteBack()) return os << "!";
|
|
return os;
|
|
}
|
|
|
|
class EncodingValue {
|
|
bool valid_;
|
|
uint32_t encoding_value_;
|
|
|
|
public:
|
|
EncodingValue() {
|
|
valid_ = false;
|
|
encoding_value_ = 0;
|
|
}
|
|
bool IsValid() const { return valid_; }
|
|
uint32_t GetEncodingValue() const { return encoding_value_; }
|
|
void SetEncodingValue(uint32_t encoding_value) {
|
|
valid_ = true;
|
|
encoding_value_ = encoding_value;
|
|
}
|
|
};
|
|
|
|
class EncodingValueAndImmediate : public EncodingValue {
|
|
uint32_t encoded_immediate_;
|
|
|
|
public:
|
|
EncodingValueAndImmediate() { encoded_immediate_ = 0; }
|
|
uint32_t GetEncodedImmediate() const { return encoded_immediate_; }
|
|
void SetEncodedImmediate(uint32_t encoded_immediate) {
|
|
encoded_immediate_ = encoded_immediate;
|
|
}
|
|
};
|
|
|
|
class ImmediateT32 : public EncodingValue {
|
|
public:
|
|
explicit ImmediateT32(uint32_t imm);
|
|
static bool IsImmediateT32(uint32_t imm);
|
|
static uint32_t Decode(uint32_t value);
|
|
};
|
|
|
|
class ImmediateA32 : public EncodingValue {
|
|
public:
|
|
explicit ImmediateA32(uint32_t imm);
|
|
static bool IsImmediateA32(uint32_t imm);
|
|
static uint32_t Decode(uint32_t value);
|
|
};
|
|
|
|
// Return the encoding value of a shift type.
|
|
uint32_t TypeEncodingValue(Shift shift);
|
|
// Return the encoding value for a shift amount depending on the shift type.
|
|
uint32_t AmountEncodingValue(Shift shift, uint32_t amount);
|
|
|
|
enum MemoryBarrierType {
|
|
OSHLD = 0x1,
|
|
OSHST = 0x2,
|
|
OSH = 0x3,
|
|
NSHLD = 0x5,
|
|
NSHST = 0x6,
|
|
NSH = 0x7,
|
|
ISHLD = 0x9,
|
|
ISHST = 0xa,
|
|
ISH = 0xb,
|
|
LD = 0xd,
|
|
ST = 0xe,
|
|
SY = 0xf
|
|
};
|
|
|
|
class MemoryBarrier {
|
|
MemoryBarrierType type_;
|
|
|
|
public:
|
|
MemoryBarrier(MemoryBarrierType type) // NOLINT(runtime/explicit)
|
|
: type_(type) {}
|
|
MemoryBarrier(uint32_t type) // NOLINT(runtime/explicit)
|
|
: type_(static_cast<MemoryBarrierType>(type)) {
|
|
VIXL_ASSERT((type & 0x3) != 0);
|
|
}
|
|
MemoryBarrierType GetType() const { return type_; }
|
|
const char* GetName() const;
|
|
};
|
|
|
|
inline std::ostream& operator<<(std::ostream& os, MemoryBarrier option) {
|
|
return os << option.GetName();
|
|
}
|
|
|
|
enum InterruptFlagsType {
|
|
F = 0x1,
|
|
I = 0x2,
|
|
IF = 0x3,
|
|
A = 0x4,
|
|
AF = 0x5,
|
|
AI = 0x6,
|
|
AIF = 0x7
|
|
};
|
|
|
|
class InterruptFlags {
|
|
InterruptFlagsType type_;
|
|
|
|
public:
|
|
InterruptFlags(InterruptFlagsType type) // NOLINT(runtime/explicit)
|
|
: type_(type) {}
|
|
InterruptFlags(uint32_t type) // NOLINT(runtime/explicit)
|
|
: type_(static_cast<InterruptFlagsType>(type)) {
|
|
VIXL_ASSERT(type <= 7);
|
|
}
|
|
InterruptFlagsType GetType() const { return type_; }
|
|
const char* GetName() const;
|
|
};
|
|
|
|
inline std::ostream& operator<<(std::ostream& os, InterruptFlags option) {
|
|
return os << option.GetName();
|
|
}
|
|
|
|
enum EndiannessType { LE = 0, BE = 1 };
|
|
|
|
class Endianness {
|
|
EndiannessType type_;
|
|
|
|
public:
|
|
Endianness(EndiannessType type) : type_(type) {} // NOLINT(runtime/explicit)
|
|
Endianness(uint32_t type) // NOLINT(runtime/explicit)
|
|
: type_(static_cast<EndiannessType>(type)) {
|
|
VIXL_ASSERT(type <= 1);
|
|
}
|
|
EndiannessType GetType() const { return type_; }
|
|
const char* GetName() const;
|
|
};
|
|
|
|
inline std::ostream& operator<<(std::ostream& os, Endianness endian_specifier) {
|
|
return os << endian_specifier.GetName();
|
|
}
|
|
|
|
enum AlignmentType {
|
|
k16BitAlign = 0,
|
|
k32BitAlign = 1,
|
|
k64BitAlign = 2,
|
|
k128BitAlign = 3,
|
|
k256BitAlign = 4,
|
|
kNoAlignment = 5,
|
|
kBadAlignment = 6
|
|
};
|
|
|
|
class Alignment {
|
|
AlignmentType align_;
|
|
|
|
public:
|
|
Alignment(AlignmentType align) // NOLINT(runtime/explicit)
|
|
: align_(align) {}
|
|
Alignment(uint32_t align) // NOLINT(runtime/explicit)
|
|
: align_(static_cast<AlignmentType>(align)) {
|
|
VIXL_ASSERT(align <= static_cast<uint32_t>(k256BitAlign));
|
|
}
|
|
AlignmentType GetType() const { return align_; }
|
|
bool Is(AlignmentType type) { return align_ == type; }
|
|
};
|
|
|
|
inline std::ostream& operator<<(std::ostream& os, Alignment align) {
|
|
if (align.GetType() == kBadAlignment) return os << " :??";
|
|
if (align.GetType() == kNoAlignment) return os;
|
|
return os << " :" << (0x10 << static_cast<uint32_t>(align.GetType()));
|
|
}
|
|
|
|
// Structure containing information on forward references.
|
|
struct ReferenceInfo {
|
|
int size;
|
|
int min_offset;
|
|
int max_offset;
|
|
int alignment; // As a power of two.
|
|
enum { kAlignPc, kDontAlignPc } pc_needs_aligning;
|
|
};
|
|
|
|
} // namespace aarch32
|
|
} // namespace vixl
|
|
|
|
#endif // VIXL_AARCH32_INSTRUCTIONS_AARCH32_H_
|