Dep: Update vixl to 662828c
This commit is contained in:
@@ -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:
|
||||
//
|
||||
|
||||
@@ -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
@@ -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_
|
||||
@@ -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
@@ -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 ↦
|
||||
}
|
||||
|
||||
// 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 ↦
|
||||
}
|
||||
|
||||
// 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 ↦
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
||||
@@ -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) {}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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_; }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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, ¤t);
|
||||
@@ -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_
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user