Dep: Update vixl to 662828c

This commit is contained in:
Stenzek
2024-06-14 17:27:12 +10:00
parent d45e218da7
commit f0c2832d03
66 changed files with 65453 additions and 10345 deletions
@@ -27,10 +27,10 @@
#ifndef VIXL_AARCH32_ASSEMBLER_AARCH32_H_
#define VIXL_AARCH32_ASSEMBLER_AARCH32_H_
#include "../assembler-base-vixl.h"
#include "assembler-base-vixl.h"
#include "instructions-aarch32.h"
#include "location-aarch32.h"
#include "aarch32/instructions-aarch32.h"
#include "aarch32/location-aarch32.h"
namespace vixl {
namespace aarch32 {
@@ -32,7 +32,7 @@ extern "C" {
#include <stdint.h>
}
#include "../globals-vixl.h"
#include "globals-vixl.h"
namespace vixl {
@@ -33,8 +33,14 @@ extern "C" {
#include <iomanip>
#include "constants-aarch32.h"
#include "operands-aarch32.h"
#include "aarch32/constants-aarch32.h"
#include "aarch32/operands-aarch32.h"
// Microsoft Visual C++ defines a `mvn` macro that conflicts with our own
// definition.
#if defined(_MSC_VER) && defined(mvn)
#undef mvn
#endif
namespace vixl {
namespace aarch32 {
@@ -34,13 +34,14 @@ extern "C" {
#include <algorithm>
#include <ostream>
#include "../code-buffer-vixl.h"
#include "../utils-vixl.h"
#include "code-buffer-vixl.h"
#include "utils-vixl.h"
#include "aarch32/constants-aarch32.h"
#include "constants-aarch32.h"
#ifdef __arm__
#if defined(__arm__) && !defined(__SOFTFP__)
#define HARDFLOAT __attribute__((noinline, pcs("aapcs-vfp")))
#elif defined(_MSC_VER)
#define HARDFLOAT __declspec(noinline)
#else
#define HARDFLOAT __attribute__((noinline))
#endif
@@ -492,6 +493,8 @@ class RegisterList {
}
Register GetFirstAvailableRegister() const;
bool IsEmpty() const { return list_ == 0; }
bool IsSingleRegister() const { return IsPowerOf2(list_); }
int GetCount() const { return CountSetBits(list_); }
static RegisterList Union(const RegisterList& list_1,
const RegisterList& list_2) {
return RegisterList(list_1.list_ | list_2.list_);
@@ -1039,7 +1042,9 @@ class Sign {
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; }
int32_t ApplyTo(uint32_t value) {
return IsPlus() ? value : UnsignedNegate(value);
}
private:
SignType sign_;
@@ -36,9 +36,9 @@ extern "C" {
#include <iomanip>
#include <list>
#include "../invalset-vixl.h"
#include "../pool-manager.h"
#include "../utils-vixl.h"
#include "invalset-vixl.h"
#include "pool-manager.h"
#include "utils-vixl.h"
#include "constants-aarch32.h"
#include "instructions-aarch32.h"
@@ -58,12 +58,12 @@ class Location : public LocationBase<int32_t> {
// with the assembler methods for generating instructions, but will never
// be handled by the pool manager.
Location()
: LocationBase<int32_t>(kRawLocation, 1 /* dummy size*/),
: LocationBase<int32_t>(kRawLocation, 1 /* placeholder size*/),
referenced_(false) {}
typedef int32_t Offset;
~Location() {
~Location() VIXL_NEGATIVE_TESTING_ALLOW_EXCEPTION {
#ifdef VIXL_DEBUG
if (IsReferenced() && !IsBound()) {
VIXL_ABORT_WITH_MSG("Location, label or literal used but not bound.\n");
@@ -217,7 +217,7 @@ class Location : public LocationBase<int32_t> {
protected:
// Types passed to LocationBase. Must be distinct for unbound Locations (not
// relevant for bound locations, as they don't have a correspoding
// relevant for bound locations, as they don't have a corresponding
// PoolObject).
static const int kRawLocation = 0; // Will not be used by the pool manager.
static const int kVeneerType = 1;
@@ -28,15 +28,15 @@
#ifndef VIXL_AARCH32_MACRO_ASSEMBLER_AARCH32_H_
#define VIXL_AARCH32_MACRO_ASSEMBLER_AARCH32_H_
#include "../code-generation-scopes-vixl.h"
#include "../macro-assembler-interface.h"
#include "../pool-manager-impl.h"
#include "../pool-manager.h"
#include "../utils-vixl.h"
#include "code-generation-scopes-vixl.h"
#include "macro-assembler-interface.h"
#include "pool-manager-impl.h"
#include "pool-manager.h"
#include "utils-vixl.h"
#include "assembler-aarch32.h"
#include "instructions-aarch32.h"
#include "operands-aarch32.h"
#include "aarch32/assembler-aarch32.h"
#include "aarch32/instructions-aarch32.h"
#include "aarch32/operands-aarch32.h"
namespace vixl {
@@ -268,7 +268,8 @@ class MacroAssembler : public Assembler, public MacroAssemblerInterface {
generate_simulator_code_(VIXL_AARCH32_GENERATE_SIMULATOR_CODE),
pool_end_(NULL) {
#ifdef VIXL_DEBUG
SetAllowMacroInstructions(true);
SetAllowMacroInstructions( // NOLINT(clang-analyzer-optin.cplusplus.VirtualCall)
true);
#else
USE(allow_macro_instructions_);
#endif
@@ -283,7 +284,8 @@ class MacroAssembler : public Assembler, public MacroAssemblerInterface {
generate_simulator_code_(VIXL_AARCH32_GENERATE_SIMULATOR_CODE),
pool_end_(NULL) {
#ifdef VIXL_DEBUG
SetAllowMacroInstructions(true);
SetAllowMacroInstructions( // NOLINT(clang-analyzer-optin.cplusplus.VirtualCall)
true);
#endif
}
MacroAssembler(byte* buffer, size_t size, InstructionSet isa = kDefaultISA)
@@ -296,7 +298,8 @@ class MacroAssembler : public Assembler, public MacroAssemblerInterface {
generate_simulator_code_(VIXL_AARCH32_GENERATE_SIMULATOR_CODE),
pool_end_(NULL) {
#ifdef VIXL_DEBUG
SetAllowMacroInstructions(true);
SetAllowMacroInstructions( // NOLINT(clang-analyzer-optin.cplusplus.VirtualCall)
true);
#endif
}
@@ -399,13 +402,13 @@ class MacroAssembler : public Assembler, public MacroAssemblerInterface {
VIXL_ASSERT(GetBuffer()->Is32bitAligned());
}
// If we need to add padding, check if we have to emit the pool.
const int32_t pc = GetCursorOffset();
if (label->Needs16BitPadding(pc)) {
const int32_t cursor = GetCursorOffset();
if (label->Needs16BitPadding(cursor)) {
const int kPaddingBytes = 2;
if (pool_manager_.MustEmit(pc, kPaddingBytes)) {
int32_t new_pc = pool_manager_.Emit(this, pc, kPaddingBytes);
USE(new_pc);
VIXL_ASSERT(new_pc == GetCursorOffset());
if (pool_manager_.MustEmit(cursor, kPaddingBytes)) {
int32_t new_cursor = pool_manager_.Emit(this, cursor, kPaddingBytes);
USE(new_cursor);
VIXL_ASSERT(new_cursor == GetCursorOffset());
}
}
pool_manager_.Bind(this, label, GetCursorOffset());
@@ -427,30 +430,30 @@ class MacroAssembler : public Assembler, public MacroAssemblerInterface {
Location* location,
Condition* cond = NULL) {
int size = info->size;
int32_t pc = GetCursorOffset();
int32_t cursor = GetCursorOffset();
// If we need to emit a branch over the instruction, take this into account.
if ((cond != NULL) && NeedBranch(cond)) {
size += kBranchSize;
pc += kBranchSize;
cursor += kBranchSize;
}
int32_t from = pc;
int32_t from = cursor;
from += IsUsingT32() ? kT32PcDelta : kA32PcDelta;
if (info->pc_needs_aligning) from = AlignDown(from, 4);
int32_t min = from + info->min_offset;
int32_t max = from + info->max_offset;
ForwardReference<int32_t> temp_ref(pc,
ForwardReference<int32_t> temp_ref(cursor,
info->size,
min,
max,
info->alignment);
if (pool_manager_.MustEmit(GetCursorOffset(), size, &temp_ref, location)) {
int32_t new_pc = pool_manager_.Emit(this,
GetCursorOffset(),
info->size,
&temp_ref,
location);
USE(new_pc);
VIXL_ASSERT(new_pc == GetCursorOffset());
int32_t new_cursor = pool_manager_.Emit(this,
GetCursorOffset(),
info->size,
&temp_ref,
location);
USE(new_cursor);
VIXL_ASSERT(new_cursor == GetCursorOffset());
}
}
@@ -461,13 +464,13 @@ class MacroAssembler : public Assembler, public MacroAssemblerInterface {
// into account, as well as potential 16-bit padding needed to reach the
// minimum accessible location.
int alignment = literal->GetMaxAlignment();
int32_t pc = GetCursorOffset();
int total_size = AlignUp(pc, alignment) - pc + literal->GetSize();
if (literal->Needs16BitPadding(pc)) total_size += 2;
if (pool_manager_.MustEmit(pc, total_size)) {
int32_t new_pc = pool_manager_.Emit(this, pc, total_size);
USE(new_pc);
VIXL_ASSERT(new_pc == GetCursorOffset());
int32_t cursor = GetCursorOffset();
int total_size = AlignUp(cursor, alignment) - cursor + literal->GetSize();
if (literal->Needs16BitPadding(cursor)) total_size += 2;
if (pool_manager_.MustEmit(cursor, total_size)) {
int32_t new_cursor = pool_manager_.Emit(this, cursor, total_size);
USE(new_cursor);
VIXL_ASSERT(new_cursor == GetCursorOffset());
}
pool_manager_.Bind(this, literal, GetCursorOffset());
literal->EmitPoolObject(this);
@@ -2894,7 +2897,12 @@ class MacroAssembler : public Assembler, public MacroAssemblerInterface {
VIXL_ASSERT(OutsideITBlock());
MacroEmissionCheckScope guard(this);
ITScope it_scope(this, &cond, guard);
pop(cond, registers);
if (registers.IsSingleRegister() &&
(!IsUsingT32() || !registers.IsR0toR7orPC())) {
pop(cond, registers.GetFirstAvailableRegister());
} else if (!registers.IsEmpty()) {
pop(cond, registers);
}
}
void Pop(RegisterList registers) { Pop(al, registers); }
@@ -2914,7 +2922,12 @@ class MacroAssembler : public Assembler, public MacroAssemblerInterface {
VIXL_ASSERT(OutsideITBlock());
MacroEmissionCheckScope guard(this);
ITScope it_scope(this, &cond, guard);
push(cond, registers);
if (registers.IsSingleRegister() && !registers.Includes(sp) &&
(!IsUsingT32() || !registers.IsR0toR7orLR())) {
push(cond, registers.GetFirstAvailableRegister());
} else if (!registers.IsEmpty()) {
push(cond, registers);
}
}
void Push(RegisterList registers) { Push(al, registers); }
@@ -2924,7 +2937,12 @@ class MacroAssembler : public Assembler, public MacroAssemblerInterface {
VIXL_ASSERT(OutsideITBlock());
MacroEmissionCheckScope guard(this);
ITScope it_scope(this, &cond, guard);
push(cond, rt);
if (IsUsingA32() && rt.IsSP()) {
// Only the A32 multiple-register form can push sp.
push(cond, RegisterList(rt));
} else {
push(cond, rt);
}
}
void Push(Register rt) { Push(al, rt); }
@@ -11170,10 +11188,11 @@ class UseScratchRegisterScope {
uint32_t old_available_; // kRRegister
uint64_t old_available_vfp_; // kVRegister
VIXL_DEBUG_NO_RETURN UseScratchRegisterScope(const UseScratchRegisterScope&) {
VIXL_NO_RETURN_IN_DEBUG_MODE UseScratchRegisterScope(
const UseScratchRegisterScope&) {
VIXL_UNREACHABLE();
}
VIXL_DEBUG_NO_RETURN void operator=(const UseScratchRegisterScope&) {
VIXL_NO_RETURN_IN_DEBUG_MODE void operator=(const UseScratchRegisterScope&) {
VIXL_UNREACHABLE();
}
};
@@ -28,7 +28,7 @@
#ifndef VIXL_AARCH32_OPERANDS_AARCH32_H_
#define VIXL_AARCH32_OPERANDS_AARCH32_H_
#include "instructions-aarch32.h"
#include "aarch32/instructions-aarch32.h"
namespace vixl {
namespace aarch32 {
@@ -54,28 +54,16 @@ class Operand {
// This is allowed to be an implicit constructor because Operand is
// a wrapper class that doesn't normally perform any type conversion.
Operand(uint32_t immediate) // NOLINT(runtime/explicit)
: imm_(immediate),
rm_(NoReg),
shift_(LSL),
amount_(0),
rs_(NoReg) {}
: imm_(immediate), rm_(NoReg), shift_(LSL), amount_(0), rs_(NoReg) {}
Operand(int32_t immediate) // NOLINT(runtime/explicit)
: imm_(immediate),
rm_(NoReg),
shift_(LSL),
amount_(0),
rs_(NoReg) {}
: imm_(immediate), rm_(NoReg), shift_(LSL), amount_(0), rs_(NoReg) {}
// rm
// where rm is the base register
// This is allowed to be an implicit constructor because Operand is
// a wrapper class that doesn't normally perform any type conversion.
Operand(Register rm) // NOLINT(runtime/explicit)
: imm_(0),
rm_(rm),
shift_(LSL),
amount_(0),
rs_(NoReg) {
: imm_(0), rm_(rm), shift_(LSL), amount_(0), rs_(NoReg) {
VIXL_ASSERT(rm_.IsValid());
}
@@ -202,7 +190,7 @@ class Operand {
}
private:
// Forbid implicitely creating operands around types that cannot be encoded
// Forbid implicitly creating operands around types that cannot be encoded
// into a uint32_t without loss.
#if __cplusplus >= 201103L
Operand(int64_t) = delete; // NOLINT(runtime/explicit)
@@ -245,22 +233,18 @@ class NeonImmediate {
// This is allowed to be an implicit constructor because NeonImmediate is
// a wrapper class that doesn't normally perform any type conversion.
NeonImmediate(uint32_t immediate) // NOLINT(runtime/explicit)
: imm_(immediate),
immediate_type_(I32) {}
: imm_(immediate), immediate_type_(I32) {}
NeonImmediate(int immediate) // NOLINT(runtime/explicit)
: imm_(immediate),
immediate_type_(I32) {}
: imm_(immediate), immediate_type_(I32) {}
// { #<immediate> }
// where <immediate> is a 64 bit number
// This is allowed to be an implicit constructor because NeonImmediate is
// a wrapper class that doesn't normally perform any type conversion.
NeonImmediate(int64_t immediate) // NOLINT(runtime/explicit)
: imm_(immediate),
immediate_type_(I64) {}
: imm_(immediate), immediate_type_(I64) {}
NeonImmediate(uint64_t immediate) // NOLINT(runtime/explicit)
: imm_(immediate),
immediate_type_(I64) {}
: imm_(immediate), immediate_type_(I64) {}
// { #<immediate> }
// where <immediate> is a non zero floating point number which can be encoded
@@ -268,11 +252,9 @@ class NeonImmediate {
// This is allowed to be an implicit constructor because NeonImmediate is
// a wrapper class that doesn't normally perform any type conversion.
NeonImmediate(float immediate) // NOLINT(runtime/explicit)
: imm_(immediate),
immediate_type_(F32) {}
: imm_(immediate), immediate_type_(F32) {}
NeonImmediate(double immediate) // NOLINT(runtime/explicit)
: imm_(immediate),
immediate_type_(F64) {}
: imm_(immediate), immediate_type_(F64) {}
NeonImmediate(const NeonImmediate& src)
: imm_(src.imm_), immediate_type_(src.immediate_type_) {}
@@ -311,7 +293,7 @@ class NeonImmediate {
bool IsInteger32() const { return immediate_type_.Is(I32); }
bool IsInteger64() const { return immediate_type_.Is(I64); }
bool IsInteger() const { return IsInteger32() | IsInteger64(); }
bool IsInteger() const { return IsInteger32() || IsInteger64(); }
bool IsFloat() const { return immediate_type_.Is(F32); }
bool IsDouble() const { return immediate_type_.Is(F64); }
bool IsFloatZero() const {
@@ -374,29 +356,21 @@ std::ostream& operator<<(std::ostream& os, const NeonImmediate& operand);
class NeonOperand {
public:
NeonOperand(int32_t immediate) // NOLINT(runtime/explicit)
: imm_(immediate),
rm_(NoDReg) {}
: imm_(immediate), rm_(NoDReg) {}
NeonOperand(uint32_t immediate) // NOLINT(runtime/explicit)
: imm_(immediate),
rm_(NoDReg) {}
: imm_(immediate), rm_(NoDReg) {}
NeonOperand(int64_t immediate) // NOLINT(runtime/explicit)
: imm_(immediate),
rm_(NoDReg) {}
: imm_(immediate), rm_(NoDReg) {}
NeonOperand(uint64_t immediate) // NOLINT(runtime/explicit)
: imm_(immediate),
rm_(NoDReg) {}
: imm_(immediate), rm_(NoDReg) {}
NeonOperand(float immediate) // NOLINT(runtime/explicit)
: imm_(immediate),
rm_(NoDReg) {}
: imm_(immediate), rm_(NoDReg) {}
NeonOperand(double immediate) // NOLINT(runtime/explicit)
: imm_(immediate),
rm_(NoDReg) {}
: imm_(immediate), rm_(NoDReg) {}
NeonOperand(const NeonImmediate& imm) // NOLINT(runtime/explicit)
: imm_(imm),
rm_(NoDReg) {}
: imm_(imm), rm_(NoDReg) {}
NeonOperand(const VRegister& rm) // NOLINT(runtime/explicit)
: imm_(0),
rm_(rm) {
: imm_(0), rm_(rm) {
VIXL_ASSERT(rm_.IsValid());
}
@@ -641,7 +615,7 @@ class ImmediateVorn : public ImmediateVorr {
// - a shifted index register <Rm>, <shift> #<amount>
//
// The index register may have an associated {+/-} sign,
// which if ommitted, defaults to + .
// which if omitted, defaults to + .
//
// We have two constructors for the offset:
//
+3 -3
View File
@@ -105,7 +105,7 @@ class ABI {
// Stage C.1
if (is_floating_point_type && (NSRN_ < 8)) {
return GenericOperand(FPRegister(NSRN_++, size * kBitsPerByte));
return GenericOperand(VRegister(NSRN_++, size * kBitsPerByte));
}
// Stages C.2, C.3, and C.4: Unsupported. Caught by the assertions above.
// Stages C.5 and C.6
@@ -159,8 +159,8 @@ template <>
inline GenericOperand ABI::GetReturnGenericOperand<void>() const {
return GenericOperand();
}
}
} // namespace vixl::aarch64
} // namespace aarch64
} // namespace vixl
#endif // VIXL_AARCH64_ABI_AARCH64_H_
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+246
View File
@@ -27,13 +27,219 @@
#ifndef VIXL_CPU_AARCH64_H
#define VIXL_CPU_AARCH64_H
#include "../cpu-features.h"
#include "../globals-vixl.h"
#include "instructions-aarch64.h"
#include "simulator-aarch64.h"
#ifndef VIXL_INCLUDE_TARGET_AARCH64
// The supporting .cc file is only compiled when the A64 target is selected.
// Throw an explicit error now to avoid a harder-to-debug linker error later.
//
// These helpers _could_ work on any AArch64 host, even when generating AArch32
// code, but we don't support this because the available features may differ
// between AArch32 and AArch64 on the same platform, so basing AArch32 code
// generation on aarch64::CPU features is probably broken.
#error cpu-aarch64.h requires VIXL_INCLUDE_TARGET_AARCH64 (scons target=a64).
#endif
namespace vixl {
namespace aarch64 {
// A CPU ID register, for use with CPUFeatures::kIDRegisterEmulation. Fields
// specific to each register are described in relevant subclasses.
class IDRegister {
protected:
explicit IDRegister(uint64_t value = 0) : value_(value) {}
class Field {
public:
enum Type { kUnsigned, kSigned };
static const int kMaxWidthInBits = 4;
// This needs to be constexpr so that fields have "constant initialisation".
// This avoids initialisation order problems when these values are used to
// (dynamically) initialise static variables, etc.
explicit constexpr Field(int lsb,
int bitWidth = kMaxWidthInBits,
Type type = kUnsigned)
: lsb_(lsb), bitWidth_(bitWidth), type_(type) {}
int GetWidthInBits() const { return bitWidth_; }
int GetLsb() const { return lsb_; }
int GetMsb() const { return lsb_ + GetWidthInBits() - 1; }
Type GetType() const { return type_; }
private:
int lsb_;
int bitWidth_;
Type type_;
};
public:
// Extract the specified field, performing sign-extension for signed fields.
// This allows us to implement the 'value >= number' detection mechanism
// recommended by the Arm ARM, for both signed and unsigned fields.
int Get(Field field) const;
private:
uint64_t value_;
};
class AA64PFR0 : public IDRegister {
public:
explicit AA64PFR0(uint64_t value) : IDRegister(value) {}
CPUFeatures GetCPUFeatures() const;
private:
static const Field kFP;
static const Field kAdvSIMD;
static const Field kRAS;
static const Field kSVE;
static const Field kDIT;
static const Field kCSV2;
static const Field kCSV3;
};
class AA64PFR1 : public IDRegister {
public:
explicit AA64PFR1(uint64_t value) : IDRegister(value) {}
CPUFeatures GetCPUFeatures() const;
private:
static const Field kBT;
static const Field kSSBS;
static const Field kMTE;
static const Field kSME;
};
class AA64ISAR0 : public IDRegister {
public:
explicit AA64ISAR0(uint64_t value) : IDRegister(value) {}
CPUFeatures GetCPUFeatures() const;
private:
static const Field kAES;
static const Field kSHA1;
static const Field kSHA2;
static const Field kCRC32;
static const Field kAtomic;
static const Field kRDM;
static const Field kSHA3;
static const Field kSM3;
static const Field kSM4;
static const Field kDP;
static const Field kFHM;
static const Field kTS;
static const Field kRNDR;
};
class AA64ISAR1 : public IDRegister {
public:
explicit AA64ISAR1(uint64_t value) : IDRegister(value) {}
CPUFeatures GetCPUFeatures() const;
private:
static const Field kDPB;
static const Field kAPA;
static const Field kAPI;
static const Field kJSCVT;
static const Field kFCMA;
static const Field kLRCPC;
static const Field kGPA;
static const Field kGPI;
static const Field kFRINTTS;
static const Field kSB;
static const Field kSPECRES;
static const Field kBF16;
static const Field kDGH;
static const Field kI8MM;
};
class AA64ISAR2 : public IDRegister {
public:
explicit AA64ISAR2(uint64_t value) : IDRegister(value) {}
CPUFeatures GetCPUFeatures() const;
private:
static const Field kWFXT;
static const Field kRPRES;
static const Field kMOPS;
static const Field kCSSC;
};
class AA64MMFR0 : public IDRegister {
public:
explicit AA64MMFR0(uint64_t value) : IDRegister(value) {}
CPUFeatures GetCPUFeatures() const;
private:
static const Field kECV;
};
class AA64MMFR1 : public IDRegister {
public:
explicit AA64MMFR1(uint64_t value) : IDRegister(value) {}
CPUFeatures GetCPUFeatures() const;
private:
static const Field kLO;
static const Field kAFP;
};
class AA64MMFR2 : public IDRegister {
public:
explicit AA64MMFR2(uint64_t value) : IDRegister(value) {}
CPUFeatures GetCPUFeatures() const;
private:
static const Field kAT;
};
class AA64ZFR0 : public IDRegister {
public:
explicit AA64ZFR0(uint64_t value) : IDRegister(value) {}
CPUFeatures GetCPUFeatures() const;
private:
static const Field kSVEver;
static const Field kAES;
static const Field kBitPerm;
static const Field kBF16;
static const Field kSHA3;
static const Field kSM4;
static const Field kI8MM;
static const Field kF32MM;
static const Field kF64MM;
};
class AA64SMFR0 : public IDRegister {
public:
explicit AA64SMFR0(uint64_t value) : IDRegister(value) {}
CPUFeatures GetCPUFeatures() const;
private:
static const Field kSMEf32f32;
static const Field kSMEb16f32;
static const Field kSMEf16f32;
static const Field kSMEi8i32;
static const Field kSMEf64f64;
static const Field kSMEi16i64;
static const Field kSMEfa64;
};
class CPU {
public:
// Initialise CPU support.
@@ -45,6 +251,25 @@ class CPU {
// safely run.
static void EnsureIAndDCacheCoherency(void *address, size_t length);
// Read and interpret the ID registers. This requires
// CPUFeatures::kIDRegisterEmulation, and therefore cannot be called on
// non-AArch64 platforms.
static CPUFeatures InferCPUFeaturesFromIDRegisters();
// Read and interpret CPUFeatures reported by the OS. Failed queries (or
// unsupported platforms) return an empty list. Note that this is
// indistinguishable from a successful query on a platform that advertises no
// features.
//
// Non-AArch64 hosts are considered to be unsupported platforms, and this
// function returns an empty list.
static CPUFeatures InferCPUFeaturesFromOS(
CPUFeatures::QueryIDRegistersOption option =
CPUFeatures::kQueryIDRegistersIfAvailable);
// Query the SVE vector length. This requires CPUFeatures::kSVE.
static int ReadSVEVectorLengthInBits();
// Handle tagged pointers.
template <typename T>
static T SetPointerTag(T pointer, uint64_t tag) {
@@ -72,6 +297,27 @@ class CPU {
}
private:
#define VIXL_AARCH64_ID_REG_LIST(V) \
V(AA64PFR0, "ID_AA64PFR0_EL1") \
V(AA64PFR1, "ID_AA64PFR1_EL1") \
V(AA64ISAR0, "ID_AA64ISAR0_EL1") \
V(AA64ISAR1, "ID_AA64ISAR1_EL1") \
V(AA64MMFR0, "ID_AA64MMFR0_EL1") \
V(AA64MMFR1, "ID_AA64MMFR1_EL1") \
/* These registers are RES0 in the baseline Arm8.0. We can always safely */ \
/* read them, but some compilers don't accept the symbolic names. */ \
V(AA64SMFR0, "S3_0_C0_C4_5") \
V(AA64ISAR2, "S3_0_C0_C6_2") \
V(AA64MMFR2, "S3_0_C0_C7_2") \
V(AA64ZFR0, "S3_0_C0_C4_4")
#define VIXL_READ_ID_REG(NAME, MRS_ARG) static NAME Read##NAME();
// On native AArch64 platforms, read the named CPU ID registers. These require
// CPUFeatures::kIDRegisterEmulation, and should not be called on non-AArch64
// platforms.
VIXL_AARCH64_ID_REG_LIST(VIXL_READ_ID_REG)
#undef VIXL_READ_ID_REG
// Return the content of the cache type register.
static uint32_t GetCacheType();
@@ -27,10 +27,14 @@
#ifndef VIXL_AARCH64_CPU_FEATURES_AUDITOR_AARCH64_H_
#define VIXL_AARCH64_CPU_FEATURES_AUDITOR_AARCH64_H_
#include <functional>
#include <iostream>
#include <unordered_map>
#include "../cpu-features.h"
#include "decoder-aarch64.h"
#include "decoder-visitor-map-aarch64.h"
namespace vixl {
namespace aarch64 {
@@ -100,15 +104,16 @@ class CPUFeaturesAuditor : public DecoderVisitor {
SetAvailableFeatures(available);
}
// Declare all Visitor functions.
#define DECLARE(A) \
virtual void Visit##A(const Instruction* instr) VIXL_OVERRIDE;
VISITOR_LIST(DECLARE)
#undef DECLARE
virtual void Visit(Metadata* metadata,
const Instruction* instr) VIXL_OVERRIDE;
private:
class RecordInstructionFeaturesScope;
#define DECLARE(A) virtual void Visit##A(const Instruction* instr);
VISITOR_LIST(DECLARE)
#undef DECLARE
void LoadStoreHelper(const Instruction* instr);
void LoadStorePairHelper(const Instruction* instr);
@@ -117,6 +122,12 @@ class CPUFeaturesAuditor : public DecoderVisitor {
CPUFeatures available_;
Decoder* decoder_;
using FormToVisitorFnMap = std::unordered_map<
uint32_t,
std::function<void(CPUFeaturesAuditor*, const Instruction*)>>;
static const FormToVisitorFnMap* GetFormToVisitorFnMap();
uint32_t form_hash_;
};
} // namespace aarch64
@@ -0,0 +1,276 @@
// Copyright 2023, 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_DEBUGGER_AARCH64_H_
#define VIXL_AARCH64_DEBUGGER_AARCH64_H_
#include <optional>
#include <unordered_set>
#include <vector>
#include "../globals-vixl.h"
#include "../utils-vixl.h"
#include "../cpu-features.h"
#include "abi-aarch64.h"
#include "cpu-features-auditor-aarch64.h"
#include "disasm-aarch64.h"
#include "instructions-aarch64.h"
#include "simulator-aarch64.h"
#include "simulator-constants-aarch64.h"
#ifdef VIXL_INCLUDE_SIMULATOR_AARCH64
namespace vixl {
namespace aarch64 {
class Simulator;
enum DebugReturn { DebugContinue, DebugExit };
// A debugger command that performs some action when used by the simulator
// debugger.
class DebuggerCmd {
public:
DebuggerCmd(Simulator* sim,
std::string cmd_word,
std::string cmd_alias,
std::string usage,
std::string description);
virtual ~DebuggerCmd() {}
// Perform some action based on the arguments passed in. Returns true if the
// debugger should exit after the action, false otherwise.
virtual DebugReturn Action(const std::vector<std::string>& args) = 0;
// Return the command word.
std::string_view GetCommandWord() { return command_word_; }
// Return the alias for this command. Returns an empty string if this command
// has no alias.
std::string_view GetCommandAlias() { return command_alias_; }
// Return this commands usage.
std::string_view GetArgsString() { return args_str_; }
// Return this commands description.
std::string_view GetDescription() { return description_; }
protected:
// Simulator which this command will be performed on.
Simulator* sim_;
// Stream to output the result of the command to.
FILE* ostream_;
// Command word that, when given to the interactive debugger, calls Action.
std::string command_word_;
// Optional alias for the command_word.
std::string command_alias_;
// Optional string showing the arguments that can be passed to the command.
std::string args_str_;
// Optional description of the command.
std::string description_;
};
//
// Base debugger command handlers:
//
class HelpCmd : public DebuggerCmd {
public:
HelpCmd(Simulator* sim)
: DebuggerCmd(sim, "help", "h", "", "Display this help message.") {}
DebugReturn Action(const std::vector<std::string>& args) override;
};
class BreakCmd : public DebuggerCmd {
public:
BreakCmd(Simulator* sim)
: DebuggerCmd(sim,
"break",
"b",
"<address>",
"Set or remove a breakpoint.") {}
DebugReturn Action(const std::vector<std::string>& args) override;
};
class StepCmd : public DebuggerCmd {
public:
StepCmd(Simulator* sim)
: DebuggerCmd(sim,
"step",
"s",
"[<n>]",
"Step n instructions, default step 1 instruction.") {}
DebugReturn Action(const std::vector<std::string>& args) override;
};
class ContinueCmd : public DebuggerCmd {
public:
ContinueCmd(Simulator* sim)
: DebuggerCmd(sim,
"continue",
"c",
"",
"Exit the debugger and continue executing instructions.") {}
DebugReturn Action(const std::vector<std::string>& args) override;
};
class PrintCmd : public DebuggerCmd {
public:
PrintCmd(Simulator* sim)
: DebuggerCmd(sim,
"print",
"p",
"<register|all|system>",
"Print the contents of a register, all registers or all"
" system registers.") {}
DebugReturn Action(const std::vector<std::string>& args) override;
};
class TraceCmd : public DebuggerCmd {
public:
TraceCmd(Simulator* sim)
: DebuggerCmd(sim,
"trace",
"t",
"",
"Start/stop memory and register tracing.") {}
DebugReturn Action(const std::vector<std::string>& args) override;
};
class GdbCmd : public DebuggerCmd {
public:
GdbCmd(Simulator* sim)
: DebuggerCmd(sim,
"gdb",
"g",
"",
"Enter an already running instance of gdb.") {}
DebugReturn Action(const std::vector<std::string>& args) override;
};
// A debugger for the Simulator which takes input from the user in order to
// control the running of the Simulator.
class Debugger {
public:
// A pair consisting of a register character (e.g: W, X, V) and a register
// code (e.g: 0, 1 ...31) which represents a single parsed register.
//
// Note: the register character is guaranteed to be upper case.
using RegisterParsedFormat = std::pair<char, unsigned>;
Debugger(Simulator* sim);
// Set the input stream, from which commands are read, to a custom stream.
void SetInputStream(std::istream* stream) { input_stream_ = stream; }
// Register a new command for the debugger.
template <class T>
void RegisterCmd();
// Set a breakpoint at the given address.
void RegisterBreakpoint(uint64_t addr) { breakpoints_.insert(addr); }
// Remove a breakpoint at the given address.
void RemoveBreakpoint(uint64_t addr) { breakpoints_.erase(addr); }
// Return true if the address is the location of a breakpoint.
bool IsBreakpoint(uint64_t addr) const {
return (breakpoints_.find(addr) != breakpoints_.end());
}
// Return true if the simulator pc is a breakpoint.
bool IsAtBreakpoint() const;
// Main loop for the debugger. Keep prompting for user inputted debugger
// commands and try to execute them until a command is given that exits the
// interactive debugger.
void Debug();
// Get an unsigned integer value from a string and return it in 'value'.
// Base is used to determine the numeric base of the number to be read,
// i.e: 8 for octal, 10 for decimal, 16 for hexadecimal and 0 for
// auto-detect. Return true if an integer value was found, false otherwise.
static std::optional<uint64_t> ParseUint64String(std::string_view uint64_str,
int base = 0);
// Get a register from a string and return it in 'reg'. Return true if a
// valid register character and code (e.g: W0, X29, V31) was found, false
// otherwise.
static std::optional<RegisterParsedFormat> ParseRegString(
std::string_view reg_str);
// Print the usage of each debugger command.
void PrintUsage();
private:
// Split a string based on the separator given (a single space character by
// default) and return as a std::vector of strings.
static std::vector<std::string> Tokenize(std::string_view input_line,
char separator = ' ');
// Try to execute a single debugger command.
DebugReturn ExecDebugCommand(const std::vector<std::string>& tokenized_cmd);
// Return true if the string is zero, i.e: all characters in the string
// (other than prefixes) are zero.
static bool IsZeroUint64String(std::string_view uint64_str, int base);
// The simulator that this debugger acts on.
Simulator* sim_;
// A vector of all commands recognised by the debugger.
std::vector<std::unique_ptr<DebuggerCmd>> debugger_cmds_;
// Input stream from which commands are read. Default is std::cin.
std::istream* input_stream_;
// Output stream from the simulator.
FILE* ostream_;
// A list of all instruction addresses that, when executed by the
// simulator, will start the interactive debugger if it hasn't already.
std::unordered_set<uint64_t> breakpoints_;
};
} // namespace aarch64
} // namespace vixl
#endif // VIXL_INCLUDE_SIMULATOR_AARCH64
#endif // VIXL_AARCH64_DEBUGGER_AARCH64_H_
+562 -157
View File
@@ -1,4 +1,4 @@
// Copyright 2014, VIXL authors
// Copyright 2019, VIXL authors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@@ -28,95 +28,245 @@
#define VIXL_AARCH64_DECODER_AARCH64_H_
#include <list>
#include <map>
#include <string>
#include "../globals-vixl.h"
#include "instructions-aarch64.h"
// List macro containing all visitors needed by the decoder class.
#define VISITOR_LIST_THAT_RETURN(V) \
V(AddSubExtended) \
V(AddSubImmediate) \
V(AddSubShifted) \
V(AddSubWithCarry) \
V(AtomicMemory) \
V(Bitfield) \
V(CompareBranch) \
V(ConditionalBranch) \
V(ConditionalCompareImmediate) \
V(ConditionalCompareRegister) \
V(ConditionalSelect) \
V(Crypto2RegSHA) \
V(Crypto3RegSHA) \
V(CryptoAES) \
V(DataProcessing1Source) \
V(DataProcessing2Source) \
V(DataProcessing3Source) \
V(Exception) \
V(Extract) \
V(FPCompare) \
V(FPConditionalCompare) \
V(FPConditionalSelect) \
V(FPDataProcessing1Source) \
V(FPDataProcessing2Source) \
V(FPDataProcessing3Source) \
V(FPFixedPointConvert) \
V(FPImmediate) \
V(FPIntegerConvert) \
V(LoadLiteral) \
V(LoadStoreExclusive) \
V(LoadStorePairNonTemporal) \
V(LoadStorePairOffset) \
V(LoadStorePairPostIndex) \
V(LoadStorePairPreIndex) \
V(LoadStorePostIndex) \
V(LoadStorePreIndex) \
V(LoadStoreRegisterOffset) \
V(LoadStoreUnscaledOffset) \
V(LoadStoreUnsignedOffset) \
V(LogicalImmediate) \
V(LogicalShifted) \
V(MoveWideImmediate) \
V(NEON2RegMisc) \
V(NEON2RegMiscFP16) \
V(NEON3Different) \
V(NEON3Same) \
V(NEON3SameExtra) \
V(NEON3SameFP16) \
V(NEONAcrossLanes) \
V(NEONByIndexedElement) \
V(NEONCopy) \
V(NEONExtract) \
V(NEONLoadStoreMultiStruct) \
V(NEONLoadStoreMultiStructPostIndex) \
V(NEONLoadStoreSingleStruct) \
V(NEONLoadStoreSingleStructPostIndex) \
V(NEONModifiedImmediate) \
V(NEONPerm) \
V(NEONScalar2RegMisc) \
V(NEONScalar2RegMiscFP16) \
V(NEONScalar3Diff) \
V(NEONScalar3Same) \
V(NEONScalar3SameExtra) \
V(NEONScalar3SameFP16) \
V(NEONScalarByIndexedElement) \
V(NEONScalarCopy) \
V(NEONScalarPairwise) \
V(NEONScalarShiftImmediate) \
V(NEONShiftImmediate) \
V(NEONTable) \
V(PCRelAddressing) \
V(System) \
V(TestBranch) \
V(UnconditionalBranch) \
V(UnconditionalBranchToRegister)
#define VISITOR_LIST_THAT_DONT_RETURN(V) \
V(Unallocated) \
#define VISITOR_LIST_THAT_RETURN(V) \
V(AddSubExtended) \
V(AddSubImmediate) \
V(AddSubShifted) \
V(AddSubWithCarry) \
V(AtomicMemory) \
V(Bitfield) \
V(CompareBranch) \
V(ConditionalBranch) \
V(ConditionalCompareImmediate) \
V(ConditionalCompareRegister) \
V(ConditionalSelect) \
V(Crypto2RegSHA) \
V(Crypto3RegSHA) \
V(CryptoAES) \
V(DataProcessing1Source) \
V(DataProcessing2Source) \
V(DataProcessing3Source) \
V(EvaluateIntoFlags) \
V(Exception) \
V(Extract) \
V(FPCompare) \
V(FPConditionalCompare) \
V(FPConditionalSelect) \
V(FPDataProcessing1Source) \
V(FPDataProcessing2Source) \
V(FPDataProcessing3Source) \
V(FPFixedPointConvert) \
V(FPImmediate) \
V(FPIntegerConvert) \
V(LoadLiteral) \
V(LoadStoreExclusive) \
V(LoadStorePAC) \
V(LoadStorePairNonTemporal) \
V(LoadStorePairOffset) \
V(LoadStorePairPostIndex) \
V(LoadStorePairPreIndex) \
V(LoadStorePostIndex) \
V(LoadStorePreIndex) \
V(LoadStoreRCpcUnscaledOffset) \
V(LoadStoreRegisterOffset) \
V(LoadStoreUnscaledOffset) \
V(LoadStoreUnsignedOffset) \
V(LogicalImmediate) \
V(LogicalShifted) \
V(MoveWideImmediate) \
V(NEON2RegMisc) \
V(NEON2RegMiscFP16) \
V(NEON3Different) \
V(NEON3Same) \
V(NEON3SameExtra) \
V(NEON3SameFP16) \
V(NEONAcrossLanes) \
V(NEONByIndexedElement) \
V(NEONCopy) \
V(NEONExtract) \
V(NEONLoadStoreMultiStruct) \
V(NEONLoadStoreMultiStructPostIndex) \
V(NEONLoadStoreSingleStruct) \
V(NEONLoadStoreSingleStructPostIndex) \
V(NEONModifiedImmediate) \
V(NEONPerm) \
V(NEONScalar2RegMisc) \
V(NEONScalar2RegMiscFP16) \
V(NEONScalar3Diff) \
V(NEONScalar3Same) \
V(NEONScalar3SameExtra) \
V(NEONScalar3SameFP16) \
V(NEONScalarByIndexedElement) \
V(NEONScalarCopy) \
V(NEONScalarPairwise) \
V(NEONScalarShiftImmediate) \
V(NEONShiftImmediate) \
V(NEONTable) \
V(PCRelAddressing) \
V(RotateRightIntoFlags) \
V(SVE32BitGatherLoad_ScalarPlus32BitUnscaledOffsets) \
V(SVE32BitGatherLoad_VectorPlusImm) \
V(SVE32BitGatherLoadHalfwords_ScalarPlus32BitScaledOffsets) \
V(SVE32BitGatherLoadWords_ScalarPlus32BitScaledOffsets) \
V(SVE32BitGatherPrefetch_ScalarPlus32BitScaledOffsets) \
V(SVE32BitGatherPrefetch_VectorPlusImm) \
V(SVE32BitScatterStore_ScalarPlus32BitScaledOffsets) \
V(SVE32BitScatterStore_ScalarPlus32BitUnscaledOffsets) \
V(SVE32BitScatterStore_VectorPlusImm) \
V(SVE64BitGatherLoad_ScalarPlus32BitUnpackedScaledOffsets) \
V(SVE64BitGatherLoad_ScalarPlus64BitScaledOffsets) \
V(SVE64BitGatherLoad_ScalarPlus64BitUnscaledOffsets) \
V(SVE64BitGatherLoad_ScalarPlusUnpacked32BitUnscaledOffsets) \
V(SVE64BitGatherLoad_VectorPlusImm) \
V(SVE64BitGatherPrefetch_ScalarPlus64BitScaledOffsets) \
V(SVE64BitGatherPrefetch_ScalarPlusUnpacked32BitScaledOffsets) \
V(SVE64BitGatherPrefetch_VectorPlusImm) \
V(SVE64BitScatterStore_ScalarPlus64BitScaledOffsets) \
V(SVE64BitScatterStore_ScalarPlus64BitUnscaledOffsets) \
V(SVE64BitScatterStore_ScalarPlusUnpacked32BitScaledOffsets) \
V(SVE64BitScatterStore_ScalarPlusUnpacked32BitUnscaledOffsets) \
V(SVE64BitScatterStore_VectorPlusImm) \
V(SVEAddressGeneration) \
V(SVEBitwiseLogicalUnpredicated) \
V(SVEBitwiseShiftUnpredicated) \
V(SVEFFRInitialise) \
V(SVEFFRWriteFromPredicate) \
V(SVEFPAccumulatingReduction) \
V(SVEFPArithmeticUnpredicated) \
V(SVEFPCompareVectors) \
V(SVEFPCompareWithZero) \
V(SVEFPComplexAddition) \
V(SVEFPComplexMulAdd) \
V(SVEFPComplexMulAddIndex) \
V(SVEFPFastReduction) \
V(SVEFPMulIndex) \
V(SVEFPMulAdd) \
V(SVEFPMulAddIndex) \
V(SVEFPUnaryOpUnpredicated) \
V(SVEIncDecByPredicateCount) \
V(SVEIndexGeneration) \
V(SVEIntArithmeticUnpredicated) \
V(SVEIntCompareSignedImm) \
V(SVEIntCompareUnsignedImm) \
V(SVEIntCompareVectors) \
V(SVEIntMulAddPredicated) \
V(SVEIntMulAddUnpredicated) \
V(SVEIntReduction) \
V(SVEIntUnaryArithmeticPredicated) \
V(SVEMovprfx) \
V(SVEMulIndex) \
V(SVEPermuteVectorExtract) \
V(SVEPermuteVectorInterleaving) \
V(SVEPredicateCount) \
V(SVEPredicateLogical) \
V(SVEPropagateBreak) \
V(SVEStackFrameAdjustment) \
V(SVEStackFrameSize) \
V(SVEVectorSelect) \
V(SVEBitwiseLogical_Predicated) \
V(SVEBitwiseLogicalWithImm_Unpredicated) \
V(SVEBitwiseShiftByImm_Predicated) \
V(SVEBitwiseShiftByVector_Predicated) \
V(SVEBitwiseShiftByWideElements_Predicated) \
V(SVEBroadcastBitmaskImm) \
V(SVEBroadcastFPImm_Unpredicated) \
V(SVEBroadcastGeneralRegister) \
V(SVEBroadcastIndexElement) \
V(SVEBroadcastIntImm_Unpredicated) \
V(SVECompressActiveElements) \
V(SVEConditionallyBroadcastElementToVector) \
V(SVEConditionallyExtractElementToSIMDFPScalar) \
V(SVEConditionallyExtractElementToGeneralRegister) \
V(SVEConditionallyTerminateScalars) \
V(SVEConstructivePrefix_Unpredicated) \
V(SVEContiguousFirstFaultLoad_ScalarPlusScalar) \
V(SVEContiguousLoad_ScalarPlusImm) \
V(SVEContiguousLoad_ScalarPlusScalar) \
V(SVEContiguousNonFaultLoad_ScalarPlusImm) \
V(SVEContiguousNonTemporalLoad_ScalarPlusImm) \
V(SVEContiguousNonTemporalLoad_ScalarPlusScalar) \
V(SVEContiguousNonTemporalStore_ScalarPlusImm) \
V(SVEContiguousNonTemporalStore_ScalarPlusScalar) \
V(SVEContiguousPrefetch_ScalarPlusImm) \
V(SVEContiguousPrefetch_ScalarPlusScalar) \
V(SVEContiguousStore_ScalarPlusImm) \
V(SVEContiguousStore_ScalarPlusScalar) \
V(SVECopySIMDFPScalarRegisterToVector_Predicated) \
V(SVECopyFPImm_Predicated) \
V(SVECopyGeneralRegisterToVector_Predicated) \
V(SVECopyIntImm_Predicated) \
V(SVEElementCount) \
V(SVEExtractElementToSIMDFPScalarRegister) \
V(SVEExtractElementToGeneralRegister) \
V(SVEFPArithmetic_Predicated) \
V(SVEFPArithmeticWithImm_Predicated) \
V(SVEFPConvertPrecision) \
V(SVEFPConvertToInt) \
V(SVEFPExponentialAccelerator) \
V(SVEFPRoundToIntegralValue) \
V(SVEFPTrigMulAddCoefficient) \
V(SVEFPTrigSelectCoefficient) \
V(SVEFPUnaryOp) \
V(SVEIncDecRegisterByElementCount) \
V(SVEIncDecVectorByElementCount) \
V(SVEInsertSIMDFPScalarRegister) \
V(SVEInsertGeneralRegister) \
V(SVEIntAddSubtractImm_Unpredicated) \
V(SVEIntAddSubtractVectors_Predicated) \
V(SVEIntCompareScalarCountAndLimit) \
V(SVEIntConvertToFP) \
V(SVEIntDivideVectors_Predicated) \
V(SVEIntMinMaxImm_Unpredicated) \
V(SVEIntMinMaxDifference_Predicated) \
V(SVEIntMulImm_Unpredicated) \
V(SVEIntMulVectors_Predicated) \
V(SVELoadAndBroadcastElement) \
V(SVELoadAndBroadcastQOWord_ScalarPlusImm) \
V(SVELoadAndBroadcastQOWord_ScalarPlusScalar) \
V(SVELoadMultipleStructures_ScalarPlusImm) \
V(SVELoadMultipleStructures_ScalarPlusScalar) \
V(SVELoadPredicateRegister) \
V(SVELoadVectorRegister) \
V(SVEPartitionBreakCondition) \
V(SVEPermutePredicateElements) \
V(SVEPredicateFirstActive) \
V(SVEPredicateInitialize) \
V(SVEPredicateNextActive) \
V(SVEPredicateReadFromFFR_Predicated) \
V(SVEPredicateReadFromFFR_Unpredicated) \
V(SVEPredicateTest) \
V(SVEPredicateZero) \
V(SVEPropagateBreakToNextPartition) \
V(SVEReversePredicateElements) \
V(SVEReverseVectorElements) \
V(SVEReverseWithinElements) \
V(SVESaturatingIncDecRegisterByElementCount) \
V(SVESaturatingIncDecVectorByElementCount) \
V(SVEStoreMultipleStructures_ScalarPlusImm) \
V(SVEStoreMultipleStructures_ScalarPlusScalar) \
V(SVEStorePredicateRegister) \
V(SVEStoreVectorRegister) \
V(SVETableLookup) \
V(SVEUnpackPredicateElements) \
V(SVEUnpackVectorElements) \
V(SVEVectorSplice) \
V(System) \
V(TestBranch) \
V(Unallocated) \
V(UnconditionalBranch) \
V(UnconditionalBranchToRegister) \
V(Unimplemented)
#define VISITOR_LIST_THAT_DONT_RETURN(V) V(Reserved)
#define VISITOR_LIST(V) \
VISITOR_LIST_THAT_RETURN(V) \
VISITOR_LIST_THAT_DONT_RETURN(V)
@@ -124,8 +274,12 @@
namespace vixl {
namespace aarch64 {
// The Visitor interface. Disassembler and simulator (and other tools)
// must provide implementations for all of these functions.
using Metadata = std::map<std::string, std::string>;
// The Visitor interface consists only of the Visit() method. User classes
// that inherit from this one must provide an implementation of the method.
// Information about the instruction encountered by the Decoder is available
// via the metadata pointer.
class DecoderVisitor {
public:
enum VisitorConstness { kConstVisitor, kNonConstVisitor };
@@ -134,9 +288,7 @@ class DecoderVisitor {
virtual ~DecoderVisitor() {}
#define DECLARE(A) virtual void Visit##A(const Instruction* instr) = 0;
VISITOR_LIST(DECLARE)
#undef DECLARE
virtual void Visit(Metadata* metadata, const Instruction* instr) = 0;
bool IsConstVisitor() const { return constness_ == kConstVisitor; }
Instruction* MutableInstruction(const Instruction* instr) {
@@ -148,22 +300,22 @@ class DecoderVisitor {
const VisitorConstness constness_;
};
class DecodeNode;
class CompiledDecodeNode;
// The instruction decoder is constructed from a graph of decode nodes. At each
// node, a number of bits are sampled from the instruction being decoded. The
// resulting value is used to look up the next node in the graph, which then
// samples other bits, and moves to other decode nodes. Eventually, a visitor
// node is reached, and the corresponding visitor function is called, which
// handles the instruction.
class Decoder {
public:
Decoder() {}
Decoder() { ConstructDecodeGraph(); }
// Top-level wrappers around the actual decoding function.
void Decode(const Instruction* instr) {
std::list<DecoderVisitor*>::iterator it;
for (it = visitors_.begin(); it != visitors_.end(); it++) {
VIXL_ASSERT((*it)->IsConstVisitor());
}
DecodeInstruction(instr);
}
void Decode(Instruction* instr) {
DecodeInstruction(const_cast<const Instruction*>(instr));
}
void Decode(const Instruction* instr);
void Decode(Instruction* instr);
// Decode all instructions from start (inclusive) to end (exclusive).
template <typename T>
@@ -212,76 +364,329 @@ class Decoder {
// of visitors stored by the decoder.
void RemoveVisitor(DecoderVisitor* visitor);
#define DECLARE(A) void Visit##A(const Instruction* instr);
VISITOR_LIST(DECLARE)
#undef DECLARE
void VisitNamedInstruction(const Instruction* instr, const std::string& name);
std::list<DecoderVisitor*>* visitors() { return &visitors_; }
// Get a DecodeNode by name from the Decoder's map.
DecodeNode* GetDecodeNode(std::string name);
private:
// Decodes an instruction and calls the visitor functions registered with the
// Decoder class.
void DecodeInstruction(const Instruction* instr);
// Decode the PC relative addressing instruction, and call the corresponding
// visitors.
// On entry, instruction bits 27:24 = 0x0.
void DecodePCRelAddressing(const Instruction* instr);
// Add an initialised DecodeNode to the decode_node_ map.
void AddDecodeNode(const DecodeNode& node);
// Decode the add/subtract immediate instruction, and call the correspoding
// visitors.
// On entry, instruction bits 27:24 = 0x1.
void DecodeAddSubImmediate(const Instruction* instr);
// Decode the branch, system command, and exception generation parts of
// the instruction tree, and call the corresponding visitors.
// On entry, instruction bits 27:24 = {0x4, 0x5, 0x6, 0x7}.
void DecodeBranchSystemException(const Instruction* instr);
// Decode the load and store parts of the instruction tree, and call
// the corresponding visitors.
// On entry, instruction bits 27:24 = {0x8, 0x9, 0xC, 0xD}.
void DecodeLoadStore(const Instruction* instr);
// Decode the logical immediate and move wide immediate parts of the
// instruction tree, and call the corresponding visitors.
// On entry, instruction bits 27:24 = 0x2.
void DecodeLogical(const Instruction* instr);
// Decode the bitfield and extraction parts of the instruction tree,
// and call the corresponding visitors.
// On entry, instruction bits 27:24 = 0x3.
void DecodeBitfieldExtract(const Instruction* instr);
// Decode the data processing parts of the instruction tree, and call the
// corresponding visitors.
// On entry, instruction bits 27:24 = {0x1, 0xA, 0xB}.
void DecodeDataProcessing(const Instruction* instr);
// Decode the floating point parts of the instruction tree, and call the
// corresponding visitors.
// On entry, instruction bits 27:24 = {0xE, 0xF}.
void DecodeFP(const Instruction* instr);
// Decode the Advanced SIMD (NEON) load/store part of the instruction tree,
// and call the corresponding visitors.
// On entry, instruction bits 29:25 = 0x6.
void DecodeNEONLoadStore(const Instruction* instr);
// Decode the Advanced SIMD (NEON) vector data processing part of the
// instruction tree, and call the corresponding visitors.
// On entry, instruction bits 28:25 = 0x7.
void DecodeNEONVectorDataProcessing(const Instruction* instr);
// Decode the Advanced SIMD (NEON) scalar data processing part of the
// instruction tree, and call the corresponding visitors.
// On entry, instruction bits 28:25 = 0xF.
void DecodeNEONScalarDataProcessing(const Instruction* instr);
private:
// Visitors are registered in a list.
std::list<DecoderVisitor*> visitors_;
// Compile the dynamically generated decode graph based on the static
// information in kDecodeMapping and kVisitorNodes.
void ConstructDecodeGraph();
// Root node for the compiled decoder graph, stored here to avoid a map lookup
// for every instruction decoded.
CompiledDecodeNode* compiled_decoder_root_;
// Map of node names to DecodeNodes.
std::map<std::string, DecodeNode> decode_nodes_;
};
typedef void (Decoder::*DecodeFnPtr)(const Instruction*);
typedef uint32_t (Instruction::*BitExtractFn)(void) const;
// A Visitor node maps the name of a visitor to the function that handles it.
struct VisitorNode {
const char* name;
const DecodeFnPtr visitor_fn;
};
// DecodePattern and DecodeMapping represent the input data to the decoder
// compilation stage. After compilation, the decoder is embodied in the graph
// of CompiledDecodeNodes pointer to by compiled_decoder_root_.
// A DecodePattern maps a pattern of set/unset/don't care (1, 0, x) bits encoded
// as uint32_t to its handler.
// The encoding uses two bits per symbol: 0 => 0b00, 1 => 0b01, x => 0b10.
// 0b11 marks the edge of the most-significant bits of the pattern, which is
// required to determine the length. For example, the pattern "1x01"_b is
// encoded in a uint32_t as 0b11_01_10_00_01.
struct DecodePattern {
uint32_t pattern;
const char* handler;
};
// A DecodeMapping consists of the name of a handler, the bits sampled in the
// instruction by that handler, and a mapping from the pattern that those
// sampled bits match to the corresponding name of a node.
struct DecodeMapping {
const char* name;
const std::vector<uint8_t> sampled_bits;
const std::vector<DecodePattern> mapping;
};
// For speed, before nodes can be used for decoding instructions, they must
// be compiled. This converts the mapping "bit pattern strings to decoder name
// string" stored in DecodeNodes to an array look up for the pointer to the next
// node, stored in CompiledDecodeNodes. Compilation may also apply other
// optimisations for simple decode patterns.
class CompiledDecodeNode {
public:
// Constructor for decode node, containing a decode table and pointer to a
// function that extracts the bits to be sampled.
CompiledDecodeNode(BitExtractFn bit_extract_fn, size_t decode_table_size)
: bit_extract_fn_(bit_extract_fn),
instruction_name_("node"),
decode_table_size_(decode_table_size),
decoder_(NULL) {
decode_table_ = new CompiledDecodeNode*[decode_table_size_];
memset(decode_table_, 0, decode_table_size_ * sizeof(decode_table_[0]));
}
// Constructor for wrappers around visitor functions. These require no
// decoding, so no bit extraction function or decode table is assigned.
explicit CompiledDecodeNode(std::string iname, Decoder* decoder)
: bit_extract_fn_(NULL),
instruction_name_(iname),
decode_table_(NULL),
decode_table_size_(0),
decoder_(decoder) {}
~CompiledDecodeNode() VIXL_NEGATIVE_TESTING_ALLOW_EXCEPTION {
// Free the decode table, if this is a compiled, non-leaf node.
if (decode_table_ != NULL) {
VIXL_ASSERT(!IsLeafNode());
delete[] decode_table_;
}
}
// Decode the instruction by either sampling the bits using the bit extract
// function to find the next node, or, if we're at a leaf, calling the visitor
// function.
void Decode(const Instruction* instr) const;
// A leaf node is a wrapper for a visitor function.
bool IsLeafNode() const {
VIXL_ASSERT(((instruction_name_ == "node") && (bit_extract_fn_ != NULL)) ||
((instruction_name_ != "node") && (bit_extract_fn_ == NULL)));
return instruction_name_ != "node";
}
// Get a pointer to the next node required in the decode process, based on the
// bits sampled by the current node.
CompiledDecodeNode* GetNodeForBits(uint32_t bits) const {
VIXL_ASSERT(bits < decode_table_size_);
return decode_table_[bits];
}
// Set the next node in the decode process for the pattern of sampled bits in
// the current node.
void SetNodeForBits(uint32_t bits, CompiledDecodeNode* n) {
VIXL_ASSERT(bits < decode_table_size_);
VIXL_ASSERT(n != NULL);
decode_table_[bits] = n;
}
private:
// Pointer to an instantiated template function for extracting the bits
// sampled by this node. Set to NULL for leaf nodes.
const BitExtractFn bit_extract_fn_;
// Visitor function that handles the instruction identified. Set only for
// leaf nodes, where no extra decoding is required, otherwise NULL.
std::string instruction_name_;
// Mapping table from instruction bits to next decode stage.
CompiledDecodeNode** decode_table_;
const size_t decode_table_size_;
// Pointer to the decoder containing this node, used to call its visitor
// function for leaf nodes. Set to NULL for non-leaf nodes.
Decoder* decoder_;
};
class DecodeNode {
public:
// Default constructor needed for map initialisation.
DecodeNode()
: sampled_bits_(DecodeNode::kEmptySampledBits),
pattern_table_(DecodeNode::kEmptyPatternTable),
compiled_node_(NULL) {}
// Constructor for DecodeNode wrappers around visitor functions. These are
// marked as "compiled", as there is no decoding left to do.
explicit DecodeNode(const std::string& iname, Decoder* decoder)
: name_(iname),
sampled_bits_(DecodeNode::kEmptySampledBits),
instruction_name_(iname),
pattern_table_(DecodeNode::kEmptyPatternTable),
decoder_(decoder),
compiled_node_(NULL) {}
// Constructor for DecodeNodes that map bit patterns to other DecodeNodes.
explicit DecodeNode(const DecodeMapping& map, Decoder* decoder = NULL)
: name_(map.name),
sampled_bits_(map.sampled_bits),
instruction_name_("node"),
pattern_table_(map.mapping),
decoder_(decoder),
compiled_node_(NULL) {
// With the current two bits per symbol encoding scheme, the maximum pattern
// length is (32 - 2) / 2 = 15 bits.
VIXL_CHECK(GetPatternLength(map.mapping[0].pattern) <= 15);
for (const DecodePattern& p : map.mapping) {
VIXL_CHECK(GetPatternLength(p.pattern) == map.sampled_bits.size());
}
}
~DecodeNode() {
// Delete the compiled version of this node, if one was created.
if (compiled_node_ != NULL) {
delete compiled_node_;
}
}
// Get the bits sampled from the instruction by this node.
const std::vector<uint8_t>& GetSampledBits() const { return sampled_bits_; }
// Get the number of bits sampled from the instruction by this node.
size_t GetSampledBitsCount() const { return sampled_bits_.size(); }
// A leaf node is a DecodeNode that wraps the visitor function for the
// identified instruction class.
bool IsLeafNode() const { return instruction_name_ != "node"; }
std::string GetName() const { return name_; }
// Create a CompiledDecodeNode of specified table size that uses
// bit_extract_fn to sample bits from the instruction.
void CreateCompiledNode(BitExtractFn bit_extract_fn, size_t table_size) {
VIXL_ASSERT(bit_extract_fn != NULL);
VIXL_ASSERT(table_size > 0);
compiled_node_ = new CompiledDecodeNode(bit_extract_fn, table_size);
}
// Create a CompiledDecodeNode wrapping a visitor function. No decoding is
// required for this node; the visitor function is called instead.
void CreateVisitorNode() {
compiled_node_ = new CompiledDecodeNode(instruction_name_, decoder_);
}
// Find and compile the DecodeNode named "name", and set it as the node for
// the pattern "bits".
void CompileNodeForBits(Decoder* decoder, std::string name, uint32_t bits);
// Get a pointer to an instruction method that extracts the instruction bits
// specified by the mask argument, and returns those sampled bits as a
// contiguous sequence, suitable for indexing an array.
// For example, a mask of 0b1010 returns a function that, given an instruction
// 0bXYZW, will return 0bXZ.
BitExtractFn GetBitExtractFunction(uint32_t mask) {
return GetBitExtractFunctionHelper(mask, 0);
}
// Get a pointer to an Instruction method that applies a mask to the
// instruction bits, and tests if the result is equal to value. The returned
// function gives a 1 result if (inst & mask == value), 0 otherwise.
BitExtractFn GetBitExtractFunction(uint32_t mask, uint32_t value) {
return GetBitExtractFunctionHelper(value, mask);
}
// Compile this DecodeNode into a new CompiledDecodeNode and returns a pointer
// to it. This pointer is also stored inside the DecodeNode itself. Destroying
// a DecodeNode frees its associated CompiledDecodeNode.
CompiledDecodeNode* Compile(Decoder* decoder);
// Get a pointer to the CompiledDecodeNode associated with this DecodeNode.
// Returns NULL if the node has not been compiled yet.
CompiledDecodeNode* GetCompiledNode() const { return compiled_node_; }
bool IsCompiled() const { return GetCompiledNode() != NULL; }
enum class PatternSymbol { kSymbol0 = 0, kSymbol1 = 1, kSymbolX = 2 };
static const uint32_t kEndOfPattern = 3;
static const uint32_t kPatternSymbolMask = 3;
size_t GetPatternLength(uint32_t pattern) const {
uint32_t hsb = HighestSetBitPosition(pattern);
// The pattern length is signified by two set bits in a two bit-aligned
// position. Ensure that the pattern has a highest set bit, it's at an odd
// bit position, and that the bit to the right of the hsb is also set.
VIXL_ASSERT(((hsb % 2) == 1) && (pattern >> (hsb - 1)) == kEndOfPattern);
return hsb / 2;
}
bool PatternContainsSymbol(uint32_t pattern, PatternSymbol symbol) const {
while ((pattern & kPatternSymbolMask) != kEndOfPattern) {
if (static_cast<PatternSymbol>(pattern & kPatternSymbolMask) == symbol)
return true;
pattern >>= 2;
}
return false;
}
PatternSymbol GetSymbolAt(uint32_t pattern, size_t pos) const {
size_t len = GetPatternLength(pattern);
VIXL_ASSERT((pos < 15) && (pos < len));
uint32_t shift = static_cast<uint32_t>(2 * (len - pos - 1));
uint32_t sym = (pattern >> shift) & kPatternSymbolMask;
return static_cast<PatternSymbol>(sym);
}
private:
// Generate a mask and value pair from a pattern constructed from 0, 1 and x
// (don't care) 2-bit symbols.
// For example "10x1"_b should return mask = 0b1101, value = 0b1001.
typedef std::pair<Instr, Instr> MaskValuePair;
MaskValuePair GenerateMaskValuePair(uint32_t pattern) const;
// Generate a pattern ordered by the bit positions sampled by this node.
// The symbol corresponding to the lowest sample position is placed in the
// least-significant bits of the result pattern.
// For example, a pattern of "1x0"_b expected when sampling bits 31, 1 and 30
// returns the pattern "x01"_b; bit 1 should be 'x', bit 30 '0' and bit 31
// '1'.
// This output makes comparisons easier between the pattern and bits sampled
// from an instruction using the fast "compress" algorithm. See
// Instruction::Compress().
uint32_t GenerateOrderedPattern(uint32_t pattern) const;
// Generate a mask with a bit set at each sample position.
uint32_t GenerateSampledBitsMask() const;
// Try to compile a more optimised decode operation for this node, returning
// true if successful.
bool TryCompileOptimisedDecodeTable(Decoder* decoder);
// Helper function that returns a bit extracting function. If y is zero,
// x is a bit extraction mask. Otherwise, y is the mask, and x is the value
// to match after masking.
BitExtractFn GetBitExtractFunctionHelper(uint32_t x, uint32_t y);
// Name of this decoder node, used to construct edges in the decode graph.
std::string name_;
// Vector of bits sampled from an instruction to determine which node to look
// up next in the decode process.
const std::vector<uint8_t>& sampled_bits_;
static const std::vector<uint8_t> kEmptySampledBits;
// For leaf nodes, this is the name of the instruction form that the node
// represents. For other nodes, this is always set to "node".
std::string instruction_name_;
// Source mapping from bit pattern to name of next decode stage.
const std::vector<DecodePattern>& pattern_table_;
static const std::vector<DecodePattern> kEmptyPatternTable;
// Pointer to the decoder containing this node, used to call its visitor
// function for leaf nodes.
Decoder* decoder_;
// Pointer to the compiled version of this node. Is this node hasn't been
// compiled yet, this pointer is NULL.
CompiledDecodeNode* compiled_node_;
};
} // namespace aarch64
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+162 -6
View File
@@ -27,11 +27,16 @@
#ifndef VIXL_AARCH64_DISASM_AARCH64_H
#define VIXL_AARCH64_DISASM_AARCH64_H
#include <functional>
#include <unordered_map>
#include <utility>
#include "../globals-vixl.h"
#include "../utils-vixl.h"
#include "cpu-features-auditor-aarch64.h"
#include "decoder-aarch64.h"
#include "decoder-visitor-map-aarch64.h"
#include "instructions-aarch64.h"
#include "operands-aarch64.h"
@@ -45,11 +50,9 @@ class Disassembler : public DecoderVisitor {
virtual ~Disassembler();
char* GetOutput();
// Declare all Visitor functions.
#define DECLARE(A) \
virtual void Visit##A(const Instruction* instr) VIXL_OVERRIDE;
VISITOR_LIST(DECLARE)
#undef DECLARE
// Declare all Visitor functions.
virtual void Visit(Metadata* metadata,
const Instruction* instr) VIXL_OVERRIDE;
protected:
virtual void ProcessOutput(const Instruction* instr);
@@ -110,12 +113,145 @@ class Disassembler : public DecoderVisitor {
int64_t CodeRelativeAddress(const void* instr);
private:
#define DECLARE(A) virtual void Visit##A(const Instruction* instr);
VISITOR_LIST(DECLARE)
#undef DECLARE
using FormToVisitorFnMap = std::unordered_map<
uint32_t,
std::function<void(Disassembler*, const Instruction*)>>;
static const FormToVisitorFnMap* GetFormToVisitorFnMap();
std::string mnemonic_;
uint32_t form_hash_;
void SetMnemonicFromForm(const std::string& form) {
if (form != "unallocated") {
VIXL_ASSERT(form.find_first_of('_') != std::string::npos);
mnemonic_ = form.substr(0, form.find_first_of('_'));
}
}
void Disassemble_PdT_PgZ_ZnT_ZmT(const Instruction* instr);
void Disassemble_ZdB_Zn1B_Zn2B_imm(const Instruction* instr);
void Disassemble_ZdB_ZnB_ZmB(const Instruction* instr);
void Disassemble_ZdD_PgM_ZnS(const Instruction* instr);
void Disassemble_ZdD_ZnD_ZmD(const Instruction* instr);
void Disassemble_ZdD_ZnD_ZmD_imm(const Instruction* instr);
void Disassemble_ZdD_ZnS_ZmS_imm(const Instruction* instr);
void Disassemble_ZdH_PgM_ZnS(const Instruction* instr);
void Disassemble_ZdH_ZnH_ZmH_imm(const Instruction* instr);
void Disassemble_ZdS_PgM_ZnD(const Instruction* instr);
void Disassemble_ZdS_PgM_ZnH(const Instruction* instr);
void Disassemble_ZdS_PgM_ZnS(const Instruction* instr);
void Disassemble_ZdS_ZnH_ZmH_imm(const Instruction* instr);
void Disassemble_ZdS_ZnS_ZmS(const Instruction* instr);
void Disassemble_ZdS_ZnS_ZmS_imm(const Instruction* instr);
void Disassemble_ZdT_PgM_ZnT(const Instruction* instr);
void Disassemble_ZdT_PgZ_ZnT_ZmT(const Instruction* instr);
void Disassemble_ZdT_Pg_Zn1T_Zn2T(const Instruction* instr);
void Disassemble_ZdT_Zn1T_Zn2T_ZmT(const Instruction* instr);
void Disassemble_ZdT_ZnT_ZmT(const Instruction* instr);
void Disassemble_ZdT_ZnT_ZmTb(const Instruction* instr);
void Disassemble_ZdT_ZnTb(const Instruction* instr);
void Disassemble_ZdT_ZnTb_ZmTb(const Instruction* instr);
void Disassemble_ZdaD_ZnD_ZmD_imm(const Instruction* instr);
void Disassemble_ZdaD_ZnH_ZmH_imm_const(const Instruction* instr);
void Disassemble_ZdaD_ZnS_ZmS_imm(const Instruction* instr);
void Disassemble_ZdaH_ZnH_ZmH_imm(const Instruction* instr);
void Disassemble_ZdaH_ZnH_ZmH_imm_const(const Instruction* instr);
void Disassemble_ZdaS_ZnB_ZmB_imm_const(const Instruction* instr);
void Disassemble_ZdaS_ZnH_ZmH(const Instruction* instr);
void Disassemble_ZdaS_ZnH_ZmH_imm(const Instruction* instr);
void Disassemble_ZdaS_ZnS_ZmS_imm(const Instruction* instr);
void Disassemble_ZdaS_ZnS_ZmS_imm_const(const Instruction* instr);
void Disassemble_ZdaT_PgM_ZnTb(const Instruction* instr);
void Disassemble_ZdaT_ZnT_ZmT(const Instruction* instr);
void Disassemble_ZdaT_ZnT_ZmT_const(const Instruction* instr);
void Disassemble_ZdaT_ZnT_const(const Instruction* instr);
void Disassemble_ZdaT_ZnTb_ZmTb(const Instruction* instr);
void Disassemble_ZdaT_ZnTb_ZmTb_const(const Instruction* instr);
void Disassemble_ZdnB_ZdnB(const Instruction* instr);
void Disassemble_ZdnB_ZdnB_ZmB(const Instruction* instr);
void Disassemble_ZdnS_ZdnS_ZmS(const Instruction* instr);
void Disassemble_ZdnT_PgM_ZdnT_ZmT(const Instruction* instr);
void Disassemble_ZdnT_PgM_ZdnT_const(const Instruction* instr);
void Disassemble_ZdnT_ZdnT_ZmT_const(const Instruction* instr);
void Disassemble_ZtD_PgZ_ZnD_Xm(const Instruction* instr);
void Disassemble_ZtD_Pg_ZnD_Xm(const Instruction* instr);
void Disassemble_ZtS_PgZ_ZnS_Xm(const Instruction* instr);
void Disassemble_ZtS_Pg_ZnS_Xm(const Instruction* instr);
void Disassemble_ZdaS_ZnB_ZmB(const Instruction* instr);
void Disassemble_Vd4S_Vn16B_Vm16B(const Instruction* instr);
void DisassembleCpy(const Instruction* instr);
void DisassembleSet(const Instruction* instr);
void DisassembleMinMaxImm(const Instruction* instr);
void DisassembleSVEShiftLeftImm(const Instruction* instr);
void DisassembleSVEShiftRightImm(const Instruction* instr);
void DisassembleSVEAddSubCarry(const Instruction* instr);
void DisassembleSVEAddSubHigh(const Instruction* instr);
void DisassembleSVEComplexIntAddition(const Instruction* instr);
void DisassembleSVEBitwiseTernary(const Instruction* instr);
void DisassembleSVEFlogb(const Instruction* instr);
void DisassembleSVEFPPair(const Instruction* instr);
void DisassembleNoArgs(const Instruction* instr);
void DisassembleNEONMulByElementLong(const Instruction* instr);
void DisassembleNEONDotProdByElement(const Instruction* instr);
void DisassembleNEONFPMulByElement(const Instruction* instr);
void DisassembleNEONHalfFPMulByElement(const Instruction* instr);
void DisassembleNEONFPMulByElementLong(const Instruction* instr);
void DisassembleNEONComplexMulByElement(const Instruction* instr);
void DisassembleNEON2RegLogical(const Instruction* instr);
void DisassembleNEON2RegExtract(const Instruction* instr);
void DisassembleNEON2RegAddlp(const Instruction* instr);
void DisassembleNEON2RegCompare(const Instruction* instr);
void DisassembleNEON2RegFPCompare(const Instruction* instr);
void DisassembleNEON2RegFPConvert(const Instruction* instr);
void DisassembleNEON2RegFP(const Instruction* instr);
void DisassembleNEON3SameLogical(const Instruction* instr);
void DisassembleNEON3SameFHM(const Instruction* instr);
void DisassembleNEON3SameNoD(const Instruction* instr);
void DisassembleNEONShiftLeftLongImm(const Instruction* instr);
void DisassembleNEONShiftRightImm(const Instruction* instr);
void DisassembleNEONShiftRightNarrowImm(const Instruction* instr);
void DisassembleNEONScalarSatMulLongIndex(const Instruction* instr);
void DisassembleNEONFPScalarMulIndex(const Instruction* instr);
void DisassembleNEONFPScalar3Same(const Instruction* instr);
void DisassembleNEONScalar3SameOnlyD(const Instruction* instr);
void DisassembleNEONFPAcrossLanes(const Instruction* instr);
void DisassembleNEONFP16AcrossLanes(const Instruction* instr);
void DisassembleNEONScalarShiftImmOnlyD(const Instruction* instr);
void DisassembleNEONScalarShiftRightNarrowImm(const Instruction* instr);
void DisassembleNEONScalar2RegMiscOnlyD(const Instruction* instr);
void DisassembleNEONFPScalar2RegMisc(const Instruction* instr);
void DisassembleNEONPolynomialMul(const Instruction* instr);
void DisassembleMTELoadTag(const Instruction* instr);
void DisassembleMTEStoreTag(const Instruction* instr);
void DisassembleMTEStoreTagPair(const Instruction* instr);
void Disassemble_XdSP_XnSP_Xm(const Instruction* instr);
void Disassemble_XdSP_XnSP_uimm6_uimm4(const Instruction* instr);
void Disassemble_Xd_XnSP_Xm(const Instruction* instr);
void Disassemble_Xd_XnSP_XmSP(const Instruction* instr);
void Format(const Instruction* instr,
const char* mnemonic,
const char* format);
const char* format0,
const char* format1 = NULL);
void FormatWithDecodedMnemonic(const Instruction* instr,
const char* format0,
const char* format1 = NULL);
void Substitute(const Instruction* instr, const char* string);
int SubstituteField(const Instruction* instr, const char* format);
int SubstituteRegisterField(const Instruction* instr, const char* format);
int SubstitutePredicateRegisterField(const Instruction* instr,
const char* format);
int SubstituteImmediateField(const Instruction* instr, const char* format);
int SubstituteLiteralField(const Instruction* instr, const char* format);
int SubstituteBitfieldImmediateField(const Instruction* instr,
@@ -130,6 +266,14 @@ class Disassembler : public DecoderVisitor {
int SubstituteBarrierField(const Instruction* instr, const char* format);
int SubstituteSysOpField(const Instruction* instr, const char* format);
int SubstituteCrField(const Instruction* instr, const char* format);
int SubstituteIntField(const Instruction* instr, const char* format);
int SubstituteSVESize(const Instruction* instr, const char* format);
int SubstituteTernary(const Instruction* instr, const char* format);
std::pair<unsigned, unsigned> GetRegNumForField(const Instruction* instr,
char reg_prefix,
const char* field);
bool RdIsZROrSP(const Instruction* instr) const {
return (instr->GetRd() == kZeroRegCode);
}
@@ -173,6 +317,7 @@ class PrintDisassembler : public Disassembler {
: cpu_features_auditor_(NULL),
cpu_features_prefix_("// Needs: "),
cpu_features_suffix_(""),
signed_addresses_(false),
stream_(stream) {}
// Convenience helpers for quick disassembly, without having to manually
@@ -201,12 +346,23 @@ class PrintDisassembler : public Disassembler {
cpu_features_suffix_ = suffix;
}
// By default, addresses are printed as simple, unsigned 64-bit hex values.
//
// With `PrintSignedAddresses(true)`:
// - negative addresses are printed as "-0x1234...",
// - positive addresses have a leading space, like " 0x1234...", to maintain
// alignment.
//
// This is most useful in combination with Disassembler::MapCodeAddress(...).
void PrintSignedAddresses(bool s) { signed_addresses_ = s; }
protected:
virtual void ProcessOutput(const Instruction* instr) VIXL_OVERRIDE;
CPUFeaturesAuditor* cpu_features_auditor_;
const char* cpu_features_prefix_;
const char* cpu_features_suffix_;
bool signed_addresses_;
private:
FILE* stream_;
@@ -32,6 +32,11 @@
#include "constants-aarch64.h"
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-enum-enum-conversion"
#endif
namespace vixl {
namespace aarch64 {
// ISA constants. --------------------------------------------------------------
@@ -81,6 +86,7 @@ const uint64_t kXRegMask = UINT64_C(0xffffffffffffffff);
const uint64_t kHRegMask = UINT64_C(0xffff);
const uint64_t kSRegMask = UINT64_C(0xffffffff);
const uint64_t kDRegMask = UINT64_C(0xffffffffffffffff);
const uint64_t kHSignMask = UINT64_C(0x8000);
const uint64_t kSSignMask = UINT64_C(0x80000000);
const uint64_t kDSignMask = UINT64_C(0x8000000000000000);
const uint64_t kWSignMask = UINT64_C(0x80000000);
@@ -106,6 +112,8 @@ const unsigned kZeroRegCode = 31;
const unsigned kSPRegInternalCode = 63;
const unsigned kRegCodeMask = 0x1f;
const unsigned kAtomicAccessGranule = 16;
const unsigned kAddressTagOffset = 56;
const unsigned kAddressTagWidth = 8;
const uint64_t kAddressTagMask = ((UINT64_C(1) << kAddressTagWidth) - 1)
@@ -114,21 +122,49 @@ VIXL_STATIC_ASSERT(kAddressTagMask == UINT64_C(0xff00000000000000));
const uint64_t kTTBRMask = UINT64_C(1) << 55;
// We can't define a static kZRegSize because the size depends on the
// implementation. However, it is sometimes useful to know the minimum and
// maximum possible sizes.
const unsigned kZRegMinSize = 128;
const unsigned kZRegMinSizeLog2 = 7;
const unsigned kZRegMinSizeInBytes = kZRegMinSize / 8;
const unsigned kZRegMinSizeInBytesLog2 = kZRegMinSizeLog2 - 3;
const unsigned kZRegMaxSize = 2048;
const unsigned kZRegMaxSizeLog2 = 11;
const unsigned kZRegMaxSizeInBytes = kZRegMaxSize / 8;
const unsigned kZRegMaxSizeInBytesLog2 = kZRegMaxSizeLog2 - 3;
// The P register size depends on the Z register size.
const unsigned kZRegBitsPerPRegBit = kBitsPerByte;
const unsigned kZRegBitsPerPRegBitLog2 = 3;
const unsigned kPRegMinSize = kZRegMinSize / kZRegBitsPerPRegBit;
const unsigned kPRegMinSizeLog2 = kZRegMinSizeLog2 - 3;
const unsigned kPRegMinSizeInBytes = kPRegMinSize / 8;
const unsigned kPRegMinSizeInBytesLog2 = kPRegMinSizeLog2 - 3;
const unsigned kPRegMaxSize = kZRegMaxSize / kZRegBitsPerPRegBit;
const unsigned kPRegMaxSizeLog2 = kZRegMaxSizeLog2 - 3;
const unsigned kPRegMaxSizeInBytes = kPRegMaxSize / 8;
const unsigned kPRegMaxSizeInBytesLog2 = kPRegMaxSizeLog2 - 3;
const unsigned kMTETagGranuleInBytes = 16;
const unsigned kMTETagGranuleInBytesLog2 = 4;
const unsigned kMTETagWidth = 4;
// Make these moved float constants backwards compatible
// with explicit vixl::aarch64:: namespace references.
using vixl::kDoubleMantissaBits;
using vixl::kDoubleExponentBits;
using vixl::kFloatMantissaBits;
using vixl::kFloatExponentBits;
using vixl::kFloat16MantissaBits;
using vixl::kDoubleMantissaBits;
using vixl::kFloat16ExponentBits;
using vixl::kFloat16MantissaBits;
using vixl::kFloatExponentBits;
using vixl::kFloatMantissaBits;
using vixl::kFP16PositiveInfinity;
using vixl::kFP16NegativeInfinity;
using vixl::kFP32PositiveInfinity;
using vixl::kFP16PositiveInfinity;
using vixl::kFP32NegativeInfinity;
using vixl::kFP64PositiveInfinity;
using vixl::kFP32PositiveInfinity;
using vixl::kFP64NegativeInfinity;
using vixl::kFP64PositiveInfinity;
using vixl::kFP16DefaultNaN;
using vixl::kFP32DefaultNaN;
@@ -149,6 +185,49 @@ enum AddrMode { Offset, PreIndex, PostIndex };
enum Reg31Mode { Reg31IsStackPointer, Reg31IsZeroRegister };
enum VectorFormat {
kFormatUndefined = 0xffffffff,
kFormat8B = NEON_8B,
kFormat16B = NEON_16B,
kFormat4H = NEON_4H,
kFormat8H = NEON_8H,
kFormat2S = NEON_2S,
kFormat4S = NEON_4S,
kFormat1D = NEON_1D,
kFormat2D = NEON_2D,
// Scalar formats. We add the scalar bit to distinguish between scalar and
// vector enumerations; the bit is always set in the encoding of scalar ops
// and always clear for vector ops. Although kFormatD and kFormat1D appear
// to be the same, their meaning is subtly different. The first is a scalar
// operation, the second a vector operation that only affects one lane.
kFormatB = NEON_B | NEONScalar,
kFormatH = NEON_H | NEONScalar,
kFormatS = NEON_S | NEONScalar,
kFormatD = NEON_D | NEONScalar,
// An artificial value, used to distinguish from NEON format category.
kFormatSVE = 0x0000fffd,
// Artificial values. Q and O lane sizes aren't encoded in the usual size
// field.
kFormatSVEQ = 0x00080000,
kFormatSVEO = 0x00040000,
// Vector element width of SVE register with the unknown lane count since
// the vector length is implementation dependent.
kFormatVnB = SVE_B | kFormatSVE,
kFormatVnH = SVE_H | kFormatSVE,
kFormatVnS = SVE_S | kFormatSVE,
kFormatVnD = SVE_D | kFormatSVE,
kFormatVnQ = kFormatSVEQ | kFormatSVE,
kFormatVnO = kFormatSVEO | kFormatSVE,
// Artificial values, used by simulator trace tests and a few oddball
// instructions (such as FMLAL).
kFormat2H = 0xfffffffe,
kFormat1Q = 0xfffffffd
};
// Instructions. ---------------------------------------------------------------
class Instruction {
@@ -176,6 +255,47 @@ class Instruction {
return ExtractBits(msb, lsb);
}
// Compress bit extraction operation from Hacker's Delight.
// https://github.com/hcs0/Hackers-Delight/blob/master/compress.c.txt
uint32_t Compress(uint32_t mask) const {
uint32_t mk, mp, mv, t;
uint32_t x = GetInstructionBits() & mask; // Clear irrelevant bits.
mk = ~mask << 1; // We will count 0's to right.
for (int i = 0; i < 5; i++) {
mp = mk ^ (mk << 1); // Parallel suffix.
mp = mp ^ (mp << 2);
mp = mp ^ (mp << 4);
mp = mp ^ (mp << 8);
mp = mp ^ (mp << 16);
mv = mp & mask; // Bits to move.
mask = (mask ^ mv) | (mv >> (1 << i)); // Compress mask.
t = x & mv;
x = (x ^ t) | (t >> (1 << i)); // Compress x.
mk = mk & ~mp;
}
return x;
}
template <uint32_t M>
uint32_t ExtractBits() const {
return Compress(M);
}
uint32_t ExtractBitsAbsent() const {
VIXL_UNREACHABLE();
return 0;
}
template <uint32_t M, uint32_t V>
uint32_t IsMaskedValue() const {
return (Mask(M) == V) ? 1 : 0;
}
uint32_t IsMaskedValueAbsent() const {
VIXL_UNREACHABLE();
return 0;
}
int32_t ExtractSignedBits(int msb, int lsb) const {
int32_t bits = *(reinterpret_cast<const int32_t*>(this));
return ExtractSignedBitfield32(msb, lsb, bits);
@@ -196,6 +316,34 @@ class Instruction {
INSTRUCTION_FIELDS_LIST(DEFINE_GETTER)
#undef DEFINE_GETTER
template <int msb, int lsb>
int32_t GetRx() const {
// We don't have any register fields wider than five bits, so the result
// will always fit into an int32_t.
VIXL_ASSERT((msb - lsb + 1) <= 5);
return this->ExtractBits(msb, lsb);
}
VectorFormat GetSVEVectorFormat(int field_lsb = 22) const {
VIXL_ASSERT((field_lsb >= 0) && (field_lsb <= 30));
uint32_t instr = ExtractUnsignedBitfield32(field_lsb + 1,
field_lsb,
GetInstructionBits())
<< 22;
switch (instr & SVESizeFieldMask) {
case SVE_B:
return kFormatVnB;
case SVE_H:
return kFormatVnH;
case SVE_S:
return kFormatVnS;
case SVE_D:
return kFormatVnD;
}
VIXL_UNREACHABLE();
return kFormatUndefined;
}
// ImmPCRel is a compound field (not present in INSTRUCTION_FIELDS_LIST),
// formed from ImmPCRelLo and ImmPCRelHi.
int GetImmPCRel() const {
@@ -207,10 +355,40 @@ class Instruction {
}
VIXL_DEPRECATED("GetImmPCRel", int ImmPCRel() const) { return GetImmPCRel(); }
// ImmLSPAC is a compound field (not present in INSTRUCTION_FIELDS_LIST),
// formed from ImmLSPACLo and ImmLSPACHi.
int GetImmLSPAC() const {
uint32_t hi = static_cast<uint32_t>(GetImmLSPACHi());
uint32_t lo = GetImmLSPACLo();
uint32_t offset = (hi << ImmLSPACLo_width) | lo;
int width = ImmLSPACLo_width + ImmLSPACHi_width;
return ExtractSignedBitfield32(width - 1, 0, offset) << 3;
}
uint64_t GetImmLogical() const;
VIXL_DEPRECATED("GetImmLogical", uint64_t ImmLogical() const) {
return GetImmLogical();
}
uint64_t GetSVEImmLogical() const;
int GetSVEBitwiseImmLaneSizeInBytesLog2() const;
uint64_t DecodeImmBitMask(int32_t n,
int32_t imm_s,
int32_t imm_r,
int32_t size) const;
std::pair<int, int> GetSVEPermuteIndexAndLaneSizeLog2() const;
std::pair<int, int> GetSVEMulZmAndIndex() const;
std::pair<int, int> GetSVEMulLongZmAndIndex() const;
std::pair<int, int> GetSVEImmShiftAndLaneSizeLog2(bool is_predicated) const;
int GetSVEExtractImmediate() const;
int GetSVEMsizeFromDtype(bool is_signed, int dtype_h_lsb = 23) const;
int GetSVEEsizeFromDtype(bool is_signed, int dtype_l_lsb = 21) const;
unsigned GetImmNEONabcdefgh() const;
VIXL_DEPRECATED("GetImmNEONabcdefgh", unsigned ImmNEONabcdefgh() const) {
@@ -237,6 +415,16 @@ class Instruction {
return GetImmNEONFP64();
}
Float16 GetSVEImmFP16() const { return Imm8ToFloat16(ExtractBits(12, 5)); }
float GetSVEImmFP32() const { return Imm8ToFP32(ExtractBits(12, 5)); }
double GetSVEImmFP64() const { return Imm8ToFP64(ExtractBits(12, 5)); }
static Float16 Imm8ToFloat16(uint32_t imm8);
static float Imm8ToFP32(uint32_t imm8);
static double Imm8ToFP64(uint32_t imm8);
unsigned GetSizeLS() const {
return CalcLSDataSize(static_cast<LoadStoreOp>(Mask(LoadStoreMask)));
}
@@ -299,6 +487,10 @@ class Instruction {
return Mask(LoadStoreAnyFMask) == LoadStoreAnyFixed;
}
// True if `this` is valid immediately after the provided movprfx instruction.
bool CanTakeSVEMovprfx(uint32_t form_hash, Instruction const* movprfx) const;
bool CanTakeSVEMovprfx(const char* form, Instruction const* movprfx) const;
bool IsLoad() const;
bool IsStore() const;
@@ -312,6 +504,83 @@ class Instruction {
(Mask(MoveWideImmediateMask) == MOVN_w);
}
bool IsException() const { return Mask(ExceptionFMask) == ExceptionFixed; }
bool IsPAuth() const { return Mask(SystemPAuthFMask) == SystemPAuthFixed; }
bool IsBti() const {
if (Mask(SystemHintFMask) == SystemHintFixed) {
int imm_hint = GetImmHint();
switch (imm_hint) {
case BTI:
case BTI_c:
case BTI_j:
case BTI_jc:
return true;
}
}
return false;
}
bool IsMOPSPrologueOf(const Instruction* instr, uint32_t mops_type) const {
VIXL_ASSERT((mops_type == "set"_h) || (mops_type == "setg"_h) ||
(mops_type == "cpy"_h));
const int op_lsb = (mops_type == "cpy"_h) ? 22 : 14;
return GetInstructionBits() == instr->Mask(~(0x3U << op_lsb));
}
bool IsMOPSMainOf(const Instruction* instr, uint32_t mops_type) const {
VIXL_ASSERT((mops_type == "set"_h) || (mops_type == "setg"_h) ||
(mops_type == "cpy"_h));
const int op_lsb = (mops_type == "cpy"_h) ? 22 : 14;
return GetInstructionBits() ==
(instr->Mask(~(0x3U << op_lsb)) | (0x1 << op_lsb));
}
bool IsMOPSEpilogueOf(const Instruction* instr, uint32_t mops_type) const {
VIXL_ASSERT((mops_type == "set"_h) || (mops_type == "setg"_h) ||
(mops_type == "cpy"_h));
const int op_lsb = (mops_type == "cpy"_h) ? 22 : 14;
return GetInstructionBits() ==
(instr->Mask(~(0x3U << op_lsb)) | (0x2 << op_lsb));
}
template <uint32_t mops_type>
bool IsConsistentMOPSTriplet() const {
VIXL_STATIC_ASSERT((mops_type == "set"_h) || (mops_type == "setg"_h) ||
(mops_type == "cpy"_h));
int64_t isize = static_cast<int64_t>(kInstructionSize);
const Instruction* prev2 = GetInstructionAtOffset(-2 * isize);
const Instruction* prev1 = GetInstructionAtOffset(-1 * isize);
const Instruction* next1 = GetInstructionAtOffset(1 * isize);
const Instruction* next2 = GetInstructionAtOffset(2 * isize);
// Use the encoding of the current instruction to determine the expected
// adjacent instructions. NB. this doesn't check if the nearby instructions
// are MOPS-type, but checks that they form a consistent triplet if they
// are. For example, 'mov x0, #0; mov x0, #512; mov x0, #1024' is a
// consistent triplet, but they are not MOPS instructions.
const int op_lsb = (mops_type == "cpy"_h) ? 22 : 14;
const uint32_t kMOPSOpfield = 0x3 << op_lsb;
const uint32_t kMOPSPrologue = 0;
const uint32_t kMOPSMain = 0x1 << op_lsb;
const uint32_t kMOPSEpilogue = 0x2 << op_lsb;
switch (Mask(kMOPSOpfield)) {
case kMOPSPrologue:
return next1->IsMOPSMainOf(this, mops_type) &&
next2->IsMOPSEpilogueOf(this, mops_type);
case kMOPSMain:
return prev1->IsMOPSPrologueOf(this, mops_type) &&
next1->IsMOPSEpilogueOf(this, mops_type);
case kMOPSEpilogue:
return prev2->IsMOPSPrologueOf(this, mops_type) &&
prev1->IsMOPSMainOf(this, mops_type);
default:
VIXL_ABORT_WITH_MSG("Undefined MOPS operation\n");
}
}
static int GetImmBranchRangeBitwidth(ImmBranchType branch_type);
VIXL_DEPRECATED(
"GetImmBranchRangeBitwidth",
@@ -496,40 +765,12 @@ class Instruction {
private:
int GetImmBranch() const;
static Float16 Imm8ToFloat16(uint32_t imm8);
static float Imm8ToFP32(uint32_t imm8);
static double Imm8ToFP64(uint32_t imm8);
void SetPCRelImmTarget(const Instruction* target);
void SetBranchImmTarget(const Instruction* target);
};
// Functions for handling NEON vector format information.
enum VectorFormat {
kFormatUndefined = 0xffffffff,
kFormat8B = NEON_8B,
kFormat16B = NEON_16B,
kFormat4H = NEON_4H,
kFormat8H = NEON_8H,
kFormat2S = NEON_2S,
kFormat4S = NEON_4S,
kFormat1D = NEON_1D,
kFormat2D = NEON_2D,
// Scalar formats. We add the scalar bit to distinguish between scalar and
// vector enumerations; the bit is always set in the encoding of scalar ops
// and always clear for vector ops. Although kFormatD and kFormat1D appear
// to be the same, their meaning is subtly different. The first is a scalar
// operation, the second a vector operation that only affects one lane.
kFormatB = NEON_B | NEONScalar,
kFormatH = NEON_H | NEONScalar,
kFormatS = NEON_S | NEONScalar,
kFormatD = NEON_D | NEONScalar,
// A value invented solely for FP16 scalar pairwise simulator trace tests.
kFormat2H = 0xfffffffe
};
// Functions for handling NEON and SVE vector format information.
const int kMaxLanesPerVector = 16;
@@ -537,12 +778,16 @@ VectorFormat VectorFormatHalfWidth(VectorFormat vform);
VectorFormat VectorFormatDoubleWidth(VectorFormat vform);
VectorFormat VectorFormatDoubleLanes(VectorFormat vform);
VectorFormat VectorFormatHalfLanes(VectorFormat vform);
VectorFormat ScalarFormatFromLaneSize(int lanesize);
VectorFormat ScalarFormatFromLaneSize(int lane_size_in_bits);
VectorFormat VectorFormatHalfWidthDoubleLanes(VectorFormat vform);
VectorFormat VectorFormatFillQ(VectorFormat vform);
VectorFormat ScalarFormatFromFormat(VectorFormat vform);
VectorFormat SVEFormatFromLaneSizeInBits(int lane_size_in_bits);
VectorFormat SVEFormatFromLaneSizeInBytes(int lane_size_in_bytes);
VectorFormat SVEFormatFromLaneSizeInBytesLog2(int lane_size_in_bytes_log_2);
unsigned RegisterSizeInBitsFromFormat(VectorFormat vform);
unsigned RegisterSizeInBytesFromFormat(VectorFormat vform);
bool IsSVEFormat(VectorFormat vform);
// TODO: Make the return types of these functions consistent.
unsigned LaneSizeInBitsFromFormat(VectorFormat vform);
int LaneSizeInBytesFromFormat(VectorFormat vform);
@@ -588,7 +833,7 @@ class NEONFormatDecoder {
enum SubstitutionMode { kPlaceholder, kFormat };
// Construct a format decoder with increasingly specific format maps for each
// subsitution. If no format map is specified, the default is the integer
// substitution. If no format map is specified, the default is the integer
// format map.
explicit NEONFormatDecoder(const Instruction* instr) {
instrbits_ = instr->GetInstructionBits();
@@ -639,18 +884,26 @@ class NEONFormatDecoder {
SubstitutionMode mode0 = kFormat,
SubstitutionMode mode1 = kFormat,
SubstitutionMode mode2 = kFormat) {
const char* subst0 = GetSubstitute(0, mode0);
const char* subst1 = GetSubstitute(1, mode1);
const char* subst2 = GetSubstitute(2, mode2);
if ((subst0 == NULL) || (subst1 == NULL) || (subst2 == NULL)) {
return NULL;
}
snprintf(form_buffer_,
sizeof(form_buffer_),
string,
GetSubstitute(0, mode0),
GetSubstitute(1, mode1),
GetSubstitute(2, mode2));
subst0,
subst1,
subst2);
return form_buffer_;
}
// Append a "2" to a mnemonic string based of the state of the Q bit.
// Append a "2" to a mnemonic string based on the state of the Q bit.
const char* Mnemonic(const char* mnemonic) {
if ((instrbits_ & NEON_Q) != 0) {
if ((mnemonic != NULL) && (instrbits_ & NEON_Q) != 0) {
snprintf(mne_buffer_, sizeof(mne_buffer_), "%s2", mnemonic);
return mne_buffer_;
}
@@ -745,6 +998,33 @@ class NEONFormatDecoder {
return &map;
}
// The shift immediate map uses between two and five bits to encode the NEON
// vector format:
// 00010->8B, 00011->16B, 001x0->4H, 001x1->8H,
// 01xx0->2S, 01xx1->4S, 1xxx1->2D, all others undefined.
static const NEONFormatMap* ShiftImmFormatMap() {
static const NEONFormatMap map = {{22, 21, 20, 19, 30},
{NF_UNDEF, NF_UNDEF, NF_8B, NF_16B,
NF_4H, NF_8H, NF_4H, NF_8H,
NF_2S, NF_4S, NF_2S, NF_4S,
NF_2S, NF_4S, NF_2S, NF_4S,
NF_UNDEF, NF_2D, NF_UNDEF, NF_2D,
NF_UNDEF, NF_2D, NF_UNDEF, NF_2D,
NF_UNDEF, NF_2D, NF_UNDEF, NF_2D,
NF_UNDEF, NF_2D, NF_UNDEF, NF_2D}};
return &map;
}
// The shift long/narrow immediate map uses between two and four bits to
// encode the NEON vector format:
// 0001->8H, 001x->4S, 01xx->2D, all others undefined.
static const NEONFormatMap* ShiftLongNarrowImmFormatMap() {
static const NEONFormatMap map =
{{22, 21, 20, 19},
{NF_UNDEF, NF_8H, NF_4S, NF_4S, NF_2D, NF_2D, NF_2D, NF_2D}};
return &map;
}
// The scalar format map uses two bits (size<1:0>) to encode the NEON scalar
// formats: NF_B, NF_H, NF_S, NF_D.
static const NEONFormatMap* ScalarFormatMap() {
@@ -818,7 +1098,7 @@ class NEONFormatDecoder {
static const char* NEONFormatAsString(NEONFormat format) {
// clang-format off
static const char* formats[] = {
"undefined",
NULL,
"8b", "16b", "4h", "8h", "2s", "4s", "1d", "2d",
"b", "h", "s", "d"
};
@@ -833,9 +1113,9 @@ class NEONFormatDecoder {
(format == NF_D) || (format == NF_UNDEF));
// clang-format off
static const char* formats[] = {
"undefined",
"undefined", "undefined", "undefined", "undefined",
"undefined", "undefined", "undefined", "undefined",
NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
"'B", "'H", "'S", "'D"
};
// clang-format on
@@ -862,4 +1142,8 @@ class NEONFormatDecoder {
} // namespace aarch64
} // namespace vixl
#ifdef __clang__
#pragma clang diagnostic pop
#endif
#endif // VIXL_AARCH64_INSTRUCTIONS_AARCH64_H_
@@ -1,117 +0,0 @@
// Copyright 2014, 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_INSTRUMENT_AARCH64_H_
#define VIXL_AARCH64_INSTRUMENT_AARCH64_H_
#include "../globals-vixl.h"
#include "../utils-vixl.h"
#include "constants-aarch64.h"
#include "decoder-aarch64.h"
#include "instrument-aarch64.h"
namespace vixl {
namespace aarch64 {
const int kCounterNameMaxLength = 256;
const uint64_t kDefaultInstrumentationSamplingPeriod = 1 << 22;
enum InstrumentState { InstrumentStateDisable = 0, InstrumentStateEnable = 1 };
enum CounterType {
Gauge = 0, // Gauge counters reset themselves after reading.
Cumulative = 1 // Cumulative counters keep their value after reading.
};
class Counter {
public:
explicit Counter(const char* name, CounterType type = Gauge);
void Increment();
void Enable();
void Disable();
bool IsEnabled();
uint64_t GetCount();
VIXL_DEPRECATED("GetCount", uint64_t count()) { return GetCount(); }
const char* GetName();
VIXL_DEPRECATED("GetName", const char* name()) { return GetName(); }
CounterType GetType();
VIXL_DEPRECATED("GetType", CounterType type()) { return GetType(); }
private:
char name_[kCounterNameMaxLength];
uint64_t count_;
bool enabled_;
CounterType type_;
};
class Instrument : public DecoderVisitor {
public:
explicit Instrument(
const char* datafile = NULL,
uint64_t sample_period = kDefaultInstrumentationSamplingPeriod);
~Instrument();
void Enable();
void Disable();
// Declare all Visitor functions.
#define DECLARE(A) void Visit##A(const Instruction* instr) VIXL_OVERRIDE;
VISITOR_LIST(DECLARE)
#undef DECLARE
private:
void Update();
void DumpCounters();
void DumpCounterNames();
void DumpEventMarker(unsigned marker);
void HandleInstrumentationEvent(unsigned event);
Counter* GetCounter(const char* name);
void InstrumentLoadStore(const Instruction* instr);
void InstrumentLoadStorePair(const Instruction* instr);
std::list<Counter*> counters_;
FILE* output_stream_;
// Counter information is dumped every sample_period_ instructions decoded.
// For a sample_period_ = 0 a final counter value is only produced when the
// Instrumentation class is destroyed.
uint64_t sample_period_;
};
} // namespace aarch64
} // namespace vixl
#endif // VIXL_AARCH64_INSTRUMENT_AARCH64_H_
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,902 @@
// Copyright 2019, 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_REGISTERS_AARCH64_H_
#define VIXL_AARCH64_REGISTERS_AARCH64_H_
#include <string>
#include "instructions-aarch64.h"
namespace vixl {
namespace aarch64 {
// An integer type capable of representing a homogeneous, non-overlapping set of
// registers as a bitmask of their codes.
typedef uint64_t RegList;
static const int kRegListSizeInBits = sizeof(RegList) * 8;
class Register;
class WRegister;
class XRegister;
class VRegister;
class BRegister;
class HRegister;
class SRegister;
class DRegister;
class QRegister;
class ZRegister;
class PRegister;
class PRegisterWithLaneSize;
class PRegisterM;
class PRegisterZ;
// A container for any single register supported by the processor. Selected
// qualifications are also supported. Basic registers can be constructed
// directly as CPURegister objects. Other variants should be constructed as one
// of the derived classes.
//
// CPURegister aims to support any getter that would also be available to more
// specialised register types. However, using the equivalent functions on the
// specialised register types can avoid run-time checks, and should therefore be
// preferred where run-time polymorphism isn't required.
//
// Type-specific modifiers are typically implemented only on the derived
// classes.
//
// The encoding is such that CPURegister objects are cheap to pass by value.
class CPURegister {
public:
enum RegisterBank : uint8_t {
kNoRegisterBank = 0,
kRRegisterBank,
kVRegisterBank,
kPRegisterBank
};
enum RegisterType {
kNoRegister,
kRegister,
kVRegister,
kZRegister,
kPRegister
};
static const unsigned kUnknownSize = 0;
VIXL_CONSTEXPR CPURegister()
: code_(0),
bank_(kNoRegisterBank),
size_(kEncodedUnknownSize),
qualifiers_(kNoQualifiers),
lane_size_(kEncodedUnknownSize) {}
CPURegister(int code, int size_in_bits, RegisterType type)
: code_(code),
bank_(GetBankFor(type)),
size_(EncodeSizeInBits(size_in_bits)),
qualifiers_(kNoQualifiers),
lane_size_(EncodeSizeInBits(size_in_bits)) {
VIXL_ASSERT(IsValid());
}
// Basic accessors.
// TODO: Make this return 'int'.
unsigned GetCode() const { return code_; }
RegisterBank GetBank() const { return bank_; }
// For scalar registers, the lane size matches the register size, and is
// always known.
bool HasSize() const { return size_ != kEncodedUnknownSize; }
bool HasLaneSize() const { return lane_size_ != kEncodedUnknownSize; }
RegList GetBit() const {
if (IsNone()) return 0;
VIXL_ASSERT(code_ < kRegListSizeInBits);
return static_cast<RegList>(1) << code_;
}
// Return the architectural name for this register.
// TODO: This is temporary. Ultimately, we should move the
// Simulator::*RegNameForCode helpers out of the simulator, and provide an
// independent way to obtain the name of a register.
std::string GetArchitecturalName() const;
// Return the highest valid register code for this type, to allow generic
// loops to be written. This excludes kSPRegInternalCode, since it is not
// contiguous, and sp usually requires special handling anyway.
unsigned GetMaxCode() const { return GetMaxCodeFor(GetBank()); }
// Registers without a known size report kUnknownSize.
int GetSizeInBits() const { return DecodeSizeInBits(size_); }
int GetSizeInBytes() const { return DecodeSizeInBytes(size_); }
// TODO: Make these return 'int'.
unsigned GetLaneSizeInBits() const { return DecodeSizeInBits(lane_size_); }
unsigned GetLaneSizeInBytes() const { return DecodeSizeInBytes(lane_size_); }
unsigned GetLaneSizeInBytesLog2() const {
VIXL_ASSERT(HasLaneSize());
return DecodeSizeInBytesLog2(lane_size_);
}
int GetLanes() const {
if (HasSize() && HasLaneSize()) {
// Take advantage of the size encoding to calculate this efficiently.
VIXL_STATIC_ASSERT(kEncodedHRegSize == (kEncodedBRegSize + 1));
VIXL_STATIC_ASSERT(kEncodedSRegSize == (kEncodedHRegSize + 1));
VIXL_STATIC_ASSERT(kEncodedDRegSize == (kEncodedSRegSize + 1));
VIXL_STATIC_ASSERT(kEncodedQRegSize == (kEncodedDRegSize + 1));
int log2_delta = static_cast<int>(size_) - static_cast<int>(lane_size_);
VIXL_ASSERT(log2_delta >= 0);
return 1 << log2_delta;
}
return kUnknownSize;
}
bool Is8Bits() const { return size_ == kEncodedBRegSize; }
bool Is16Bits() const { return size_ == kEncodedHRegSize; }
bool Is32Bits() const { return size_ == kEncodedSRegSize; }
bool Is64Bits() const { return size_ == kEncodedDRegSize; }
bool Is128Bits() const { return size_ == kEncodedQRegSize; }
bool IsLaneSizeB() const { return lane_size_ == kEncodedBRegSize; }
bool IsLaneSizeH() const { return lane_size_ == kEncodedHRegSize; }
bool IsLaneSizeS() const { return lane_size_ == kEncodedSRegSize; }
bool IsLaneSizeD() const { return lane_size_ == kEncodedDRegSize; }
bool IsLaneSizeQ() const { return lane_size_ == kEncodedQRegSize; }
// If Is<Foo>Register(), then it is valid to convert the CPURegister to some
// <Foo>Register<Bar> type.
//
// If... ... then it is safe to construct ...
// r.IsRegister() -> Register(r)
// r.IsVRegister() -> VRegister(r)
// r.IsZRegister() -> ZRegister(r)
// r.IsPRegister() -> PRegister(r)
//
// r.IsPRegister() && HasLaneSize() -> PRegisterWithLaneSize(r)
// r.IsPRegister() && IsMerging() -> PRegisterM(r)
// r.IsPRegister() && IsZeroing() -> PRegisterZ(r)
bool IsRegister() const { return GetType() == kRegister; }
bool IsVRegister() const { return GetType() == kVRegister; }
bool IsZRegister() const { return GetType() == kZRegister; }
bool IsPRegister() const { return GetType() == kPRegister; }
bool IsNone() const { return GetType() == kNoRegister; }
// `GetType() == kNoRegister` implies IsNone(), and vice-versa.
// `GetType() == k<Foo>Register` implies Is<Foo>Register(), and vice-versa.
RegisterType GetType() const {
switch (bank_) {
case kNoRegisterBank:
return kNoRegister;
case kRRegisterBank:
return kRegister;
case kVRegisterBank:
return HasSize() ? kVRegister : kZRegister;
case kPRegisterBank:
return kPRegister;
}
VIXL_UNREACHABLE();
return kNoRegister;
}
// IsFPRegister() is true for scalar FP types (and therefore implies
// IsVRegister()). There is no corresponding FPRegister type.
bool IsFPRegister() const { return Is1H() || Is1S() || Is1D(); }
// TODO: These are stricter forms of the helpers above. We should make the
// basic helpers strict, and remove these.
bool IsValidRegister() const;
bool IsValidVRegister() const;
bool IsValidFPRegister() const;
bool IsValidZRegister() const;
bool IsValidPRegister() const;
bool IsValid() const;
bool IsValidOrNone() const { return IsNone() || IsValid(); }
bool IsVector() const { return HasLaneSize() && (size_ != lane_size_); }
bool IsScalar() const { return HasLaneSize() && (size_ == lane_size_); }
bool IsSameType(const CPURegister& other) const {
return GetType() == other.GetType();
}
bool IsSameBank(const CPURegister& other) const {
return GetBank() == other.GetBank();
}
// Two registers with unknown size are considered to have the same size if
// they also have the same type. For example, all Z registers have the same
// size, even though we don't know what that is.
bool IsSameSizeAndType(const CPURegister& other) const {
return IsSameType(other) && (size_ == other.size_);
}
bool IsSameFormat(const CPURegister& other) const {
return IsSameSizeAndType(other) && (lane_size_ == other.lane_size_);
}
// Note that NoReg aliases itself, so that 'Is' implies 'Aliases'.
bool Aliases(const CPURegister& other) const {
return IsSameBank(other) && (code_ == other.code_);
}
bool Is(const CPURegister& other) const {
if (IsRegister() || IsVRegister()) {
// For core (W, X) and FP/NEON registers, we only consider the code, size
// and type. This is legacy behaviour.
// TODO: We should probably check every field for all registers.
return Aliases(other) && (size_ == other.size_);
} else {
// For Z and P registers, we require all fields to match exactly.
VIXL_ASSERT(IsNone() || IsZRegister() || IsPRegister());
return (code_ == other.code_) && (bank_ == other.bank_) &&
(size_ == other.size_) && (qualifiers_ == other.qualifiers_) &&
(lane_size_ == other.lane_size_);
}
}
// Conversions to specific register types. The result is a register that
// aliases the original CPURegister. That is, the original register bank
// (`GetBank()`) is checked and the code (`GetCode()`) preserved, but all
// other properties are ignored.
//
// Typical usage:
//
// if (reg.GetBank() == kVRegisterBank) {
// DRegister d = reg.D();
// ...
// }
//
// These could all return types with compile-time guarantees (like XRegister),
// but this breaks backwards-compatibility quite severely, particularly with
// code like `cond ? reg.W() : reg.X()`, which would have indeterminate type.
// Core registers, like "w0".
Register W() const;
Register X() const;
// FP/NEON registers, like "b0".
VRegister B() const;
VRegister H() const;
VRegister S() const;
VRegister D() const;
VRegister Q() const;
VRegister V() const;
// SVE registers, like "z0".
ZRegister Z() const;
PRegister P() const;
// Utilities for kRegister types.
bool IsZero() const { return IsRegister() && (code_ == kZeroRegCode); }
bool IsSP() const { return IsRegister() && (code_ == kSPRegInternalCode); }
bool IsW() const { return IsRegister() && Is32Bits(); }
bool IsX() const { return IsRegister() && Is64Bits(); }
// Utilities for FP/NEON kVRegister types.
// These helpers 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.
bool IsB() const { return IsVRegister() && Is8Bits(); }
bool IsH() const { return IsVRegister() && Is16Bits(); }
bool IsS() const { return IsVRegister() && Is32Bits(); }
bool IsD() const { return IsVRegister() && Is64Bits(); }
bool IsQ() const { return IsVRegister() && Is128Bits(); }
// As above, but also check that the register has exactly one lane. For
// example, reg.Is1D() implies DRegister(reg).IsValid(), but reg.IsD() does
// not.
bool Is1B() const { return IsB() && IsScalar(); }
bool Is1H() const { return IsH() && IsScalar(); }
bool Is1S() const { return IsS() && IsScalar(); }
bool Is1D() const { return IsD() && IsScalar(); }
bool Is1Q() const { return IsQ() && IsScalar(); }
// Check the specific NEON format.
bool Is8B() const { return IsD() && IsLaneSizeB(); }
bool Is16B() const { return IsQ() && IsLaneSizeB(); }
bool Is2H() const { return IsS() && IsLaneSizeH(); }
bool Is4H() const { return IsD() && IsLaneSizeH(); }
bool Is8H() const { return IsQ() && IsLaneSizeH(); }
bool Is2S() const { return IsD() && IsLaneSizeS(); }
bool Is4S() const { return IsQ() && IsLaneSizeS(); }
bool Is2D() const { return IsQ() && IsLaneSizeD(); }
// A semantic alias for sdot and udot (indexed and by element) instructions.
// The current CPURegister implementation cannot not tell this from Is1S(),
// but it might do later.
// TODO: Do this with the qualifiers_ field.
bool Is1S4B() const { return Is1S(); }
// Utilities for SVE registers.
bool IsUnqualified() const { return qualifiers_ == kNoQualifiers; }
bool IsMerging() const { return IsPRegister() && (qualifiers_ == kMerging); }
bool IsZeroing() const { return IsPRegister() && (qualifiers_ == kZeroing); }
// SVE types have unknown sizes, but within known bounds.
int GetMaxSizeInBytes() const {
switch (GetType()) {
case kZRegister:
return kZRegMaxSizeInBytes;
case kPRegister:
return kPRegMaxSizeInBytes;
default:
VIXL_ASSERT(HasSize());
return GetSizeInBits();
}
}
int GetMinSizeInBytes() const {
switch (GetType()) {
case kZRegister:
return kZRegMinSizeInBytes;
case kPRegister:
return kPRegMinSizeInBytes;
default:
VIXL_ASSERT(HasSize());
return GetSizeInBits();
}
}
int GetMaxSizeInBits() const { return GetMaxSizeInBytes() * kBitsPerByte; }
int GetMinSizeInBits() const { return GetMinSizeInBytes() * kBitsPerByte; }
static RegisterBank GetBankFor(RegisterType type) {
switch (type) {
case kNoRegister:
return kNoRegisterBank;
case kRegister:
return kRRegisterBank;
case kVRegister:
case kZRegister:
return kVRegisterBank;
case kPRegister:
return kPRegisterBank;
}
VIXL_UNREACHABLE();
return kNoRegisterBank;
}
static unsigned GetMaxCodeFor(CPURegister::RegisterType type) {
return GetMaxCodeFor(GetBankFor(type));
}
protected:
enum EncodedSize : uint8_t {
// Ensure that kUnknownSize (and therefore kNoRegister) is encoded as zero.
kEncodedUnknownSize = 0,
// The implementation assumes that the remaining sizes are encoded as
// `log2(size) + c`, so the following names must remain in sequence.
kEncodedBRegSize,
kEncodedHRegSize,
kEncodedSRegSize,
kEncodedDRegSize,
kEncodedQRegSize,
kEncodedWRegSize = kEncodedSRegSize,
kEncodedXRegSize = kEncodedDRegSize
};
VIXL_STATIC_ASSERT(kSRegSize == kWRegSize);
VIXL_STATIC_ASSERT(kDRegSize == kXRegSize);
char GetLaneSizeSymbol() const {
switch (lane_size_) {
case kEncodedBRegSize:
return 'B';
case kEncodedHRegSize:
return 'H';
case kEncodedSRegSize:
return 'S';
case kEncodedDRegSize:
return 'D';
case kEncodedQRegSize:
return 'Q';
case kEncodedUnknownSize:
break;
}
VIXL_UNREACHABLE();
return '?';
}
static EncodedSize EncodeSizeInBits(int size_in_bits) {
switch (size_in_bits) {
case kUnknownSize:
return kEncodedUnknownSize;
case kBRegSize:
return kEncodedBRegSize;
case kHRegSize:
return kEncodedHRegSize;
case kSRegSize:
return kEncodedSRegSize;
case kDRegSize:
return kEncodedDRegSize;
case kQRegSize:
return kEncodedQRegSize;
}
VIXL_UNREACHABLE();
return kEncodedUnknownSize;
}
static int DecodeSizeInBytesLog2(EncodedSize encoded_size) {
switch (encoded_size) {
case kEncodedUnknownSize:
// Log2 of B-sized lane in bytes is 0, so we can't just return 0 here.
VIXL_UNREACHABLE();
return -1;
case kEncodedBRegSize:
return kBRegSizeInBytesLog2;
case kEncodedHRegSize:
return kHRegSizeInBytesLog2;
case kEncodedSRegSize:
return kSRegSizeInBytesLog2;
case kEncodedDRegSize:
return kDRegSizeInBytesLog2;
case kEncodedQRegSize:
return kQRegSizeInBytesLog2;
}
VIXL_UNREACHABLE();
return kUnknownSize;
}
static int DecodeSizeInBytes(EncodedSize encoded_size) {
if (encoded_size == kEncodedUnknownSize) {
return kUnknownSize;
}
return 1 << DecodeSizeInBytesLog2(encoded_size);
}
static int DecodeSizeInBits(EncodedSize encoded_size) {
VIXL_STATIC_ASSERT(kUnknownSize == 0);
return DecodeSizeInBytes(encoded_size) * kBitsPerByte;
}
static unsigned GetMaxCodeFor(CPURegister::RegisterBank bank);
enum Qualifiers : uint8_t {
kNoQualifiers = 0,
// Used by P registers.
kMerging,
kZeroing
};
// An unchecked constructor, for use by derived classes.
CPURegister(int code,
EncodedSize size,
RegisterBank bank,
EncodedSize lane_size,
Qualifiers qualifiers = kNoQualifiers)
: code_(code),
bank_(bank),
size_(size),
qualifiers_(qualifiers),
lane_size_(lane_size) {}
// TODO: Check that access to these fields is reasonably efficient.
uint8_t code_;
RegisterBank bank_;
EncodedSize size_;
Qualifiers qualifiers_;
EncodedSize lane_size_;
};
// Ensure that CPURegisters can fit in a single (64-bit) register. This is a
// proxy for being "cheap to pass by value", which is hard to check directly.
VIXL_STATIC_ASSERT(sizeof(CPURegister) <= sizeof(uint64_t));
// TODO: Add constexpr constructors.
#define VIXL_DECLARE_REGISTER_COMMON(NAME, REGISTER_TYPE, PARENT_TYPE) \
VIXL_CONSTEXPR NAME() : PARENT_TYPE() {} \
\
explicit NAME(CPURegister other) : PARENT_TYPE(other) { \
VIXL_ASSERT(IsValid()); \
} \
\
VIXL_CONSTEXPR static unsigned GetMaxCode() { \
return kNumberOf##REGISTER_TYPE##s - 1; \
}
// Any W or X register, including the zero register and the stack pointer.
class Register : public CPURegister {
public:
VIXL_DECLARE_REGISTER_COMMON(Register, Register, CPURegister)
Register(int code, int size_in_bits)
: CPURegister(code, size_in_bits, kRegister) {
VIXL_ASSERT(IsValidRegister());
}
bool IsValid() const { return IsValidRegister(); }
};
// Any FP or NEON V register, including vector (V.<T>) and scalar forms
// (B, H, S, D, Q).
class VRegister : public CPURegister {
public:
VIXL_DECLARE_REGISTER_COMMON(VRegister, VRegister, CPURegister)
// For historical reasons, VRegister(0) returns v0.1Q (or equivalently, q0).
explicit VRegister(int code, int size_in_bits = kQRegSize, int lanes = 1)
: CPURegister(code,
EncodeSizeInBits(size_in_bits),
kVRegisterBank,
EncodeLaneSizeInBits(size_in_bits, lanes)) {
VIXL_ASSERT(IsValidVRegister());
}
VRegister(int code, VectorFormat format)
: CPURegister(code,
EncodeSizeInBits(RegisterSizeInBitsFromFormat(format)),
kVRegisterBank,
EncodeSizeInBits(LaneSizeInBitsFromFormat(format)),
kNoQualifiers) {
VIXL_ASSERT(IsValid());
}
VRegister V8B() const;
VRegister V16B() const;
VRegister V2H() const;
VRegister V4H() const;
VRegister V8H() const;
VRegister V2S() const;
VRegister V4S() const;
VRegister V1D() const;
VRegister V2D() const;
VRegister V1Q() const;
VRegister S4B() const;
bool IsValid() const { return IsValidVRegister(); }
protected:
static EncodedSize EncodeLaneSizeInBits(int size_in_bits, int lanes) {
VIXL_ASSERT(lanes >= 1);
VIXL_ASSERT((size_in_bits % lanes) == 0);
return EncodeSizeInBits(size_in_bits / lanes);
}
};
// Any SVE Z register, with or without a lane size specifier.
class ZRegister : public CPURegister {
public:
VIXL_DECLARE_REGISTER_COMMON(ZRegister, ZRegister, CPURegister)
explicit ZRegister(int code, int lane_size_in_bits = kUnknownSize)
: CPURegister(code,
kEncodedUnknownSize,
kVRegisterBank,
EncodeSizeInBits(lane_size_in_bits)) {
VIXL_ASSERT(IsValid());
}
ZRegister(int code, VectorFormat format)
: CPURegister(code,
kEncodedUnknownSize,
kVRegisterBank,
EncodeSizeInBits(LaneSizeInBitsFromFormat(format)),
kNoQualifiers) {
VIXL_ASSERT(IsValid());
}
// Return a Z register with a known lane size (like "z0.B").
ZRegister VnB() const { return ZRegister(GetCode(), kBRegSize); }
ZRegister VnH() const { return ZRegister(GetCode(), kHRegSize); }
ZRegister VnS() const { return ZRegister(GetCode(), kSRegSize); }
ZRegister VnD() const { return ZRegister(GetCode(), kDRegSize); }
ZRegister VnQ() const { return ZRegister(GetCode(), kQRegSize); }
template <typename T>
ZRegister WithLaneSize(T format) const {
return ZRegister(GetCode(), format);
}
ZRegister WithSameLaneSizeAs(const CPURegister& other) const {
VIXL_ASSERT(other.HasLaneSize());
return this->WithLaneSize(other.GetLaneSizeInBits());
}
bool IsValid() const { return IsValidZRegister(); }
};
// Any SVE P register, with or without a qualifier or lane size specifier.
class PRegister : public CPURegister {
public:
VIXL_DECLARE_REGISTER_COMMON(PRegister, PRegister, CPURegister)
explicit PRegister(int code) : CPURegister(code, kUnknownSize, kPRegister) {
VIXL_ASSERT(IsValid());
}
bool IsValid() const {
return IsValidPRegister() && !HasLaneSize() && IsUnqualified();
}
// Return a P register with a known lane size (like "p0.B").
PRegisterWithLaneSize VnB() const;
PRegisterWithLaneSize VnH() const;
PRegisterWithLaneSize VnS() const;
PRegisterWithLaneSize VnD() const;
template <typename T>
PRegisterWithLaneSize WithLaneSize(T format) const;
PRegisterWithLaneSize WithSameLaneSizeAs(const CPURegister& other) const;
// SVE predicates are specified (in normal assembly) with a "/z" (zeroing) or
// "/m" (merging) suffix. These methods are VIXL's equivalents.
PRegisterZ Zeroing() const;
PRegisterM Merging() const;
protected:
// Unchecked constructors, for use by derived classes.
PRegister(int code, EncodedSize encoded_lane_size)
: CPURegister(code,
kEncodedUnknownSize,
kPRegisterBank,
encoded_lane_size,
kNoQualifiers) {}
PRegister(int code, Qualifiers qualifiers)
: CPURegister(code,
kEncodedUnknownSize,
kPRegisterBank,
kEncodedUnknownSize,
qualifiers) {}
};
// Any SVE P register with a known lane size (like "p0.B").
class PRegisterWithLaneSize : public PRegister {
public:
VIXL_DECLARE_REGISTER_COMMON(PRegisterWithLaneSize, PRegister, PRegister)
PRegisterWithLaneSize(int code, int lane_size_in_bits)
: PRegister(code, EncodeSizeInBits(lane_size_in_bits)) {
VIXL_ASSERT(IsValid());
}
PRegisterWithLaneSize(int code, VectorFormat format)
: PRegister(code, EncodeSizeInBits(LaneSizeInBitsFromFormat(format))) {
VIXL_ASSERT(IsValid());
}
bool IsValid() const {
return IsValidPRegister() && HasLaneSize() && IsUnqualified();
}
// Overload lane size accessors so we can assert `HasLaneSize()`. This allows
// tools such as clang-tidy to prove that the result of GetLaneSize* is
// non-zero.
// TODO: Make these return 'int'.
unsigned GetLaneSizeInBits() const {
VIXL_ASSERT(HasLaneSize());
return PRegister::GetLaneSizeInBits();
}
unsigned GetLaneSizeInBytes() const {
VIXL_ASSERT(HasLaneSize());
return PRegister::GetLaneSizeInBytes();
}
};
// Any SVE P register with the zeroing qualifier (like "p0/z").
class PRegisterZ : public PRegister {
public:
VIXL_DECLARE_REGISTER_COMMON(PRegisterZ, PRegister, PRegister)
explicit PRegisterZ(int code) : PRegister(code, kZeroing) {
VIXL_ASSERT(IsValid());
}
bool IsValid() const {
return IsValidPRegister() && !HasLaneSize() && IsZeroing();
}
};
// Any SVE P register with the merging qualifier (like "p0/m").
class PRegisterM : public PRegister {
public:
VIXL_DECLARE_REGISTER_COMMON(PRegisterM, PRegister, PRegister)
explicit PRegisterM(int code) : PRegister(code, kMerging) {
VIXL_ASSERT(IsValid());
}
bool IsValid() const {
return IsValidPRegister() && !HasLaneSize() && IsMerging();
}
};
inline PRegisterWithLaneSize PRegister::VnB() const {
return PRegisterWithLaneSize(GetCode(), kBRegSize);
}
inline PRegisterWithLaneSize PRegister::VnH() const {
return PRegisterWithLaneSize(GetCode(), kHRegSize);
}
inline PRegisterWithLaneSize PRegister::VnS() const {
return PRegisterWithLaneSize(GetCode(), kSRegSize);
}
inline PRegisterWithLaneSize PRegister::VnD() const {
return PRegisterWithLaneSize(GetCode(), kDRegSize);
}
template <typename T>
inline PRegisterWithLaneSize PRegister::WithLaneSize(T format) const {
return PRegisterWithLaneSize(GetCode(), format);
}
inline PRegisterWithLaneSize PRegister::WithSameLaneSizeAs(
const CPURegister& other) const {
VIXL_ASSERT(other.HasLaneSize());
return this->WithLaneSize(other.GetLaneSizeInBits());
}
inline PRegisterZ PRegister::Zeroing() const { return PRegisterZ(GetCode()); }
inline PRegisterM PRegister::Merging() const { return PRegisterM(GetCode()); }
#define VIXL_REGISTER_WITH_SIZE_LIST(V) \
V(WRegister, kWRegSize, Register) \
V(XRegister, kXRegSize, Register) \
V(QRegister, kQRegSize, VRegister) \
V(DRegister, kDRegSize, VRegister) \
V(SRegister, kSRegSize, VRegister) \
V(HRegister, kHRegSize, VRegister) \
V(BRegister, kBRegSize, VRegister)
#define VIXL_DEFINE_REGISTER_WITH_SIZE(NAME, SIZE, PARENT) \
class NAME : public PARENT { \
public: \
VIXL_CONSTEXPR NAME() : PARENT() {} \
explicit NAME(int code) : PARENT(code, SIZE) {} \
\
explicit NAME(PARENT other) : PARENT(other) { \
VIXL_ASSERT(GetSizeInBits() == SIZE); \
} \
\
PARENT As##PARENT() const { return *this; } \
\
VIXL_CONSTEXPR int GetSizeInBits() const { return SIZE; } \
\
bool IsValid() const { \
return PARENT::IsValid() && (PARENT::GetSizeInBits() == SIZE); \
} \
};
VIXL_REGISTER_WITH_SIZE_LIST(VIXL_DEFINE_REGISTER_WITH_SIZE)
// No*Reg is used to provide default values for unused arguments, error cases
// and so on. Note that these (and the default constructors) all compare equal
// (using the Is() method).
const Register NoReg;
const VRegister NoVReg;
const CPURegister NoCPUReg;
const ZRegister NoZReg;
// TODO: Ideally, these would use specialised register types (like XRegister and
// so on). However, doing so throws up template overloading problems elsewhere.
#define VIXL_DEFINE_REGISTERS(N) \
const Register w##N = WRegister(N); \
const Register x##N = XRegister(N); \
const VRegister b##N = BRegister(N); \
const VRegister h##N = HRegister(N); \
const VRegister s##N = SRegister(N); \
const VRegister d##N = DRegister(N); \
const VRegister q##N = QRegister(N); \
const VRegister v##N(N); \
const ZRegister z##N(N);
AARCH64_REGISTER_CODE_LIST(VIXL_DEFINE_REGISTERS)
#undef VIXL_DEFINE_REGISTERS
#define VIXL_DEFINE_P_REGISTERS(N) const PRegister p##N(N);
AARCH64_P_REGISTER_CODE_LIST(VIXL_DEFINE_P_REGISTERS)
#undef VIXL_DEFINE_P_REGISTERS
// VIXL represents 'sp' with a unique code, to tell it apart from 'xzr'.
const Register wsp = WRegister(kSPRegInternalCode);
const Register sp = XRegister(kSPRegInternalCode);
// Standard 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);
// AreEven returns true if all of the specified registers have even register
// indices. Arguments set to NoReg are ignored, as are any subsequent
// arguments. At least one argument (reg1) must be valid (not NoCPUReg).
bool AreEven(const CPURegister& reg1,
const CPURegister& reg2,
const CPURegister& reg3 = NoReg,
const CPURegister& reg4 = NoReg,
const CPURegister& reg5 = NoReg,
const CPURegister& reg6 = NoReg,
const CPURegister& reg7 = NoReg,
const CPURegister& reg8 = NoReg);
// AreConsecutive returns true if all of the specified registers are
// consecutive in the register file. Arguments set to NoReg are ignored, as are
// any subsequent arguments. At least one argument (reg1) must be valid
// (not NoCPUReg).
bool AreConsecutive(const CPURegister& reg1,
const CPURegister& reg2,
const CPURegister& reg3 = NoCPUReg,
const CPURegister& reg4 = NoCPUReg);
// AreSameFormat returns true if all of the specified registers 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 CPURegister& reg1,
const CPURegister& reg2,
const CPURegister& reg3 = NoCPUReg,
const CPURegister& reg4 = NoCPUReg);
// AreSameLaneSize returns true if all of the specified registers have the same
// element lane size, B, H, S or D. It doesn't compare the type of registers.
// Arguments set to NoReg are ignored, as are any subsequent arguments.
// At least one argument (reg1) must be valid (not NoVReg).
// TODO: Remove this, and replace its uses with AreSameFormat.
bool AreSameLaneSize(const CPURegister& reg1,
const CPURegister& reg2,
const CPURegister& reg3 = NoCPUReg,
const CPURegister& reg4 = NoCPUReg);
} // namespace aarch64
} // namespace vixl
#endif // VIXL_AARCH64_REGISTERS_AARCH64_H_
File diff suppressed because it is too large Load Diff
@@ -56,6 +56,8 @@ enum DebugHltOpcode {
kDisableCPUFeaturesOpcode,
kSaveCPUFeaturesOpcode,
kRestoreCPUFeaturesOpcode,
kMTEActive,
kMTEInactive,
// Aliases.
kDebugHltFirstOpcode = kUnreachableOpcode,
kDebugHltLastOpcode = kLogOpcode
@@ -88,7 +90,7 @@ VIXL_DEPRECATED("DebugHltOpcode", typedef DebugHltOpcode DebugHltOpcodes);
// call):
// x0: The format string
// x1-x7: Optional arguments, if type == CPURegister::kRegister
// d0-d7: Optional arguments, if type == CPURegister::kFPRegister
// d0-d7: Optional arguments, if type == CPURegister::kVRegister
const unsigned kPrintfArgCountOffset = 1 * kInstructionSize;
const unsigned kPrintfArgPatternListOffset = 2 * kInstructionSize;
const unsigned kPrintfLength = 3 * kInstructionSize;
@@ -121,7 +123,7 @@ const unsigned kTraceLength = 3 * kInstructionSize;
enum TraceParameters {
LOG_DISASM = 1 << 0, // Log disassembly.
LOG_REGS = 1 << 1, // Log general purpose registers.
LOG_VREGS = 1 << 2, // Log NEON and floating-point registers.
LOG_VREGS = 1 << 2, // Log SVE, NEON and floating-point registers.
LOG_SYSREGS = 1 << 3, // Log the flags and system registers.
LOG_WRITE = 1 << 4, // Log writes to memory.
LOG_BRANCH = 1 << 5, // Log taken branches.
+8 -3
View File
@@ -29,6 +29,12 @@
#include "code-buffer-vixl.h"
// Microsoft Visual C++ defines a `mvn` macro that conflicts with our own
// definition.
#if defined(_MSC_VER) && defined(mvn)
#undef mvn
#endif
namespace vixl {
class CodeBufferCheckScope;
@@ -37,9 +43,8 @@ namespace internal {
class AssemblerBase {
public:
AssemblerBase() : allow_assembler_(false) {}
explicit AssemblerBase(size_t capacity)
: buffer_(capacity), allow_assembler_(false) {}
AssemblerBase()
: allow_assembler_(false) {}
AssemblerBase(byte* buffer, size_t capacity)
: buffer_(buffer, capacity), allow_assembler_(false) {}
+6 -36
View File
@@ -36,24 +36,12 @@ namespace vixl {
class CodeBuffer {
public:
static const size_t kDefaultCapacity = 4 * KBytes;
explicit CodeBuffer(size_t capacity = kDefaultCapacity);
CodeBuffer();
CodeBuffer(byte* buffer, size_t capacity);
~CodeBuffer();
~CodeBuffer() VIXL_NEGATIVE_TESTING_ALLOW_EXCEPTION;
void Reset();
void Reset(byte* buffer, size_t capacity, bool managed = false);
#ifdef VIXL_CODE_BUFFER_MMAP
void SetExecutable();
void SetWritable();
#else
// These require page-aligned memory blocks, which we can only guarantee with
// mmap.
VIXL_NO_RETURN_IN_DEBUG_MODE void SetExecutable() { VIXL_UNIMPLEMENTED(); }
VIXL_NO_RETURN_IN_DEBUG_MODE void SetWritable() { VIXL_UNIMPLEMENTED(); }
#endif
void Reset(byte* buffer, size_t capacity);
ptrdiff_t GetOffsetFrom(ptrdiff_t offset) const {
ptrdiff_t cursor_offset = cursor_ - buffer_;
@@ -128,8 +116,9 @@ class CodeBuffer {
void Emit(T value) {
VIXL_ASSERT(HasSpaceFor(sizeof(value)));
dirty_ = true;
memcpy(cursor_, &value, sizeof(value));
cursor_ += sizeof(value);
byte* c = cursor_;
memcpy(c, &value, sizeof(value));
cursor_ = c + sizeof(value);
}
void UpdateData(size_t offset, const void* data, size_t size);
@@ -149,10 +138,6 @@ class CodeBuffer {
return GetCapacity();
}
bool IsManaged() const { return managed_; }
void Grow(size_t new_capacity);
bool IsDirty() const { return dirty_; }
void SetClean() { dirty_ = false; }
@@ -161,24 +146,9 @@ class CodeBuffer {
return GetRemainingBytes() >= amount;
}
void EnsureSpaceFor(size_t amount, bool* has_grown) {
bool is_full = !HasSpaceFor(amount);
if (is_full) Grow(capacity_ * 2 + amount);
VIXL_ASSERT(has_grown != NULL);
*has_grown = is_full;
}
void EnsureSpaceFor(size_t amount) {
bool dummy;
EnsureSpaceFor(amount, &dummy);
}
private:
// Backing store of the buffer.
byte* buffer_;
// If true the backing store is allocated and deallocated by the buffer. The
// backing store can then grow on demand. If false the backing store is
// provided by the user and cannot be resized internally.
bool managed_;
// Pointer to the next location to be written.
byte* cursor_;
// True if there has been any write since the buffer was created or cleaned.
@@ -68,14 +68,19 @@ class CodeBufferCheckScope {
size_t size,
BufferSpacePolicy check_policy = kReserveBufferSpace,
SizePolicy size_policy = kMaximumSize)
: assembler_(NULL), initialised_(false) {
: CodeBufferCheckScope() {
Open(assembler, size, check_policy, size_policy);
}
// This constructor does not implicitly initialise the scope. Instead, the
// user is required to explicitly call the `Open` function before using the
// scope.
CodeBufferCheckScope() : assembler_(NULL), initialised_(false) {
CodeBufferCheckScope()
: assembler_(NULL),
assert_policy_(kMaximumSize),
limit_(0),
previous_allow_assembler_(false),
initialised_(false) {
// Nothing to do.
}
@@ -90,7 +95,7 @@ class CodeBufferCheckScope {
VIXL_ASSERT(assembler != NULL);
assembler_ = assembler;
if (check_policy == kReserveBufferSpace) {
assembler->GetBuffer()->EnsureSpaceFor(size);
VIXL_ASSERT(assembler->GetBuffer()->HasSpaceFor(size));
}
#ifdef VIXL_DEBUG
limit_ = assembler_->GetSizeOfCodeGenerated() + size;
@@ -152,14 +157,15 @@ class EmissionCheckScope : public CodeBufferCheckScope {
// constructed.
EmissionCheckScope(MacroAssemblerInterface* masm,
size_t size,
SizePolicy size_policy = kMaximumSize) {
SizePolicy size_policy = kMaximumSize)
: EmissionCheckScope() {
Open(masm, size, size_policy);
}
// This constructor does not implicitly initialise the scope. Instead, the
// user is required to explicitly call the `Open` function before using the
// scope.
EmissionCheckScope() {}
EmissionCheckScope() : masm_(nullptr), pool_policy_(kBlockPools) {}
virtual ~EmissionCheckScope() { Close(); }
@@ -250,14 +256,15 @@ class ExactAssemblyScope : public EmissionCheckScope {
// constructed.
ExactAssemblyScope(MacroAssemblerInterface* masm,
size_t size,
SizePolicy size_policy = kExactSize) {
SizePolicy size_policy = kExactSize)
: ExactAssemblyScope() {
Open(masm, size, size_policy);
}
// This constructor does not implicitly initialise the scope. Instead, the
// user is required to explicitly call the `Open` function before using the
// scope.
ExactAssemblyScope() {}
ExactAssemblyScope() : previous_allow_macro_assembler_(false) {}
virtual ~ExactAssemblyScope() { Close(); }
@@ -28,6 +28,8 @@
#ifndef VIXL_COMPILER_INTRINSICS_H
#define VIXL_COMPILER_INTRINSICS_H
#include <limits.h>
#include "globals-vixl.h"
namespace vixl {
@@ -104,16 +106,23 @@ int CountTrailingZerosFallBack(uint64_t value, int width);
// TODO: The implementations could be improved for sizes different from 32bit
// and 64bit: we could mask the values and call the appropriate builtin.
// Return the number of leading bits that match the topmost (sign) bit,
// excluding the topmost bit itself.
template <typename V>
inline int CountLeadingSignBits(V value, int width = (sizeof(V) * 8)) {
VIXL_ASSERT(IsPowerOf2(width) && (width <= 64));
#if COMPILER_HAS_BUILTIN_CLRSB
if (width == 32) {
return __builtin_clrsb(value);
} else if (width == 64) {
return __builtin_clrsbll(value);
}
#endif
VIXL_ASSERT((LLONG_MIN <= value) && (value <= LLONG_MAX));
int ll_width =
sizeof(long long) * kBitsPerByte; // NOLINT(google-runtime-int)
int result = __builtin_clrsbll(value) - (ll_width - width);
// Check that the value fits in the specified width.
VIXL_ASSERT(result >= 0);
return result;
#else
VIXL_ASSERT((INT64_MIN <= value) && (value <= INT64_MAX));
return CountLeadingSignBitsFallBack(value, width);
#endif
}
+201 -57
View File
@@ -27,6 +27,7 @@
#ifndef VIXL_CPU_FEATURES_H
#define VIXL_CPU_FEATURES_H
#include <bitset>
#include <ostream>
#include "globals-vixl.h"
@@ -34,16 +35,65 @@
namespace vixl {
// VIXL aims to handle and detect all architectural features that are likely to
// influence code-generation decisions at EL0 (user-space).
//
// - There may be multiple VIXL feature flags for a given architectural
// extension. This occurs where the extension allow components to be
// implemented independently, or where kernel support is needed, and is likely
// to be fragmented.
//
// For example, Pointer Authentication (kPAuth*) has a separate feature flag
// for access to PACGA, and to indicate that the QARMA algorithm is
// implemented.
//
// - Conversely, some extensions have configuration options that do not affect
// EL0, so these are presented as a single VIXL feature.
//
// For example, the RAS extension (kRAS) has several variants, but the only
// feature relevant to VIXL is the addition of the ESB instruction so we only
// need a single flag.
//
// - VIXL offers separate flags for separate features even if they're
// architecturally linked.
//
// For example, the architecture requires kFPHalf and kNEONHalf to be equal,
// but they have separate hardware ID register fields so VIXL presents them as
// separate features.
//
// - VIXL can detect every feature for which it can generate code.
//
// - VIXL can detect some features for which it cannot generate code.
//
// The CPUFeatures::Feature enum — derived from the macro list below — is
// frequently extended. New features may be added to the list at any point, and
// no assumptions should be made about the numerical values assigned to each
// enum constant. The symbolic names can be considered to be stable.
//
// The debug descriptions are used only for debug output. The 'cpuinfo' strings
// are informative; VIXL does not use /proc/cpuinfo for feature detection.
// clang-format off
#define VIXL_CPU_FEATURE_LIST(V) \
/* If set, the OS traps and emulates MRS accesses to relevant (EL1) ID_* */ \
/* registers, so that the detailed feature registers can be read */ \
/* directly. */ \
\
/* Constant name Debug description Linux 'cpuinfo' string. */ \
V(kIDRegisterEmulation, "ID register emulation", "cpuid") \
\
V(kFP, "FP", "fp") \
V(kNEON, "NEON", "asimd") \
V(kCRC32, "CRC32", "crc32") \
V(kDGH, "DGH", "dgh") \
/* Speculation control features. */ \
V(kCSV2, "CSV2", NULL) \
V(kSCXTNUM, "SCXTNUM", NULL) \
V(kCSV3, "CSV3", NULL) \
V(kSB, "SB", "sb") \
V(kSPECRES, "SPECRES", NULL) \
V(kSSBS, "SSBS", NULL) \
V(kSSBSControl, "SSBS (PSTATE control)", "ssbs") \
/* Cryptographic support instructions. */ \
V(kAES, "AES", "aes") \
V(kSHA1, "SHA1", "sha1") \
@@ -56,34 +106,102 @@ namespace vixl {
V(kLORegions, "LORegions", NULL) \
/* Rounding doubling multiply add/subtract: SQRDMLAH and SQRDMLSH. */ \
V(kRDM, "RDM", "asimdrdm") \
/* Scalable Vector Extension. */ \
V(kSVE, "SVE", "sve") \
V(kSVEF64MM, "SVE F64MM", "svef64mm") \
V(kSVEF32MM, "SVE F32MM", "svef32mm") \
V(kSVEI8MM, "SVE I8MM", "svei8imm") \
V(kSVEBF16, "SVE BFloat16", "svebf16") \
/* SDOT and UDOT support (in NEON). */ \
V(kDotProduct, "DotProduct", "asimddp") \
/* Int8 matrix multiplication (in NEON). */ \
V(kI8MM, "NEON I8MM", "i8mm") \
/* Half-precision (FP16) support for FP and NEON, respectively. */ \
V(kFPHalf, "FPHalf", "fphp") \
V(kNEONHalf, "NEONHalf", "asimdhp") \
/* BFloat16 support (in both FP and NEON.) */ \
V(kBF16, "FP/NEON BFloat 16", "bf16") \
/* The RAS extension, including the ESB instruction. */ \
V(kRAS, "RAS", NULL) \
/* Data cache clean to the point of persistence: DC CVAP. */ \
V(kDCPoP, "DCPoP", "dcpop") \
/* Data cache clean to the point of deep persistence: DC CVADP. */ \
V(kDCCVADP, "DCCVADP", "dcpodp") \
/* Cryptographic support instructions. */ \
V(kSHA3, "SHA3", "sha3") \
V(kSHA512, "SHA512", "sha512") \
V(kSM3, "SM3", "sm3") \
V(kSM4, "SM4", "sm4") \
/* Pointer authentication for addresses. */ \
V(kPAuth, "PAuth", NULL) \
V(kPAuth, "PAuth", "paca") \
/* Pointer authentication for addresses uses QARMA. */ \
V(kPAuthQARMA, "PAuthQARMA", NULL) \
/* Generic authentication (using the PACGA instruction). */ \
V(kPAuthGeneric, "PAuthGeneric", NULL) \
V(kPAuthGeneric, "PAuthGeneric", "pacg") \
/* Generic authentication uses QARMA. */ \
V(kPAuthGenericQARMA, "PAuthGenericQARMA", NULL) \
/* JavaScript-style FP <-> integer conversion instruction: FJCVTZS. */ \
/* JavaScript-style FP -> integer conversion instruction: FJCVTZS. */ \
V(kJSCVT, "JSCVT", "jscvt") \
/* Complex number support for NEON: FCMLA and FCADD. */ \
V(kFcma, "Fcma", "fcma") \
/* RCpc-based model (for weaker release consistency): LDAPR and variants. */ \
V(kRCpc, "RCpc", "lrcpc") \
/* Complex number support for NEON: FCMLA and FCADD. */ \
V(kFcma, "Fcma", "fcma")
V(kRCpcImm, "RCpc (imm)", "ilrcpc") \
/* Flag manipulation instructions: SETF{8,16}, CFINV, RMIF. */ \
V(kFlagM, "FlagM", "flagm") \
/* Unaligned single-copy atomicity. */ \
V(kUSCAT, "USCAT", "uscat") \
/* FP16 fused multiply-add or -subtract long: FMLAL{2}, FMLSL{2}. */ \
V(kFHM, "FHM", "asimdfhm") \
/* Data-independent timing (for selected instructions). */ \
V(kDIT, "DIT", "dit") \
/* Branch target identification. */ \
V(kBTI, "BTI", "bti") \
/* Flag manipulation instructions: {AX,XA}FLAG */ \
V(kAXFlag, "AXFlag", "flagm2") \
/* Random number generation extension, */ \
V(kRNG, "RNG", "rng") \
/* Floating-point round to {32,64}-bit integer. */ \
V(kFrintToFixedSizedInt,"Frint (bounded)", "frint") \
/* Memory Tagging Extension. */ \
V(kMTEInstructions, "MTE (EL0 instructions)", NULL) \
V(kMTE, "MTE", NULL) \
V(kMTE3, "MTE (asymmetric)", "mte3") \
/* PAuth extensions. */ \
V(kPAuthEnhancedPAC, "PAuth EnhancedPAC", NULL) \
V(kPAuthEnhancedPAC2, "PAuth EnhancedPAC2", NULL) \
V(kPAuthFPAC, "PAuth FPAC", NULL) \
V(kPAuthFPACCombined, "PAuth FPACCombined", NULL) \
/* Scalable Vector Extension 2. */ \
V(kSVE2, "SVE2", "sve2") \
V(kSVESM4, "SVE SM4", "svesm4") \
V(kSVESHA3, "SVE SHA3", "svesha3") \
V(kSVEBitPerm, "SVE BitPerm", "svebitperm") \
V(kSVEAES, "SVE AES", "sveaes") \
V(kSVEPmull128, "SVE Pmull128", "svepmull") \
/* Alternate floating-point behavior */ \
V(kAFP, "AFP", "afp") \
/* Enhanced Counter Virtualization */ \
V(kECV, "ECV", "ecv") \
/* Increased precision of Reciprocal Estimate and Square Root Estimate */ \
V(kRPRES, "RPRES", "rpres") \
/* Memory operation instructions, for memcpy, memset */ \
V(kMOPS, "Memory ops", NULL) \
/* Scalable Matrix Extension (SME) */ \
V(kSME, "SME", "sme") \
V(kSMEi16i64, "SME (i16i64)", "smei16i64") \
V(kSMEf64f64, "SME (f64f64)", "smef64f64") \
V(kSMEi8i32, "SME (i8i32)", "smei8i32") \
V(kSMEf16f32, "SME (f16f32)", "smef16f32") \
V(kSMEb16f32, "SME (b16f32)", "smeb16f32") \
V(kSMEf32f32, "SME (f32f32)", "smef32f32") \
V(kSMEfa64, "SME (fa64)", "smefa64") \
/* WFET and WFIT instruction support */ \
V(kWFXT, "WFXT", "wfxt") \
/* Extended BFloat16 instructions */ \
V(kEBF16, "EBF16", "ebf16") \
V(kSVE_EBF16, "EBF16 (SVE)", "sveebf16") \
V(kCSSC, "CSSC", "cssc")
// clang-format on
@@ -176,13 +294,13 @@ class CPUFeatures {
// clang-format on
// By default, construct with no features enabled.
CPUFeatures() : features_(0) {}
CPUFeatures() : features_{} {}
// Construct with some features already enabled.
CPUFeatures(Feature feature0,
Feature feature1 = kNone,
Feature feature2 = kNone,
Feature feature3 = kNone);
template <typename T, typename... U>
CPUFeatures(T first, U... others) : features_{} {
Combine(first, others...);
}
// Construct with all features enabled. This can be used to disable feature
// checking: `Has(...)` returns true regardless of the argument.
@@ -198,51 +316,80 @@ class CPUFeatures {
return CPUFeatures(kFP, kNEON, kCRC32);
}
// Construct a new CPUFeatures object using ID registers. This assumes that
// kIDRegisterEmulation is present.
static CPUFeatures InferFromIDRegisters();
enum QueryIDRegistersOption {
kDontQueryIDRegisters,
kQueryIDRegistersIfAvailable
};
// Construct a new CPUFeatures object based on what the OS reports.
static CPUFeatures InferFromOS();
static CPUFeatures InferFromOS(
QueryIDRegistersOption option = kQueryIDRegistersIfAvailable);
// Combine another CPUFeatures object into this one. Features that already
// exist in this set are left unchanged.
void Combine(const CPUFeatures& other);
// Combine specific features into this set. Features that already exist in
// this set are left unchanged.
void Combine(Feature feature0,
Feature feature1 = kNone,
Feature feature2 = kNone,
Feature feature3 = kNone);
// Combine a specific feature into this set. If it already exists in the set,
// the set is left unchanged.
void Combine(Feature feature);
// Combine multiple features (or feature sets) into this set.
template <typename T, typename... U>
void Combine(T first, U... others) {
Combine(first);
Combine(others...);
}
// Remove features in another CPUFeatures object from this one.
void Remove(const CPUFeatures& other);
// Remove specific features from this set.
void Remove(Feature feature0,
Feature feature1 = kNone,
Feature feature2 = kNone,
Feature feature3 = kNone);
// Remove a specific feature from this set. This has no effect if the feature
// doesn't exist in the set.
void Remove(Feature feature0);
// Chaining helpers for convenient construction.
CPUFeatures With(const CPUFeatures& other) const;
CPUFeatures With(Feature feature0,
Feature feature1 = kNone,
Feature feature2 = kNone,
Feature feature3 = kNone) const;
CPUFeatures Without(const CPUFeatures& other) const;
CPUFeatures Without(Feature feature0,
Feature feature1 = kNone,
Feature feature2 = kNone,
Feature feature3 = kNone) const;
// Remove multiple features (or feature sets) from this set.
template <typename T, typename... U>
void Remove(T first, U... others) {
Remove(first);
Remove(others...);
}
// Query features.
// Note that an empty query (like `Has(kNone)`) always returns true.
// Chaining helpers for convenient construction by combining other CPUFeatures
// or individual Features.
template <typename... T>
CPUFeatures With(T... others) const {
CPUFeatures f(*this);
f.Combine(others...);
return f;
}
template <typename... T>
CPUFeatures Without(T... others) const {
CPUFeatures f(*this);
f.Remove(others...);
return f;
}
// Test whether the `other` feature set is equal to or a subset of this one.
bool Has(const CPUFeatures& other) const;
bool Has(Feature feature0,
Feature feature1 = kNone,
Feature feature2 = kNone,
Feature feature3 = kNone) const;
// Test whether a single feature exists in this set.
// Note that `Has(kNone)` always returns true.
bool Has(Feature feature) const;
// Test whether all of the specified features exist in this set.
template <typename T, typename... U>
bool Has(T first, U... others) const {
return Has(first) && Has(others...);
}
// Return the number of enabled features.
size_t Count() const;
bool HasNoFeatures() const { return Count() == 0; }
// Check for equivalence.
bool operator==(const CPUFeatures& other) const {
@@ -256,9 +403,8 @@ class CPUFeatures {
const_iterator end() const;
private:
// Each bit represents a feature. This field will be replaced as needed if
// features are added.
uint64_t features_;
// Each bit represents a feature. This set will be extended as needed.
std::bitset<kNumberOfFeatures> features_;
friend std::ostream& operator<<(std::ostream& os,
const vixl::CPUFeatures& features);
@@ -281,8 +427,8 @@ class CPUFeaturesConstIterator {
bool operator!=(const CPUFeaturesConstIterator& other) const {
return !(*this == other);
}
CPUFeatures::Feature operator++();
CPUFeatures::Feature operator++(int);
CPUFeaturesConstIterator& operator++();
CPUFeaturesConstIterator operator++(int);
CPUFeatures::Feature operator*() const {
VIXL_ASSERT(IsValid());
@@ -301,8 +447,10 @@ class CPUFeaturesConstIterator {
CPUFeatures::Feature feature_;
bool IsValid() const {
return ((cpu_features_ == NULL) && (feature_ == CPUFeatures::kNone)) ||
cpu_features_->Has(feature_);
if (cpu_features_ == NULL) {
return feature_ == CPUFeatures::kNone;
}
return cpu_features_->Has(feature_);
}
};
@@ -325,21 +473,17 @@ class CPUFeaturesScope {
// Start a CPUFeaturesScope on any object that implements
// `CPUFeatures* GetCPUFeatures()`.
template <typename T>
explicit CPUFeaturesScope(T* cpu_features_wrapper,
CPUFeatures::Feature feature0 = CPUFeatures::kNone,
CPUFeatures::Feature feature1 = CPUFeatures::kNone,
CPUFeatures::Feature feature2 = CPUFeatures::kNone,
CPUFeatures::Feature feature3 = CPUFeatures::kNone)
explicit CPUFeaturesScope(T* cpu_features_wrapper)
: cpu_features_(cpu_features_wrapper->GetCPUFeatures()),
old_features_(*cpu_features_) {
cpu_features_->Combine(feature0, feature1, feature2, feature3);
}
old_features_(*cpu_features_) {}
template <typename T>
CPUFeaturesScope(T* cpu_features_wrapper, const CPUFeatures& other)
// Start a CPUFeaturesScope on any object that implements
// `CPUFeatures* GetCPUFeatures()`, with the specified features enabled.
template <typename T, typename U, typename... V>
CPUFeaturesScope(T* cpu_features_wrapper, U first, V... features)
: cpu_features_(cpu_features_wrapper->GetCPUFeatures()),
old_features_(*cpu_features_) {
cpu_features_->Combine(other);
cpu_features_->Combine(first, features...);
}
~CPUFeaturesScope() { *cpu_features_ = old_features_; }
+39 -13
View File
@@ -27,6 +27,10 @@
#ifndef VIXL_GLOBALS_H
#define VIXL_GLOBALS_H
#if __cplusplus < 201703L
#error VIXL requires C++17
#endif
// Get standard C99 macros for integer types.
#ifndef __STDC_CONSTANT_MACROS
#define __STDC_CONSTANT_MACROS
@@ -66,7 +70,8 @@ typedef uint8_t byte;
const int KBytes = 1024;
const int MBytes = 1024 * KBytes;
const int kBitsPerByte = 8;
const int kBitsPerByteLog2 = 3;
const int kBitsPerByte = 1 << kBitsPerByteLog2;
template <int SizeInBits>
struct Unsigned;
@@ -153,7 +158,7 @@ struct Unsigned<64> {
#endif
// This is not as powerful as template based assertions, but it is simple.
// It assumes that the descriptions are unique. If this starts being a problem,
// we can switch to a different implemention.
// we can switch to a different implementation.
#define VIXL_CONCAT(a, b) a##b
#if __cplusplus >= 201103L
#define VIXL_STATIC_ASSERT_LINE(line_unused, condition, message) \
@@ -185,10 +190,9 @@ inline void USE(const T1&, const T2&, const T3&) {}
template <typename T1, typename T2, typename T3, typename T4>
inline void USE(const T1&, const T2&, const T3&, const T4&) {}
#define VIXL_ALIGNMENT_EXCEPTION() \
do { \
fprintf(stderr, "ALIGNMENT EXCEPTION\t"); \
VIXL_ABORT(); \
#define VIXL_ALIGNMENT_EXCEPTION() \
do { \
VIXL_ABORT_WITH_MSG("ALIGNMENT EXCEPTION\t"); \
} while (0)
// The clang::fallthrough attribute is used along with the Wimplicit-fallthrough
@@ -203,7 +207,7 @@ inline void USE(const T1&, const T2&, const T3&, const T4&) {}
#if __has_warning("-Wimplicit-fallthrough") && __cplusplus >= 201103L
#define VIXL_FALLTHROUGH() [[clang::fallthrough]]
// Fallthrough annotation for GCC >= 7.
#elif __GNUC__ >= 7
#elif defined(__GNUC__) && __GNUC__ >= 7
#define VIXL_FALLTHROUGH() __attribute__((fallthrough))
#else
#define VIXL_FALLTHROUGH() \
@@ -211,6 +215,18 @@ inline void USE(const T1&, const T2&, const T3&, const T4&) {}
} while (0)
#endif
// Evaluate 'init' to an std::optional and return if it's empty. If 'init' is
// not empty then define a variable 'name' with the value inside the
// std::optional.
#define VIXL_DEFINE_OR_RETURN(name, init) \
auto opt##name = init; \
if (!opt##name) return; \
auto name = *opt##name;
#define VIXL_DEFINE_OR_RETURN_FALSE(name, init) \
auto opt##name = init; \
if (!opt##name) return false; \
auto name = *opt##name;
#if __cplusplus >= 201103L
#define VIXL_NO_RETURN [[noreturn]]
#else
@@ -224,17 +240,19 @@ inline void USE(const T1&, const T2&, const T3&, const T4&) {}
#if __cplusplus >= 201103L
#define VIXL_OVERRIDE override
#define VIXL_CONSTEXPR constexpr
#define VIXL_HAS_CONSTEXPR 1
#else
#define VIXL_OVERRIDE
#define VIXL_CONSTEXPR
#endif
// Some functions might only be marked as "noreturn" for the DEBUG build. This
// macro should be used for such cases (for more details see what
// VIXL_UNREACHABLE expands to).
#ifdef VIXL_DEBUG
#define VIXL_DEBUG_NO_RETURN VIXL_NO_RETURN
// With VIXL_NEGATIVE_TESTING on, VIXL_ASSERT and VIXL_CHECK will throw
// exceptions but C++11 marks destructors as noexcept(true) by default.
#if defined(VIXL_NEGATIVE_TESTING) && __cplusplus >= 201103L
#define VIXL_NEGATIVE_TESTING_ALLOW_EXCEPTION noexcept(false)
#else
#define VIXL_DEBUG_NO_RETURN
#define VIXL_NEGATIVE_TESTING_ALLOW_EXCEPTION
#endif
#ifdef VIXL_INCLUDE_SIMULATOR_AARCH64
@@ -269,16 +287,24 @@ inline void USE(const T1&, const T2&, const T3&, const T4&) {}
// Target Architecture/ISA
#ifdef VIXL_INCLUDE_TARGET_A64
#ifndef VIXL_INCLUDE_TARGET_AARCH64
#define VIXL_INCLUDE_TARGET_AARCH64
#endif
#endif
#if defined(VIXL_INCLUDE_TARGET_A32) && defined(VIXL_INCLUDE_TARGET_T32)
#ifndef VIXL_INCLUDE_TARGET_AARCH32
#define VIXL_INCLUDE_TARGET_AARCH32
#endif
#elif defined(VIXL_INCLUDE_TARGET_A32)
#ifndef VIXL_INCLUDE_TARGET_A32_ONLY
#define VIXL_INCLUDE_TARGET_A32_ONLY
#endif
#else
#ifndef VIXL_INCLUDE_TARGET_T32_ONLY
#define VIXL_INCLUDE_TARGET_T32_ONLY
#endif
#endif
#endif // VIXL_GLOBALS_H
+13 -10
View File
@@ -27,9 +27,8 @@
#ifndef VIXL_INVALSET_H_
#define VIXL_INVALSET_H_
#include <cstring>
#include <algorithm>
#include <cstring>
#include <vector>
#include "globals-vixl.h"
@@ -91,7 +90,7 @@ template <TEMPLATE_INVALSET_P_DECL>
class InvalSet {
public:
InvalSet();
~InvalSet();
~InvalSet() VIXL_NEGATIVE_TESTING_ALLOW_EXCEPTION;
static const size_t kNPreallocatedElements = N_PREALLOCATED_ELEMENTS;
static const KeyType kInvalidKey = INVALID_KEY;
@@ -112,7 +111,7 @@ class InvalSet {
size_t size() const;
// Returns true if no elements are stored in the set.
// Note that this does not mean the the backing storage is empty: it can still
// Note that this does not mean the backing storage is empty: it can still
// contain invalid elements.
bool empty() const;
@@ -244,8 +243,13 @@ class InvalSet {
template <class S>
class InvalSetIterator/* : public std::iterator<std::forward_iterator_tag,
typename S::_ElementType> */{
class InvalSetIterator {
using iterator_category = std::forward_iterator_tag;
using value_type = typename S::_ElementType;
using difference_type = std::ptrdiff_t;
using pointer = S*;
using reference = S&;
private:
// Redefine types to mirror the associated set types.
typedef typename S::_ElementType ElementType;
@@ -323,7 +327,8 @@ InvalSet<TEMPLATE_INVALSET_P_DEF>::InvalSet()
template <TEMPLATE_INVALSET_P_DECL>
InvalSet<TEMPLATE_INVALSET_P_DEF>::~InvalSet() {
InvalSet<TEMPLATE_INVALSET_P_DEF>::~InvalSet()
VIXL_NEGATIVE_TESTING_ALLOW_EXCEPTION {
VIXL_ASSERT(monitor_ == 0);
delete vector_;
}
@@ -841,9 +846,7 @@ InvalSetIterator<S>::InvalSetIterator(const InvalSetIterator<S>& other)
#if __cplusplus >= 201103L
template <class S>
InvalSetIterator<S>::InvalSetIterator(InvalSetIterator<S>&& other) noexcept
: using_vector_(false),
index_(0),
inval_set_(NULL) {
: using_vector_(false), index_(0), inval_set_(NULL) {
swap(*this, other);
}
#endif
@@ -35,7 +35,7 @@ class MacroAssemblerInterface {
public:
virtual internal::AssemblerBase* AsAssemblerBase() = 0;
virtual ~MacroAssemblerInterface() {}
virtual ~MacroAssemblerInterface() VIXL_NEGATIVE_TESTING_ALLOW_EXCEPTION {}
virtual bool AllowMacroInstructions() const = 0;
virtual bool ArePoolsBlocked() const = 0;
+9 -9
View File
@@ -27,10 +27,10 @@
#ifndef VIXL_POOL_MANAGER_IMPL_H_
#define VIXL_POOL_MANAGER_IMPL_H_
#include "pool-manager.h"
#include <algorithm>
#include "assembler-base-vixl.h"
#include "pool-manager.h"
namespace vixl {
@@ -264,14 +264,14 @@ bool PoolManager<T>::MustEmit(T pc,
if (checkpoint < temp.min_location_) return true;
}
bool tempNotPlacedYet = true;
bool temp_not_placed_yet = true;
for (int i = static_cast<int>(objects_.size()) - 1; i >= 0; --i) {
const PoolObject<T>& current = objects_[i];
if (tempNotPlacedYet && PoolObjectLessThan(current, temp)) {
if (temp_not_placed_yet && PoolObjectLessThan(current, temp)) {
checkpoint = UpdateCheckpointForObject(checkpoint, &temp);
if (checkpoint < temp.min_location_) return true;
if (CheckFuturePC(pc, checkpoint)) return true;
tempNotPlacedYet = false;
temp_not_placed_yet = false;
}
if (current.label_base_ == label_base) continue;
checkpoint = UpdateCheckpointForObject(checkpoint, &current);
@@ -279,7 +279,7 @@ bool PoolManager<T>::MustEmit(T pc,
if (CheckFuturePC(pc, checkpoint)) return true;
}
// temp is the object with the smallest max_location_.
if (tempNotPlacedYet) {
if (temp_not_placed_yet) {
checkpoint = UpdateCheckpointForObject(checkpoint, &temp);
if (checkpoint < temp.min_location_) return true;
}
@@ -487,7 +487,7 @@ void PoolManager<T>::Release(T pc) {
}
template <typename T>
PoolManager<T>::~PoolManager<T>() {
PoolManager<T>::~PoolManager<T>() VIXL_NEGATIVE_TESTING_ALLOW_EXCEPTION {
#ifdef VIXL_DEBUG
// Check for unbound objects.
for (objects_iter iter = objects_.begin(); iter != objects_.end(); ++iter) {
@@ -497,7 +497,7 @@ PoolManager<T>::~PoolManager<T>() {
}
#endif
// Delete objects the pool manager owns.
for (typename std::vector<LocationBase<T> *>::iterator
for (typename std::vector<LocationBase<T>*>::iterator
iter = delete_on_destruction_.begin(),
end = delete_on_destruction_.end();
iter != end;
@@ -517,6 +517,6 @@ int PoolManager<T>::GetPoolSizeForTest() const {
}
return size;
}
}
} // namespace vixl
#endif // VIXL_POOL_MANAGER_IMPL_H_
+5 -6
View File
@@ -27,11 +27,10 @@
#ifndef VIXL_POOL_MANAGER_H_
#define VIXL_POOL_MANAGER_H_
#include <stdint.h>
#include <cstddef>
#include <limits>
#include <map>
#include <stdint.h>
#include <vector>
#include "globals-vixl.h"
@@ -142,7 +141,7 @@ class LocationBase {
is_bound_(true),
location_(location) {}
virtual ~LocationBase() {}
virtual ~LocationBase() VIXL_NEGATIVE_TESTING_ALLOW_EXCEPTION {}
// The PoolManager should assume ownership of some objects, and delete them
// after they have been placed. This can happen for example for literals that
@@ -369,8 +368,8 @@ class ForwardReference {
// Specify the possible locations where the object could be stored. AArch32's
// PC offset, and T32's PC alignment calculations should be applied by the
// Assembler, not here. The PoolManager deals only with simple locationes.
// Including min_object_adddress_ is necessary to handle AArch32 some
// Assembler, not here. The PoolManager deals only with simple locations.
// Including min_object_address_ is necessary to handle AArch32 some
// instructions which have a minimum offset of 0, but also have the implicit
// PC offset.
// Note that this structure cannot handle sparse ranges, such as A32's ADR,
@@ -397,7 +396,7 @@ class PoolManager {
max_pool_size_(0),
monitor_(0) {}
~PoolManager();
~PoolManager() VIXL_NEGATIVE_TESTING_ALLOW_EXCEPTION;
// Check if we will need to emit the pool at location 'pc', when planning to
// generate a certain number of bytes. This optionally takes a
+191 -30
View File
@@ -30,6 +30,7 @@
#include <cmath>
#include <cstring>
#include <limits>
#include <type_traits>
#include <vector>
#include "compiler-intrinsics-vixl.h"
@@ -67,29 +68,40 @@ namespace vixl {
#endif
template <typename T, size_t n>
size_t ArrayLength(const T (&)[n]) {
constexpr size_t ArrayLength(const T (&)[n]) {
return n;
}
inline uint64_t GetUintMask(unsigned bits) {
VIXL_ASSERT(bits <= 64);
uint64_t base = (bits >= 64) ? 0 : (UINT64_C(1) << bits);
return base - 1;
}
inline uint64_t GetSignMask(unsigned bits) {
VIXL_ASSERT(bits <= 64);
return UINT64_C(1) << (bits - 1);
}
// Check number width.
// TODO: Refactor these using templates.
inline bool IsIntN(unsigned n, uint32_t x) {
VIXL_ASSERT((0 < n) && (n < 32));
uint32_t limit = UINT32_C(1) << (n - 1);
return x < limit;
VIXL_ASSERT((0 < n) && (n <= 32));
return x <= static_cast<uint32_t>(INT32_MAX >> (32 - n));
}
inline bool IsIntN(unsigned n, int32_t x) {
VIXL_ASSERT((0 < n) && (n < 32));
VIXL_ASSERT((0 < n) && (n <= 32));
if (n == 32) return true;
int32_t limit = INT32_C(1) << (n - 1);
return (-limit <= x) && (x < limit);
}
inline bool IsIntN(unsigned n, uint64_t x) {
VIXL_ASSERT((0 < n) && (n < 64));
uint64_t limit = UINT64_C(1) << (n - 1);
return x < limit;
VIXL_ASSERT((0 < n) && (n <= 64));
return x <= static_cast<uint64_t>(INT64_MAX >> (64 - n));
}
inline bool IsIntN(unsigned n, int64_t x) {
VIXL_ASSERT((0 < n) && (n < 64));
VIXL_ASSERT((0 < n) && (n <= 64));
if (n == 64) return true;
int64_t limit = INT64_C(1) << (n - 1);
return (-limit <= x) && (x < limit);
}
@@ -98,7 +110,8 @@ VIXL_DEPRECATED("IsIntN", inline bool is_intn(unsigned n, int64_t x)) {
}
inline bool IsUintN(unsigned n, uint32_t x) {
VIXL_ASSERT((0 < n) && (n < 32));
VIXL_ASSERT((0 < n) && (n <= 32));
if (n >= 32) return true;
return !(x >> n);
}
inline bool IsUintN(unsigned n, int32_t x) {
@@ -107,7 +120,8 @@ inline bool IsUintN(unsigned n, int32_t x) {
return !(static_cast<uint32_t>(x) >> n);
}
inline bool IsUintN(unsigned n, uint64_t x) {
VIXL_ASSERT((0 < n) && (n < 64));
VIXL_ASSERT((0 < n) && (n <= 64));
if (n >= 64) return true;
return !(x >> n);
}
inline bool IsUintN(unsigned n, int64_t x) {
@@ -183,14 +197,14 @@ inline uint64_t ExtractUnsignedBitfield64(int msb, int lsb, uint64_t x) {
}
inline uint32_t ExtractUnsignedBitfield32(int msb, int lsb, uint32_t x) {
inline uint32_t ExtractUnsignedBitfield32(int msb, int lsb, uint64_t x) {
VIXL_ASSERT((static_cast<size_t>(msb) < sizeof(x) * 8) && (lsb >= 0) &&
(msb >= lsb));
return TruncateToUint32(ExtractUnsignedBitfield64(msb, lsb, x));
}
inline int64_t ExtractSignedBitfield64(int msb, int lsb, int64_t x) {
inline int64_t ExtractSignedBitfield64(int msb, int lsb, uint64_t x) {
VIXL_ASSERT((static_cast<size_t>(msb) < sizeof(x) * 8) && (lsb >= 0) &&
(msb >= lsb));
uint64_t temp = ExtractUnsignedBitfield64(msb, lsb, x);
@@ -203,8 +217,7 @@ inline int64_t ExtractSignedBitfield64(int msb, int lsb, int64_t x) {
return result;
}
inline int32_t ExtractSignedBitfield32(int msb, int lsb, int32_t x) {
inline int32_t ExtractSignedBitfield32(int msb, int lsb, uint64_t x) {
VIXL_ASSERT((static_cast<size_t>(msb) < sizeof(x) * 8) && (lsb >= 0) &&
(msb >= lsb));
uint32_t temp = TruncateToUint32(ExtractSignedBitfield64(msb, lsb, x));
@@ -213,7 +226,6 @@ inline int32_t ExtractSignedBitfield32(int msb, int lsb, int32_t x) {
return result;
}
inline uint64_t RotateRight(uint64_t value,
unsigned int rotate,
unsigned int width) {
@@ -271,6 +283,39 @@ VIXL_DEPRECATED("RawbitsToDouble",
return RawbitsToDouble(bits);
}
// Some compilers dislike negating unsigned integers,
// so we provide an equivalent.
template <typename T>
T UnsignedNegate(T value) {
VIXL_STATIC_ASSERT(std::is_unsigned<T>::value);
return ~value + 1;
}
// An absolute operation for signed integers that is defined for results outside
// the representable range. Specifically, Abs(MIN_INT) is MIN_INT.
template <typename T>
T Abs(T val) {
// TODO: this static assertion is for signed integer inputs, as that's the
// only type tested. However, the code should work for all numeric inputs.
// Remove the assertion and this comment when more tests are available.
VIXL_STATIC_ASSERT(std::is_signed<T>::value && std::is_integral<T>::value);
return ((val >= -std::numeric_limits<T>::max()) && (val < 0)) ? -val : val;
}
// Convert unsigned to signed numbers in a well-defined way (using two's
// complement representations).
inline int64_t RawbitsToInt64(uint64_t bits) {
return (bits >= UINT64_C(0x8000000000000000))
? (-static_cast<int64_t>(UnsignedNegate(bits) - 1) - 1)
: static_cast<int64_t>(bits);
}
inline int32_t RawbitsToInt32(uint32_t bits) {
return (bits >= UINT64_C(0x80000000))
? (-static_cast<int32_t>(UnsignedNegate(bits) - 1) - 1)
: static_cast<int32_t>(bits);
}
namespace internal {
// Internal simulation class used solely by the simulator to
@@ -294,7 +339,7 @@ class SimFloat16 : public Float16 {
bool operator>(SimFloat16 rhs) const;
bool operator==(SimFloat16 rhs) const;
bool operator!=(SimFloat16 rhs) const;
// This is necessary for conversions peformed in (macro asm) Fmov.
// This is necessary for conversions performed in (macro asm) Fmov.
bool operator==(double rhs) const;
operator double() const;
};
@@ -365,6 +410,10 @@ VIXL_DEPRECATED("Float16Classify", inline int float16classify(uint16_t value)) {
bool IsZero(Float16 value);
inline bool IsPositiveZero(double value) {
return (value == 0.0) && (copysign(1.0, value) > 0.0);
}
inline bool IsNaN(float value) { return std::isnan(value); }
inline bool IsNaN(double value) { return std::isnan(value); }
@@ -447,7 +496,9 @@ inline float FusedMultiplyAdd(float op1, float op2, float a) {
}
inline uint64_t LowestSetBit(uint64_t value) { return value & static_cast<uint64_t>(-static_cast<int64_t>(value)); }
inline uint64_t LowestSetBit(uint64_t value) {
return value & UnsignedNegate(value);
}
template <typename T>
@@ -484,11 +535,11 @@ T ReverseBits(T value) {
template <typename T>
inline T SignExtend(T val, int bitSize) {
VIXL_ASSERT(bitSize > 0);
T mask = (T(2) << (bitSize - 1)) - T(1);
inline T SignExtend(T val, int size_in_bits) {
VIXL_ASSERT(size_in_bits > 0);
T mask = (T(2) << (size_in_bits - 1)) - T(1);
val &= mask;
T sign_bits = -((val >> (bitSize - 1)) << bitSize);
T sign_bits = -((val >> (size_in_bits - 1)) << size_in_bits);
val |= sign_bits;
return val;
}
@@ -570,7 +621,7 @@ T AlignUp(T pointer,
// reinterpret_cast behaviour for other types.
typename Unsigned<sizeof(T)* kBitsPerByte>::type pointer_raw =
(typename Unsigned<sizeof(T) * kBitsPerByte>::type)pointer;
(typename Unsigned<sizeof(T) * kBitsPerByte>::type) pointer;
VIXL_STATIC_ASSERT(sizeof(pointer) <= sizeof(pointer_raw));
size_t mask = alignment - 1;
@@ -590,7 +641,7 @@ T AlignDown(T pointer,
// reinterpret_cast behaviour for other types.
typename Unsigned<sizeof(T)* kBitsPerByte>::type pointer_raw =
(typename Unsigned<sizeof(T) * kBitsPerByte>::type)pointer;
(typename Unsigned<sizeof(T) * kBitsPerByte>::type) pointer;
VIXL_STATIC_ASSERT(sizeof(pointer) <= sizeof(pointer_raw));
size_t mask = alignment - 1;
@@ -801,7 +852,7 @@ class Uint32 {
}
int32_t GetSigned() const { return data_; }
Uint32 operator~() const { return Uint32(~data_); }
Uint32 operator-() const { return Uint32(static_cast<uint32_t>(-static_cast<int32_t>(data_))); }
Uint32 operator-() const { return Uint32(UnsignedNegate(data_)); }
bool operator==(Uint32 value) const { return data_ == value.data_; }
bool operator!=(Uint32 value) const { return data_ != value.data_; }
bool operator>(Uint32 value) const { return data_ > value.data_; }
@@ -869,7 +920,7 @@ class Uint64 {
Uint32 GetHigh32() const { return Uint32(data_ >> 32); }
Uint32 GetLow32() const { return Uint32(data_ & 0xffffffff); }
Uint64 operator~() const { return Uint64(~data_); }
Uint64 operator-() const { return Uint64(static_cast<uint64_t>(-static_cast<int64_t>(data_))); }
Uint64 operator-() const { return Uint64(UnsignedNegate(data_)); }
bool operator==(Uint64 value) const { return data_ == value.data_; }
bool operator!=(Uint64 value) const { return data_ != value.data_; }
Uint64 operator+(Uint64 value) const { return Uint64(data_ + value.data_); }
@@ -974,6 +1025,42 @@ Uint64::Uint64(Uint128 data) : data_(data.ToUint64().Get()) {}
Int64 BitCount(Uint32 value);
// The algorithm used is adapted from the one described in section 8.2 of
// Hacker's Delight, by Henry S. Warren, Jr.
template <unsigned N, typename T>
int64_t MultiplyHigh(T u, T v) {
uint64_t u0, v0, w0, u1, v1, w1, w2, t;
VIXL_STATIC_ASSERT((N == 8) || (N == 16) || (N == 32) || (N == 64));
uint64_t sign_mask = UINT64_C(1) << (N - 1);
uint64_t sign_ext = 0;
unsigned half_bits = N / 2;
uint64_t half_mask = GetUintMask(half_bits);
if (std::numeric_limits<T>::is_signed) {
sign_ext = UINT64_C(0xffffffffffffffff) << half_bits;
}
VIXL_ASSERT(sizeof(u) == sizeof(uint64_t));
VIXL_ASSERT(sizeof(u) == sizeof(u0));
u0 = u & half_mask;
u1 = u >> half_bits | (((u & sign_mask) != 0) ? sign_ext : 0);
v0 = v & half_mask;
v1 = v >> half_bits | (((v & sign_mask) != 0) ? sign_ext : 0);
w0 = u0 * v0;
t = u1 * v0 + (w0 >> half_bits);
w1 = t & half_mask;
w2 = t >> half_bits | (((t & sign_mask) != 0) ? sign_ext : 0);
w1 = u0 * v1 + w1;
w1 = w1 >> half_bits | (((w1 & sign_mask) != 0) ? sign_ext : 0);
uint64_t value = u1 * v1 + w2 + w1;
int64_t result;
memcpy(&result, &value, sizeof(result));
return result;
}
} // namespace internal
// The default NaN values (for FPCR.DN=1).
@@ -1139,7 +1226,7 @@ T FPRound(int64_t sign,
// For subnormal outputs, the shift must be adjusted by the exponent. The +1
// is necessary because the exponent of a subnormal value (encoded as 0) is
// the same as the exponent of the smallest normal value (encoded as 1).
shift += -exponent + 1;
shift += static_cast<int>(-exponent + 1);
// Handle inputs that would produce a zero output.
//
@@ -1238,9 +1325,8 @@ inline Float16 FPRoundToFloat16(int64_t sign,
uint64_t mantissa,
FPRounding round_mode) {
return RawbitsToFloat16(
FPRound<uint16_t,
kFloat16ExponentBits,
kFloat16MantissaBits>(sign, exponent, mantissa, round_mode));
FPRound<uint16_t, kFloat16ExponentBits, kFloat16MantissaBits>(
sign, exponent, mantissa, round_mode));
}
@@ -1276,6 +1362,81 @@ Float16 FPToFloat16(double value,
FPRounding round_mode,
UseDefaultNaN DN,
bool* exception = NULL);
// Like static_cast<T>(value), but with specialisations for the Float16 type.
template <typename T, typename F>
T StaticCastFPTo(F value) {
return static_cast<T>(value);
}
template <>
inline float StaticCastFPTo<float, Float16>(Float16 value) {
return FPToFloat(value, kIgnoreDefaultNaN);
}
template <>
inline double StaticCastFPTo<double, Float16>(Float16 value) {
return FPToDouble(value, kIgnoreDefaultNaN);
}
template <>
inline Float16 StaticCastFPTo<Float16, float>(float value) {
return FPToFloat16(value, FPTieEven, kIgnoreDefaultNaN);
}
template <>
inline Float16 StaticCastFPTo<Float16, double>(double value) {
return FPToFloat16(value, FPTieEven, kIgnoreDefaultNaN);
}
template <typename T>
uint64_t FPToRawbitsWithSize(unsigned size_in_bits, T value) {
switch (size_in_bits) {
case 16:
return Float16ToRawbits(StaticCastFPTo<Float16>(value));
case 32:
return FloatToRawbits(StaticCastFPTo<float>(value));
case 64:
return DoubleToRawbits(StaticCastFPTo<double>(value));
}
VIXL_UNREACHABLE();
return 0;
}
template <typename T>
T RawbitsWithSizeToFP(unsigned size_in_bits, uint64_t value) {
VIXL_ASSERT(IsUintN(size_in_bits, value));
switch (size_in_bits) {
case 16:
return StaticCastFPTo<T>(RawbitsToFloat16(static_cast<uint16_t>(value)));
case 32:
return StaticCastFPTo<T>(RawbitsToFloat(static_cast<uint32_t>(value)));
case 64:
return StaticCastFPTo<T>(RawbitsToDouble(value));
}
VIXL_UNREACHABLE();
return 0;
}
// Jenkins one-at-a-time hash, based on
// https://en.wikipedia.org/wiki/Jenkins_hash_function citing
// https://www.drdobbs.com/database/algorithm-alley/184410284.
constexpr uint32_t Hash(const char* str, uint32_t hash = 0) {
if (*str == '\0') {
hash += hash << 3;
hash ^= hash >> 11;
hash += hash << 15;
return hash;
} else {
hash += *str;
hash += hash << 10;
hash ^= hash >> 6;
return Hash(str + 1, hash);
}
}
constexpr uint32_t operator"" _h(const char* x, size_t) { return Hash(x); }
} // namespace vixl
#endif // VIXL_UTILS_H