1 // Copyright 2013, ARM Limited 2 // All rights reserved. 3 // 4 // Redistribution and use in source and binary forms, with or without 5 // modification, are permitted provided that the following conditions are met: 6 // 7 // * Redistributions of source code must retain the above copyright notice, 8 // this list of conditions and the following disclaimer. 9 // * Redistributions in binary form must reproduce the above copyright notice, 10 // this list of conditions and the following disclaimer in the documentation 11 // and/or other materials provided with the distribution. 12 // * Neither the name of ARM Limited nor the names of its contributors may be 13 // used to endorse or promote products derived from this software without 14 // specific prior written permission. 15 // 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND 17 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 20 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 27 #ifndef VIXL_A64_SIMULATOR_A64_H_ 28 #define VIXL_A64_SIMULATOR_A64_H_ 29 30 #include "globals-vixl.h" 31 #include "utils-vixl.h" 32 #include "a64/instructions-a64.h" 33 #include "a64/assembler-a64.h" 34 #include "a64/disasm-a64.h" 35 #include "a64/instrument-a64.h" 36 37 namespace vixl { 38 39 enum ReverseByteMode { 40 Reverse16 = 0, 41 Reverse32 = 1, 42 Reverse64 = 2 43 }; 44 45 // Printf. See debugger-a64.h for more information on pseudo instructions. 46 // - arg_count: The number of arguments. 47 // - arg_pattern: A set of PrintfArgPattern values, packed into two-bit fields. 48 // 49 // Simulate a call to printf. 50 // 51 // Floating-point and integer arguments are passed in separate sets of registers 52 // in AAPCS64 (even for varargs functions), so it is not possible to determine 53 // the type of each argument without some information about the values that were 54 // passed in. This information could be retrieved from the printf format string, 55 // but the format string is not trivial to parse so we encode the relevant 56 // information with the HLT instruction. 57 // 58 // The interface is as follows: 59 // x0: The format string 60 // x1-x7: Optional arguments, if type == CPURegister::kRegister 61 // d0-d7: Optional arguments, if type == CPURegister::kFPRegister 62 const Instr kPrintfOpcode = 0xdeb1; 63 const unsigned kPrintfArgCountOffset = 1 * kInstructionSize; 64 const unsigned kPrintfArgPatternListOffset = 2 * kInstructionSize; 65 const unsigned kPrintfLength = 3 * kInstructionSize; 66 67 const unsigned kPrintfMaxArgCount = 4; 68 69 // The argument pattern is a set of two-bit-fields, each with one of the 70 // following values: 71 enum PrintfArgPattern { 72 kPrintfArgW = 1, 73 kPrintfArgX = 2, 74 // There is no kPrintfArgS because floats are always converted to doubles in C 75 // varargs calls. 76 kPrintfArgD = 3 77 }; 78 static const unsigned kPrintfArgPatternBits = 2; 79 80 81 // The proper way to initialize a simulated system register (such as NZCV) is as 82 // follows: 83 // SimSystemRegister nzcv = SimSystemRegister::DefaultValueFor(NZCV); 84 class SimSystemRegister { 85 public: 86 // The default constructor represents a register which has no writable bits. 87 // It is not possible to set its value to anything other than 0. 88 SimSystemRegister() : value_(0), write_ignore_mask_(0xffffffff) { } 89 90 inline uint32_t RawValue() const { 91 return value_; 92 } 93 94 inline void SetRawValue(uint32_t new_value) { 95 value_ = (value_ & write_ignore_mask_) | (new_value & ~write_ignore_mask_); 96 } 97 98 inline uint32_t Bits(int msb, int lsb) const { 99 return unsigned_bitextract_32(msb, lsb, value_); 100 } 101 102 inline int32_t SignedBits(int msb, int lsb) const { 103 return signed_bitextract_32(msb, lsb, value_); 104 } 105 106 void SetBits(int msb, int lsb, uint32_t bits); 107 108 // Default system register values. 109 static SimSystemRegister DefaultValueFor(SystemRegister id); 110 111 #define DEFINE_GETTER(Name, HighBit, LowBit, Func) \ 112 inline uint32_t Name() const { return Func(HighBit, LowBit); } \ 113 inline void Set##Name(uint32_t bits) { SetBits(HighBit, LowBit, bits); } 114 #define DEFINE_WRITE_IGNORE_MASK(Name, Mask) \ 115 static const uint32_t Name##WriteIgnoreMask = ~static_cast<uint32_t>(Mask); 116 117 SYSTEM_REGISTER_FIELDS_LIST(DEFINE_GETTER, DEFINE_WRITE_IGNORE_MASK) 118 119 #undef DEFINE_ZERO_BITS 120 #undef DEFINE_GETTER 121 122 protected: 123 // Most system registers only implement a few of the bits in the word. Other 124 // bits are "read-as-zero, write-ignored". The write_ignore_mask argument 125 // describes the bits which are not modifiable. 126 SimSystemRegister(uint32_t value, uint32_t write_ignore_mask) 127 : value_(value), write_ignore_mask_(write_ignore_mask) { } 128 129 uint32_t value_; 130 uint32_t write_ignore_mask_; 131 }; 132 133 134 // Represent a register (r0-r31, v0-v31). 135 template<int kSizeInBytes> 136 class SimRegisterBase { 137 public: 138 template<typename T> 139 void Set(T new_value, unsigned size = sizeof(T)) { 140 VIXL_ASSERT(size <= kSizeInBytes); 141 VIXL_ASSERT(size <= sizeof(new_value)); 142 // All AArch64 registers are zero-extending; Writing a W register clears the 143 // top bits of the corresponding X register. 144 memset(value_, 0, kSizeInBytes); 145 memcpy(value_, &new_value, size); 146 } 147 148 // Copy 'size' bytes of the register to the result, and zero-extend to fill 149 // the result. 150 template<typename T> 151 T Get(unsigned size = sizeof(T)) const { 152 VIXL_ASSERT(size <= kSizeInBytes); 153 T result; 154 memset(&result, 0, sizeof(result)); 155 memcpy(&result, value_, size); 156 return result; 157 } 158 159 protected: 160 uint8_t value_[kSizeInBytes]; 161 }; 162 typedef SimRegisterBase<kXRegSizeInBytes> SimRegister; // r0-r31 163 typedef SimRegisterBase<kDRegSizeInBytes> SimFPRegister; // v0-v31 164 165 166 class Simulator : public DecoderVisitor { 167 public: 168 explicit Simulator(Decoder* decoder, FILE* stream = stdout); 169 ~Simulator(); 170 171 void ResetState(); 172 173 // Run the simulator. 174 virtual void Run(); 175 void RunFrom(Instruction* first); 176 177 // Simulation helpers. 178 inline Instruction* pc() { return pc_; } 179 inline void set_pc(Instruction* new_pc) { 180 pc_ = new_pc; 181 pc_modified_ = true; 182 } 183 184 inline void increment_pc() { 185 if (!pc_modified_) { 186 pc_ = pc_->NextInstruction(); 187 } 188 189 pc_modified_ = false; 190 } 191 192 inline void ExecuteInstruction() { 193 // The program counter should always be aligned. 194 VIXL_ASSERT(IsWordAligned(pc_)); 195 decoder_->Decode(pc_); 196 increment_pc(); 197 } 198 199 // Declare all Visitor functions. 200 #define DECLARE(A) void Visit##A(Instruction* instr); 201 VISITOR_LIST(DECLARE) 202 #undef DECLARE 203 204 // Register accessors. 205 206 // Return 'size' bits of the value of an integer register, as the specified 207 // type. The value is zero-extended to fill the result. 208 // 209 // The only supported values of 'size' are kXRegSize and kWRegSize. 210 template<typename T> 211 inline T reg(unsigned size, unsigned code, 212 Reg31Mode r31mode = Reg31IsZeroRegister) const { 213 unsigned size_in_bytes = size / 8; 214 VIXL_ASSERT(size_in_bytes <= sizeof(T)); 215 VIXL_ASSERT((size == kXRegSize) || (size == kWRegSize)); 216 VIXL_ASSERT(code < kNumberOfRegisters); 217 218 if ((code == 31) && (r31mode == Reg31IsZeroRegister)) { 219 T result; 220 memset(&result, 0, sizeof(result)); 221 return result; 222 } 223 return registers_[code].Get<T>(size_in_bytes); 224 } 225 226 // Like reg(), but infer the access size from the template type. 227 template<typename T> 228 inline T reg(unsigned code, Reg31Mode r31mode = Reg31IsZeroRegister) const { 229 return reg<T>(sizeof(T) * 8, code, r31mode); 230 } 231 232 // Common specialized accessors for the reg() template. 233 inline int32_t wreg(unsigned code, 234 Reg31Mode r31mode = Reg31IsZeroRegister) const { 235 return reg<int32_t>(code, r31mode); 236 } 237 238 inline int64_t xreg(unsigned code, 239 Reg31Mode r31mode = Reg31IsZeroRegister) const { 240 return reg<int64_t>(code, r31mode); 241 } 242 243 inline int64_t reg(unsigned size, unsigned code, 244 Reg31Mode r31mode = Reg31IsZeroRegister) const { 245 return reg<int64_t>(size, code, r31mode); 246 } 247 248 // Write 'size' bits of 'value' into an integer register. The value is 249 // zero-extended. This behaviour matches AArch64 register writes. 250 // 251 // The only supported values of 'size' are kXRegSize and kWRegSize. 252 template<typename T> 253 inline void set_reg(unsigned size, unsigned code, T value, 254 Reg31Mode r31mode = Reg31IsZeroRegister) { 255 unsigned size_in_bytes = size / 8; 256 VIXL_ASSERT(size_in_bytes <= sizeof(T)); 257 VIXL_ASSERT((size == kXRegSize) || (size == kWRegSize)); 258 VIXL_ASSERT(code < kNumberOfRegisters); 259 260 if ((code == 31) && (r31mode == Reg31IsZeroRegister)) { 261 return; 262 } 263 return registers_[code].Set(value, size_in_bytes); 264 } 265 266 // Like set_reg(), but infer the access size from the template type. 267 template<typename T> 268 inline void set_reg(unsigned code, T value, 269 Reg31Mode r31mode = Reg31IsZeroRegister) { 270 set_reg(sizeof(value) * 8, code, value, r31mode); 271 } 272 273 // Common specialized accessors for the set_reg() template. 274 inline void set_wreg(unsigned code, int32_t value, 275 Reg31Mode r31mode = Reg31IsZeroRegister) { 276 set_reg(kWRegSize, code, value, r31mode); 277 } 278 279 inline void set_xreg(unsigned code, int64_t value, 280 Reg31Mode r31mode = Reg31IsZeroRegister) { 281 set_reg(kXRegSize, code, value, r31mode); 282 } 283 284 // Commonly-used special cases. 285 template<typename T> 286 inline void set_lr(T value) { 287 set_reg(kLinkRegCode, value); 288 } 289 290 template<typename T> 291 inline void set_sp(T value) { 292 set_reg(31, value, Reg31IsStackPointer); 293 } 294 295 // Return 'size' bits of the value of a floating-point register, as the 296 // specified type. The value is zero-extended to fill the result. 297 // 298 // The only supported values of 'size' are kDRegSize and kSRegSize. 299 template<typename T> 300 inline T fpreg(unsigned size, unsigned code) const { 301 unsigned size_in_bytes = size / 8; 302 VIXL_ASSERT(size_in_bytes <= sizeof(T)); 303 VIXL_ASSERT((size == kDRegSize) || (size == kSRegSize)); 304 VIXL_ASSERT(code < kNumberOfFPRegisters); 305 return fpregisters_[code].Get<T>(size_in_bytes); 306 } 307 308 // Like fpreg(), but infer the access size from the template type. 309 template<typename T> 310 inline T fpreg(unsigned code) const { 311 return fpreg<T>(sizeof(T) * 8, code); 312 } 313 314 // Common specialized accessors for the fpreg() template. 315 inline float sreg(unsigned code) const { 316 return fpreg<float>(code); 317 } 318 319 inline uint32_t sreg_bits(unsigned code) const { 320 return fpreg<uint32_t>(code); 321 } 322 323 inline double dreg(unsigned code) const { 324 return fpreg<double>(code); 325 } 326 327 inline uint64_t dreg_bits(unsigned code) const { 328 return fpreg<uint64_t>(code); 329 } 330 331 inline double fpreg(unsigned size, unsigned code) const { 332 switch (size) { 333 case kSRegSize: return sreg(code); 334 case kDRegSize: return dreg(code); 335 default: 336 VIXL_UNREACHABLE(); 337 return 0.0; 338 } 339 } 340 341 // Write 'value' into a floating-point register. The value is zero-extended. 342 // This behaviour matches AArch64 register writes. 343 template<typename T> 344 inline void set_fpreg(unsigned code, T value) { 345 VIXL_ASSERT((sizeof(value) == kDRegSizeInBytes) || 346 (sizeof(value) == kSRegSizeInBytes)); 347 VIXL_ASSERT(code < kNumberOfFPRegisters); 348 fpregisters_[code].Set(value, sizeof(value)); 349 } 350 351 // Common specialized accessors for the set_fpreg() template. 352 inline void set_sreg(unsigned code, float value) { 353 set_fpreg(code, value); 354 } 355 356 inline void set_sreg_bits(unsigned code, uint32_t value) { 357 set_fpreg(code, value); 358 } 359 360 inline void set_dreg(unsigned code, double value) { 361 set_fpreg(code, value); 362 } 363 364 inline void set_dreg_bits(unsigned code, uint64_t value) { 365 set_fpreg(code, value); 366 } 367 368 bool N() { return nzcv_.N() != 0; } 369 bool Z() { return nzcv_.Z() != 0; } 370 bool C() { return nzcv_.C() != 0; } 371 bool V() { return nzcv_.V() != 0; } 372 SimSystemRegister& nzcv() { return nzcv_; } 373 374 // TODO(jbramley): Find a way to make the fpcr_ members return the proper 375 // types, so these accessors are not necessary. 376 FPRounding RMode() { return static_cast<FPRounding>(fpcr_.RMode()); } 377 bool DN() { return fpcr_.DN() != 0; } 378 SimSystemRegister& fpcr() { return fpcr_; } 379 380 // Debug helpers 381 void PrintSystemRegisters(bool print_all = false); 382 void PrintRegisters(bool print_all_regs = false); 383 void PrintFPRegisters(bool print_all_regs = false); 384 void PrintProcessorState(); 385 386 static const char* WRegNameForCode(unsigned code, 387 Reg31Mode mode = Reg31IsZeroRegister); 388 static const char* XRegNameForCode(unsigned code, 389 Reg31Mode mode = Reg31IsZeroRegister); 390 static const char* SRegNameForCode(unsigned code); 391 static const char* DRegNameForCode(unsigned code); 392 static const char* VRegNameForCode(unsigned code); 393 394 inline bool coloured_trace() { return coloured_trace_; } 395 void set_coloured_trace(bool value); 396 397 inline bool disasm_trace() { return disasm_trace_; } 398 inline void set_disasm_trace(bool value) { 399 if (value != disasm_trace_) { 400 if (value) { 401 decoder_->InsertVisitorBefore(print_disasm_, this); 402 } else { 403 decoder_->RemoveVisitor(print_disasm_); 404 } 405 disasm_trace_ = value; 406 } 407 } 408 inline void set_instruction_stats(bool value) { 409 if (value != instruction_stats_) { 410 if (value) { 411 decoder_->AppendVisitor(instrumentation_); 412 } else { 413 decoder_->RemoveVisitor(instrumentation_); 414 } 415 instruction_stats_ = value; 416 } 417 } 418 419 protected: 420 const char* clr_normal; 421 const char* clr_flag_name; 422 const char* clr_flag_value; 423 const char* clr_reg_name; 424 const char* clr_reg_value; 425 const char* clr_fpreg_name; 426 const char* clr_fpreg_value; 427 const char* clr_memory_value; 428 const char* clr_memory_address; 429 const char* clr_debug_number; 430 const char* clr_debug_message; 431 const char* clr_printf; 432 433 // Simulation helpers ------------------------------------ 434 bool ConditionPassed(Condition cond) { 435 switch (cond) { 436 case eq: 437 return Z(); 438 case ne: 439 return !Z(); 440 case hs: 441 return C(); 442 case lo: 443 return !C(); 444 case mi: 445 return N(); 446 case pl: 447 return !N(); 448 case vs: 449 return V(); 450 case vc: 451 return !V(); 452 case hi: 453 return C() && !Z(); 454 case ls: 455 return !(C() && !Z()); 456 case ge: 457 return N() == V(); 458 case lt: 459 return N() != V(); 460 case gt: 461 return !Z() && (N() == V()); 462 case le: 463 return !(!Z() && (N() == V())); 464 case nv: // Fall through. 465 case al: 466 return true; 467 default: 468 VIXL_UNREACHABLE(); 469 return false; 470 } 471 } 472 473 bool ConditionFailed(Condition cond) { 474 return !ConditionPassed(cond); 475 } 476 477 void AddSubHelper(Instruction* instr, int64_t op2); 478 int64_t AddWithCarry(unsigned reg_size, 479 bool set_flags, 480 int64_t src1, 481 int64_t src2, 482 int64_t carry_in = 0); 483 void LogicalHelper(Instruction* instr, int64_t op2); 484 void ConditionalCompareHelper(Instruction* instr, int64_t op2); 485 void LoadStoreHelper(Instruction* instr, 486 int64_t offset, 487 AddrMode addrmode); 488 void LoadStorePairHelper(Instruction* instr, AddrMode addrmode); 489 uint8_t* AddressModeHelper(unsigned addr_reg, 490 int64_t offset, 491 AddrMode addrmode); 492 493 uint64_t MemoryRead(const uint8_t* address, unsigned num_bytes); 494 uint8_t MemoryRead8(uint8_t* address); 495 uint16_t MemoryRead16(uint8_t* address); 496 uint32_t MemoryRead32(uint8_t* address); 497 float MemoryReadFP32(uint8_t* address); 498 uint64_t MemoryRead64(uint8_t* address); 499 double MemoryReadFP64(uint8_t* address); 500 501 void MemoryWrite(uint8_t* address, uint64_t value, unsigned num_bytes); 502 void MemoryWrite32(uint8_t* address, uint32_t value); 503 void MemoryWriteFP32(uint8_t* address, float value); 504 void MemoryWrite64(uint8_t* address, uint64_t value); 505 void MemoryWriteFP64(uint8_t* address, double value); 506 507 int64_t ShiftOperand(unsigned reg_size, 508 int64_t value, 509 Shift shift_type, 510 unsigned amount); 511 int64_t Rotate(unsigned reg_width, 512 int64_t value, 513 Shift shift_type, 514 unsigned amount); 515 int64_t ExtendValue(unsigned reg_width, 516 int64_t value, 517 Extend extend_type, 518 unsigned left_shift = 0); 519 520 uint64_t ReverseBits(uint64_t value, unsigned num_bits); 521 uint64_t ReverseBytes(uint64_t value, ReverseByteMode mode); 522 523 template <typename T> 524 T FPDefaultNaN() const; 525 526 void FPCompare(double val0, double val1); 527 double FPRoundInt(double value, FPRounding round_mode); 528 double FPToDouble(float value); 529 float FPToFloat(double value, FPRounding round_mode); 530 double FixedToDouble(int64_t src, int fbits, FPRounding round_mode); 531 double UFixedToDouble(uint64_t src, int fbits, FPRounding round_mode); 532 float FixedToFloat(int64_t src, int fbits, FPRounding round_mode); 533 float UFixedToFloat(uint64_t src, int fbits, FPRounding round_mode); 534 int32_t FPToInt32(double value, FPRounding rmode); 535 int64_t FPToInt64(double value, FPRounding rmode); 536 uint32_t FPToUInt32(double value, FPRounding rmode); 537 uint64_t FPToUInt64(double value, FPRounding rmode); 538 539 template <typename T> 540 T FPAdd(T op1, T op2); 541 542 template <typename T> 543 T FPDiv(T op1, T op2); 544 545 template <typename T> 546 T FPMax(T a, T b); 547 548 template <typename T> 549 T FPMaxNM(T a, T b); 550 551 template <typename T> 552 T FPMin(T a, T b); 553 554 template <typename T> 555 T FPMinNM(T a, T b); 556 557 template <typename T> 558 T FPMul(T op1, T op2); 559 560 template <typename T> 561 T FPMulAdd(T a, T op1, T op2); 562 563 template <typename T> 564 T FPSqrt(T op); 565 566 template <typename T> 567 T FPSub(T op1, T op2); 568 569 // This doesn't do anything at the moment. We'll need it if we want support 570 // for cumulative exception bits or floating-point exceptions. 571 void FPProcessException() { } 572 573 // Standard NaN processing. 574 template <typename T> 575 T FPProcessNaN(T op); 576 577 bool FPProcessNaNs(Instruction* instr); 578 579 template <typename T> 580 T FPProcessNaNs(T op1, T op2); 581 582 template <typename T> 583 T FPProcessNaNs3(T op1, T op2, T op3); 584 585 // Pseudo Printf instruction 586 void DoPrintf(Instruction* instr); 587 588 // Processor state --------------------------------------- 589 590 // Output stream. 591 FILE* stream_; 592 PrintDisassembler* print_disasm_; 593 594 // Instruction statistics instrumentation. 595 Instrument* instrumentation_; 596 597 // General purpose registers. Register 31 is the stack pointer. 598 SimRegister registers_[kNumberOfRegisters]; 599 600 // Floating point registers 601 SimFPRegister fpregisters_[kNumberOfFPRegisters]; 602 603 // Program Status Register. 604 // bits[31, 27]: Condition flags N, Z, C, and V. 605 // (Negative, Zero, Carry, Overflow) 606 SimSystemRegister nzcv_; 607 608 // Floating-Point Control Register 609 SimSystemRegister fpcr_; 610 611 // Only a subset of FPCR features are supported by the simulator. This helper 612 // checks that the FPCR settings are supported. 613 // 614 // This is checked when floating-point instructions are executed, not when 615 // FPCR is set. This allows generated code to modify FPCR for external 616 // functions, or to save and restore it when entering and leaving generated 617 // code. 618 void AssertSupportedFPCR() { 619 VIXL_ASSERT(fpcr().FZ() == 0); // No flush-to-zero support. 620 VIXL_ASSERT(fpcr().RMode() == FPTieEven); // Ties-to-even rounding only. 621 622 // The simulator does not support half-precision operations so fpcr().AHP() 623 // is irrelevant, and is not checked here. 624 } 625 626 static inline int CalcNFlag(uint64_t result, unsigned reg_size) { 627 return (result >> (reg_size - 1)) & 1; 628 } 629 630 static inline int CalcZFlag(uint64_t result) { 631 return result == 0; 632 } 633 634 static const uint32_t kConditionFlagsMask = 0xf0000000; 635 636 // Stack 637 byte* stack_; 638 static const int stack_protection_size_ = 256; 639 // 2 KB stack. 640 static const int stack_size_ = 2 * 1024 + 2 * stack_protection_size_; 641 byte* stack_limit_; 642 643 Decoder* decoder_; 644 // Indicates if the pc has been modified by the instruction and should not be 645 // automatically incremented. 646 bool pc_modified_; 647 Instruction* pc_; 648 649 static const char* xreg_names[]; 650 static const char* wreg_names[]; 651 static const char* sreg_names[]; 652 static const char* dreg_names[]; 653 static const char* vreg_names[]; 654 655 static const Instruction* kEndOfSimAddress; 656 657 private: 658 bool coloured_trace_; 659 660 // Indicates whether the disassembly trace is active. 661 bool disasm_trace_; 662 663 // Indicates whether the instruction instrumentation is active. 664 bool instruction_stats_; 665 }; 666 } // namespace vixl 667 668 #endif // VIXL_A64_SIMULATOR_A64_H_ 669