blob: 1f48ed4ed2d168da57583bb4b3215a1267fd7170 [file] [log] [blame]
// Copyright 2013, ARM Limited
// 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_A64_SIMULATOR_A64_H_
#define VIXL_A64_SIMULATOR_A64_H_
#include "globals-vixl.h"
#include "utils-vixl.h"
#include "a64/instructions-a64.h"
#include "a64/assembler-a64.h"
#include "a64/disasm-a64.h"
#include "a64/instrument-a64.h"
namespace vixl {
enum ReverseByteMode {
Reverse16 = 0,
Reverse32 = 1,
Reverse64 = 2
};
// Printf. See debugger-a64.h for more information on pseudo instructions.
// - arg_count: The number of arguments.
// - arg_pattern: A set of PrintfArgPattern values, packed into two-bit fields.
//
// Simulate a call to printf.
//
// Floating-point and integer arguments are passed in separate sets of registers
// in AAPCS64 (even for varargs functions), so it is not possible to determine
// the type of each argument without some information about the values that were
// passed in. This information could be retrieved from the printf format string,
// but the format string is not trivial to parse so we encode the relevant
// information with the HLT instruction.
//
// The interface is as follows:
// x0: The format string
// x1-x7: Optional arguments, if type == CPURegister::kRegister
// d0-d7: Optional arguments, if type == CPURegister::kFPRegister
const Instr kPrintfOpcode = 0xdeb1;
const unsigned kPrintfArgCountOffset = 1 * kInstructionSize;
const unsigned kPrintfArgPatternListOffset = 2 * kInstructionSize;
const unsigned kPrintfLength = 3 * kInstructionSize;
const unsigned kPrintfMaxArgCount = 4;
// The argument pattern is a set of two-bit-fields, each with one of the
// following values:
enum PrintfArgPattern {
kPrintfArgW = 1,
kPrintfArgX = 2,
// There is no kPrintfArgS because floats are always converted to doubles in C
// varargs calls.
kPrintfArgD = 3
};
static const unsigned kPrintfArgPatternBits = 2;
// The proper way to initialize a simulated system register (such as NZCV) is as
// follows:
// SimSystemRegister nzcv = SimSystemRegister::DefaultValueFor(NZCV);
class SimSystemRegister {
public:
// The default constructor represents a register which has no writable bits.
// It is not possible to set its value to anything other than 0.
SimSystemRegister() : value_(0), write_ignore_mask_(0xffffffff) { }
inline uint32_t RawValue() const {
return value_;
}
inline void SetRawValue(uint32_t new_value) {
value_ = (value_ & write_ignore_mask_) | (new_value & ~write_ignore_mask_);
}
inline uint32_t Bits(int msb, int lsb) const {
return unsigned_bitextract_32(msb, lsb, value_);
}
inline int32_t SignedBits(int msb, int lsb) const {
return signed_bitextract_32(msb, lsb, value_);
}
void SetBits(int msb, int lsb, uint32_t bits);
// Default system register values.
static SimSystemRegister DefaultValueFor(SystemRegister id);
#define DEFINE_GETTER(Name, HighBit, LowBit, Func) \
inline uint32_t Name() const { return Func(HighBit, LowBit); } \
inline void Set##Name(uint32_t bits) { SetBits(HighBit, LowBit, bits); }
#define DEFINE_WRITE_IGNORE_MASK(Name, Mask) \
static const uint32_t Name##WriteIgnoreMask = ~static_cast<uint32_t>(Mask);
SYSTEM_REGISTER_FIELDS_LIST(DEFINE_GETTER, DEFINE_WRITE_IGNORE_MASK)
#undef DEFINE_ZERO_BITS
#undef DEFINE_GETTER
protected:
// Most system registers only implement a few of the bits in the word. Other
// bits are "read-as-zero, write-ignored". The write_ignore_mask argument
// describes the bits which are not modifiable.
SimSystemRegister(uint32_t value, uint32_t write_ignore_mask)
: value_(value), write_ignore_mask_(write_ignore_mask) { }
uint32_t value_;
uint32_t write_ignore_mask_;
};
// Represent a register (r0-r31, v0-v31).
template<int kSizeInBytes>
class SimRegisterBase {
public:
// Write the specified value. The value is zero-extended if necessary.
template<typename T>
void Set(T new_value) {
VIXL_STATIC_ASSERT(sizeof(new_value) <= kSizeInBytes);
if (sizeof(new_value) < kSizeInBytes) {
// All AArch64 registers are zero-extending.
memset(value_ + sizeof(new_value), 0, kSizeInBytes - sizeof(new_value));
}
memcpy(value_, &new_value, sizeof(new_value));
}
// Read the value as the specified type. The value is truncated if necessary.
template<typename T>
T Get() const {
T result;
VIXL_STATIC_ASSERT(sizeof(result) <= kSizeInBytes);
memcpy(&result, value_, sizeof(T));
return result;
}
protected:
uint8_t value_[kSizeInBytes];
};
typedef SimRegisterBase<kXRegSizeInBytes> SimRegister; // r0-r31
typedef SimRegisterBase<kDRegSizeInBytes> SimFPRegister; // v0-v31
class SimExclusiveLocalMonitor {
public:
SimExclusiveLocalMonitor() : kSkipClearProbability(8), seed_(0x87654321) {
Clear();
}
// Clear the exclusive monitor (like clrex).
void Clear() {
address_ = 0;
size_ = 0;
}
// Clear the exclusive monitor most of the time.
void MaybeClear() {
if ((seed_ % kSkipClearProbability) != 0) {
Clear();
}
// Advance seed_ using a simple linear congruential generator.
seed_ = (seed_ * 48271) % 2147483647;
}
// Mark the address range for exclusive access (like load-exclusive).
template <typename T>
void MarkExclusive(T address, size_t size) {
VIXL_STATIC_ASSERT(sizeof(address) == sizeof(address_));
address_ = reinterpret_cast<uintptr_t>(address);
size_ = size;
}
// Return true if the address range is marked (like store-exclusive).
// This helper doesn't implicitly clear the monitor.
template <typename T>
bool IsExclusive(T address, size_t size) {
VIXL_STATIC_ASSERT(sizeof(address) == sizeof(address_));
VIXL_ASSERT(size > 0);
// Be pedantic: Require both the address and the size to match.
return (size == size_) &&
(reinterpret_cast<uintptr_t>(address) == address_);
}
private:
uintptr_t address_;
size_t size_;
const int kSkipClearProbability;
uint32_t seed_;
};
// We can't accurate simulate the global monitor since it depends on external
// influences. Instead, this implementation occasionally causes accesses to
// fail, according to kPassProbability.
class SimExclusiveGlobalMonitor {
public:
SimExclusiveGlobalMonitor() : kPassProbability(8), seed_(0x87654321) {}
template <typename T>
bool IsExclusive(T address, size_t size) {
USE(address);
USE(size);
bool pass = (seed_ % kPassProbability) != 0;
// Advance seed_ using a simple linear congruential generator.
seed_ = (seed_ * 48271) % 2147483647;
return pass;
}
private:
const int kPassProbability;
uint32_t seed_;
};
class Simulator : public DecoderVisitor {
public:
explicit Simulator(Decoder* decoder, FILE* stream = stdout);
~Simulator();
void ResetState();
// Run the simulator.
virtual void Run();
void RunFrom(const Instruction* first);
// Simulation helpers.
inline const Instruction* pc() const { return pc_; }
inline void set_pc(const Instruction* new_pc) {
pc_ = AddressUntag(new_pc);
pc_modified_ = true;
}
inline void increment_pc() {
if (!pc_modified_) {
pc_ = pc_->NextInstruction();
}
pc_modified_ = false;
}
inline void ExecuteInstruction() {
// The program counter should always be aligned.
VIXL_ASSERT(IsWordAligned(pc_));
decoder_->Decode(pc_);
increment_pc();
}
// Declare all Visitor functions.
#define DECLARE(A) void Visit##A(const Instruction* instr);
VISITOR_LIST(DECLARE)
#undef DECLARE
// Integer register accessors.
// Basic accessor: Read the register as the specified type.
template<typename T>
inline T reg(unsigned code, Reg31Mode r31mode = Reg31IsZeroRegister) const {
VIXL_STATIC_ASSERT((sizeof(T) == kWRegSizeInBytes) ||
(sizeof(T) == kXRegSizeInBytes));
VIXL_ASSERT(code < kNumberOfRegisters);
if ((code == 31) && (r31mode == Reg31IsZeroRegister)) {
T result;
memset(&result, 0, sizeof(result));
return result;
}
return registers_[code].Get<T>();
}
// Common specialized accessors for the reg() template.
inline int32_t wreg(unsigned code,
Reg31Mode r31mode = Reg31IsZeroRegister) const {
return reg<int32_t>(code, r31mode);
}
inline int64_t xreg(unsigned code,
Reg31Mode r31mode = Reg31IsZeroRegister) const {
return reg<int64_t>(code, r31mode);
}
// As above, with parameterized size and return type. The value is
// either zero-extended or truncated to fit, as required.
template<typename T>
inline T reg(unsigned size, unsigned code,
Reg31Mode r31mode = Reg31IsZeroRegister) const {
uint64_t raw;
switch (size) {
case kWRegSize: raw = reg<uint32_t>(code, r31mode); break;
case kXRegSize: raw = reg<uint64_t>(code, r31mode); break;
default:
VIXL_UNREACHABLE();
return 0;
}
T result;
VIXL_STATIC_ASSERT(sizeof(result) <= sizeof(raw));
// Copy the result and truncate to fit. This assumes a little-endian host.
memcpy(&result, &raw, sizeof(result));
return result;
}
// Use int64_t by default if T is not specified.
inline int64_t reg(unsigned size, unsigned code,
Reg31Mode r31mode = Reg31IsZeroRegister) const {
return reg<int64_t>(size, code, r31mode);
}
// Basic accessor: Write the specified value.
template<typename T>
inline void set_reg(unsigned code, T value,
Reg31Mode r31mode = Reg31IsZeroRegister) {
VIXL_STATIC_ASSERT((sizeof(T) == kWRegSizeInBytes) ||
(sizeof(T) == kXRegSizeInBytes));
VIXL_ASSERT(code < kNumberOfRegisters);
if ((code == 31) && (r31mode == Reg31IsZeroRegister)) {
return;
}
registers_[code].Set(value);
}
// Common specialized accessors for the set_reg() template.
inline void set_wreg(unsigned code, int32_t value,
Reg31Mode r31mode = Reg31IsZeroRegister) {
set_reg(code, value, r31mode);
}
inline void set_xreg(unsigned code, int64_t value,
Reg31Mode r31mode = Reg31IsZeroRegister) {
set_reg(code, value, r31mode);
}
// As above, with parameterized size and type. The value is either
// zero-extended or truncated to fit, as required.
template<typename T>
inline void set_reg(unsigned size, unsigned code, T value,
Reg31Mode r31mode = Reg31IsZeroRegister) {
// Zero-extend the input.
uint64_t raw = 0;
VIXL_STATIC_ASSERT(sizeof(value) <= sizeof(raw));
memcpy(&raw, &value, sizeof(value));
// Write (and possibly truncate) the value.
switch (size) {
case kWRegSize: set_reg<uint32_t>(code, raw, r31mode); break;
case kXRegSize: set_reg<uint64_t>(code, raw, r31mode); break;
default:
VIXL_UNREACHABLE();
return;
}
}
// Common specialized accessors for the set_reg() template.
// Commonly-used special cases.
template<typename T>
inline void set_lr(T value) {
set_reg(kLinkRegCode, value);
}
template<typename T>
inline void set_sp(T value) {
set_reg(31, value, Reg31IsStackPointer);
}
// FP register accessors.
// These are equivalent to the integer register accessors, but for FP
// registers.
// Basic accessor: Read the register as the specified type.
template<typename T>
inline T fpreg(unsigned code) const {
VIXL_STATIC_ASSERT((sizeof(T) == kSRegSizeInBytes) ||
(sizeof(T) == kDRegSizeInBytes));
VIXL_ASSERT(code < kNumberOfFPRegisters);
return fpregisters_[code].Get<T>();
}
// Common specialized accessors for the fpreg() template.
inline float sreg(unsigned code) const {
return fpreg<float>(code);
}
inline uint32_t sreg_bits(unsigned code) const {
return fpreg<uint32_t>(code);
}
inline double dreg(unsigned code) const {
return fpreg<double>(code);
}
inline uint64_t dreg_bits(unsigned code) const {
return fpreg<uint64_t>(code);
}
// As above, with parameterized size and return type. The value is
// either zero-extended or truncated to fit, as required.
template<typename T>
inline T fpreg(unsigned size, unsigned code) const {
uint64_t raw;
switch (size) {
case kSRegSize: raw = fpreg<uint32_t>(code); break;
case kDRegSize: raw = fpreg<uint64_t>(code); break;
default:
VIXL_UNREACHABLE();
raw = 0;
break;
}
T result;
VIXL_STATIC_ASSERT(sizeof(result) <= sizeof(raw));
// Copy the result and truncate to fit. This assumes a little-endian host.
memcpy(&result, &raw, sizeof(result));
return result;
}
// Basic accessor: Write the specified value.
template<typename T>
inline void set_fpreg(unsigned code, T value) {
VIXL_STATIC_ASSERT((sizeof(value) == kSRegSizeInBytes) ||
(sizeof(value) == kDRegSizeInBytes));
VIXL_ASSERT(code < kNumberOfFPRegisters);
fpregisters_[code].Set(value);
}
// Common specialized accessors for the set_fpreg() template.
inline void set_sreg(unsigned code, float value) {
set_fpreg(code, value);
}
inline void set_sreg_bits(unsigned code, uint32_t value) {
set_fpreg(code, value);
}
inline void set_dreg(unsigned code, double value) {
set_fpreg(code, value);
}
inline void set_dreg_bits(unsigned code, uint64_t value) {
set_fpreg(code, value);
}
bool N() { return nzcv_.N() != 0; }
bool Z() { return nzcv_.Z() != 0; }
bool C() { return nzcv_.C() != 0; }
bool V() { return nzcv_.V() != 0; }
SimSystemRegister& nzcv() { return nzcv_; }
// TODO(jbramley): Find a way to make the fpcr_ members return the proper
// types, so these accessors are not necessary.
FPRounding RMode() { return static_cast<FPRounding>(fpcr_.RMode()); }
bool DN() { return fpcr_.DN() != 0; }
SimSystemRegister& fpcr() { return fpcr_; }
// Debug helpers
void PrintSystemRegisters(bool print_all = false);
void PrintRegisters(bool print_all_regs = false);
void PrintFPRegisters(bool print_all_regs = false);
void PrintProcessorState();
static const char* WRegNameForCode(unsigned code,
Reg31Mode mode = Reg31IsZeroRegister);
static const char* XRegNameForCode(unsigned code,
Reg31Mode mode = Reg31IsZeroRegister);
static const char* SRegNameForCode(unsigned code);
static const char* DRegNameForCode(unsigned code);
static const char* VRegNameForCode(unsigned code);
inline bool coloured_trace() { return coloured_trace_; }
void set_coloured_trace(bool value);
inline bool disasm_trace() { return disasm_trace_; }
inline void set_disasm_trace(bool value) {
if (value != disasm_trace_) {
if (value) {
decoder_->InsertVisitorBefore(print_disasm_, this);
} else {
decoder_->RemoveVisitor(print_disasm_);
}
disasm_trace_ = value;
}
}
inline void set_instruction_stats(bool value) {
if (value != instruction_stats_) {
if (value) {
decoder_->AppendVisitor(instrumentation_);
} else {
decoder_->RemoveVisitor(instrumentation_);
}
instruction_stats_ = value;
}
}
// Clear the simulated local monitor to force the next store-exclusive
// instruction to fail.
inline void ClearLocalMonitor() {
local_monitor_.Clear();
}
inline void SilenceExclusiveAccessWarning() {
print_exclusive_access_warning_ = false;
}
protected:
const char* clr_normal;
const char* clr_flag_name;
const char* clr_flag_value;
const char* clr_reg_name;
const char* clr_reg_value;
const char* clr_fpreg_name;
const char* clr_fpreg_value;
const char* clr_memory_value;
const char* clr_memory_address;
const char* clr_debug_number;
const char* clr_debug_message;
const char* clr_warning;
const char* clr_warning_message;
const char* clr_printf;
// Simulation helpers ------------------------------------
bool ConditionPassed(Condition cond) {
switch (cond) {
case eq:
return Z();
case ne:
return !Z();
case hs:
return C();
case lo:
return !C();
case mi:
return N();
case pl:
return !N();
case vs:
return V();
case vc:
return !V();
case hi:
return C() && !Z();
case ls:
return !(C() && !Z());
case ge:
return N() == V();
case lt:
return N() != V();
case gt:
return !Z() && (N() == V());
case le:
return !(!Z() && (N() == V()));
case nv: // Fall through.
case al:
return true;
default:
VIXL_UNREACHABLE();
return false;
}
}
bool ConditionPassed(Instr cond) {
return ConditionPassed(static_cast<Condition>(cond));
}
bool ConditionFailed(Condition cond) {
return !ConditionPassed(cond);
}
void AddSubHelper(const Instruction* instr, int64_t op2);
int64_t AddWithCarry(unsigned reg_size,
bool set_flags,
int64_t src1,
int64_t src2,
int64_t carry_in = 0);
void LogicalHelper(const Instruction* instr, int64_t op2);
void ConditionalCompareHelper(const Instruction* instr, int64_t op2);
void LoadStoreHelper(const Instruction* instr,
int64_t offset,
AddrMode addrmode);
void LoadStorePairHelper(const Instruction* instr, AddrMode addrmode);
uint8_t* AddressModeHelper(unsigned addr_reg,
int64_t offset,
AddrMode addrmode);
template <typename T>
T AddressUntag(T address) {
uint64_t bits = reinterpret_cast<uint64_t>(address);
return reinterpret_cast<T>(bits & ~kAddressTagMask);
}
template <typename T, typename A>
T MemoryRead(A address) {
T value;
address = AddressUntag(address);
VIXL_ASSERT((sizeof(value) == 1) || (sizeof(value) == 2) ||
(sizeof(value) == 4) || (sizeof(value) == 8));
memcpy(&value, reinterpret_cast<const char *>(address), sizeof(value));
return value;
}
template <typename T, typename A>
void MemoryWrite(A address, T value) {
address = AddressUntag(address);
VIXL_ASSERT((sizeof(value) == 1) || (sizeof(value) == 2) ||
(sizeof(value) == 4) || (sizeof(value) == 8));
memcpy(reinterpret_cast<char *>(address), &value, sizeof(value));
}
int64_t ShiftOperand(unsigned reg_size,
int64_t value,
Shift shift_type,
unsigned amount);
int64_t Rotate(unsigned reg_width,
int64_t value,
Shift shift_type,
unsigned amount);
int64_t ExtendValue(unsigned reg_width,
int64_t value,
Extend extend_type,
unsigned left_shift = 0);
uint64_t ReverseBits(uint64_t value, unsigned num_bits);
uint64_t ReverseBytes(uint64_t value, ReverseByteMode mode);
template <typename T>
T FPDefaultNaN() const;
void FPCompare(double val0, double val1);
double FPRoundInt(double value, FPRounding round_mode);
double FPToDouble(float value);
float FPToFloat(double value, FPRounding round_mode);
double FixedToDouble(int64_t src, int fbits, FPRounding round_mode);
double UFixedToDouble(uint64_t src, int fbits, FPRounding round_mode);
float FixedToFloat(int64_t src, int fbits, FPRounding round_mode);
float UFixedToFloat(uint64_t src, int fbits, FPRounding round_mode);
int32_t FPToInt32(double value, FPRounding rmode);
int64_t FPToInt64(double value, FPRounding rmode);
uint32_t FPToUInt32(double value, FPRounding rmode);
uint64_t FPToUInt64(double value, FPRounding rmode);
template <typename T>
T FPAdd(T op1, T op2);
template <typename T>
T FPDiv(T op1, T op2);
template <typename T>
T FPMax(T a, T b);
template <typename T>
T FPMaxNM(T a, T b);
template <typename T>
T FPMin(T a, T b);
template <typename T>
T FPMinNM(T a, T b);
template <typename T>
T FPMul(T op1, T op2);
template <typename T>
T FPMulAdd(T a, T op1, T op2);
template <typename T>
T FPSqrt(T op);
template <typename T>
T FPSub(T op1, T op2);
// This doesn't do anything at the moment. We'll need it if we want support
// for cumulative exception bits or floating-point exceptions.
void FPProcessException() { }
// Standard NaN processing.
template <typename T>
T FPProcessNaN(T op);
bool FPProcessNaNs(const Instruction* instr);
template <typename T>
T FPProcessNaNs(T op1, T op2);
template <typename T>
T FPProcessNaNs3(T op1, T op2, T op3);
// Pseudo Printf instruction
void DoPrintf(const Instruction* instr);
// Processor state ---------------------------------------
// Simulated monitors for exclusive access instructions.
SimExclusiveLocalMonitor local_monitor_;
SimExclusiveGlobalMonitor global_monitor_;
// Output stream.
FILE* stream_;
PrintDisassembler* print_disasm_;
// Instruction statistics instrumentation.
Instrument* instrumentation_;
// General purpose registers. Register 31 is the stack pointer.
SimRegister registers_[kNumberOfRegisters];
// Floating point registers
SimFPRegister fpregisters_[kNumberOfFPRegisters];
// Program Status Register.
// bits[31, 27]: Condition flags N, Z, C, and V.
// (Negative, Zero, Carry, Overflow)
SimSystemRegister nzcv_;
// Floating-Point Control Register
SimSystemRegister fpcr_;
// Only a subset of FPCR features are supported by the simulator. This helper
// checks that the FPCR settings are supported.
//
// This is checked when floating-point instructions are executed, not when
// FPCR is set. This allows generated code to modify FPCR for external
// functions, or to save and restore it when entering and leaving generated
// code.
void AssertSupportedFPCR() {
VIXL_ASSERT(fpcr().FZ() == 0); // No flush-to-zero support.
VIXL_ASSERT(fpcr().RMode() == FPTieEven); // Ties-to-even rounding only.
// The simulator does not support half-precision operations so fpcr().AHP()
// is irrelevant, and is not checked here.
}
static inline int CalcNFlag(uint64_t result, unsigned reg_size) {
return (result >> (reg_size - 1)) & 1;
}
static inline int CalcZFlag(uint64_t result) {
return result == 0;
}
static const uint32_t kConditionFlagsMask = 0xf0000000;
// Stack
byte* stack_;
static const int stack_protection_size_ = 256;
// 2 KB stack.
static const int stack_size_ = 2 * 1024 + 2 * stack_protection_size_;
byte* stack_limit_;
Decoder* decoder_;
// Indicates if the pc has been modified by the instruction and should not be
// automatically incremented.
bool pc_modified_;
const Instruction* pc_;
static const char* xreg_names[];
static const char* wreg_names[];
static const char* sreg_names[];
static const char* dreg_names[];
static const char* vreg_names[];
static const Instruction* kEndOfSimAddress;
private:
bool coloured_trace_;
// Indicates whether the disassembly trace is active.
bool disasm_trace_;
// Indicates whether the instruction instrumentation is active.
bool instruction_stats_;
// Indicates whether the exclusive-access warning has been printed.
bool print_exclusive_access_warning_;
void PrintExclusiveAccessWarning();
};
} // namespace vixl
#endif // VIXL_A64_SIMULATOR_A64_H_