text: // Copyright 2013 the V8 project authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #ifndef V8_EXECUTION_ARM64_SIMULATOR_ARM64_H_
  #define V8_EXECUTION_ARM64_SIMULATOR_ARM64_H_
  
  // globals.h defines USE_SIMULATOR.
  #include "src/common/globals.h"
  
  #if defined(USE_SIMULATOR)
  
  #include <stdarg.h>
  
  #include <vector>
  
  #include "src/base/compiler-specific.h"
  #include "src/base/platform/wrappers.h"
  #include "src/codegen/arm64/assembler-arm64.h"
  #include "src/codegen/arm64/decoder-arm64.h"
  #include "src/codegen/assembler.h"
  #include "src/diagnostics/arm64/disasm-arm64.h"
  #include "src/execution/simulator-base.h"
  #include "src/utils/allocation.h"
  #include "src/utils/utils.h"
  
  namespace v8 {
  namespace internal {
  
  // Assemble the specified IEEE-754 components into the target type and apply
  // appropriate rounding.
  //  sign:     0 = positive, 1 = negative
  //  exponent: Unbiased IEEE-754 exponent.
  //  mantissa: The mantissa of the input. The top bit (which is not encoded for
  //            normal IEEE-754 values) must not be omitted. This bit has the
  //            value 'pow(2, exponent)'.
  //
  // The input value is assumed to be a normalized value. That is, the input may
  // not be infinity or NaN. If the source value is subnormal, it must be
  // normalized before calling this function such that the highest set bit in the
  // mantissa has the value 'pow(2, exponent)'.
  //
  // Callers should use FPRoundToFloat or FPRoundToDouble directly, rather than
  // calling a templated FPRound.
  template <class T, int ebits, int mbits>
  T FPRound(int64_t sign, int64_t exponent, uint64_t mantissa,
            FPRounding round_mode) {
    static_assert((sizeof(T) * 8) >= (1 + ebits + mbits),
                  "destination type T not large enough");
    static_assert(sizeof(T) <= sizeof(uint64_t),
                  "maximum size of destination type T is 64 bits");
    static_assert(std::is_unsigned<T>::value,
                  "destination type T must be unsigned");
  
    DCHECK((sign == 0) || (sign == 1));
  
    // Only FPTieEven and FPRoundOdd rounding modes are implemented.
    DCHECK((round_mode == FPTieEven) || (round_mode == FPRoundOdd));
  
    // Rounding can promote subnormals to normals, and normals to infinities. For
    // example, a double with exponent 127 (FLT_MAX_EXP) would appear to be
    // encodable as a float, but rounding based on the low-order mantissa bits
    // could make it overflow. With ties-to-even rounding, this value would become
    // an infinity.
  
    // ---- Rounding Method ----
    //
    // The exponent is irrelevant in the rounding operation, so we treat the
    // lowest-order bit that will fit into the result ('onebit') as having
    // the value '1'. Similarly, the highest-order bit that won't fit into
    // the result ('halfbit') has the value '0.5'. The 'point' sits between
    // 'onebit' and 'halfbit':
    //
    //            These bits fit into the result.
    //               |---------------------|
    //  mantissa = 0bxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    //                                     ||
    //                                    / |
    //                                   /  halfbit
    //                               onebit
    //
    // For subnormal outputs, the range of representable bits is smaller and
    // the position of onebit and halfbit depends on the exponent of the
    // input, but the method is otherwise similar.
    //
    //   onebit(frac)
    //     |
    //     | halfbit(frac)          halfbit(adjusted)
    //     | /                      /
    //     | |                      |
    //  0b00.0 (exact)      -> 0b00.0 (exact)                    -> 0b00
    //  0b00.0...           -> 0b00.0...                         -> 0b00
    //  0b00.1 (exact)      -> 0b00.0111..111                    -> 0b00
    //  0b00.1...           -> 0b00.1...                         -> 0b01
    //  0b01.0 (exact)      -> 0b01.0 (exact)                    -> 0b01
    //  0b01.0...           -> 0b01.0...                         -> 0b01
    //  0b01.1 (exact)      -> 0b01.1 (exact)                    -> 0b10
    //  0b01.1...           -> 0b01.1...                         -> 0b10
    //  0b10.0 (exact)      -> 0b10.0 (exact)                    -> 0b10
    //  0b10.0...           -> 0b10.0...                         -> 0b10
    //  0b10.1 (exact)      -> 0b10.0111..111                    -> 0b10
    //  0b10.1...           -> 0b10.1...                         -> 0b11
    //  0b11.0 (exact)      -> 0b11.0 (exact)                    -> 0b11
    //  ...                   /             |                      /   |
    //                       /              |                     /    |
    //                                                           /     |
    // adjusted = frac - (halfbit(mantissa) & ~onebit(frac));   /      |
    //
    //                   mantissa = (mantissa >> shift) + halfbit(adjusted);
  
    const int mantissa_offset = 0;
    const int exponent_offset = mantissa_offset + mbits;
    const int sign_offset = exponent_offset + ebits;
    DCHECK_EQ(sign_offset, static_cast<int>(sizeof(T) * 8 - 1));
  
    // Bail out early for zero inputs.
    if (mantissa == 0) {
      return static_cast<T>(sign << sign_offset);
    }
  
    // If all bits in the exponent are set, the value is infinite or NaN.
    // This is true for all binary IEEE-754 formats.
    const int infinite_exponent = (1 << ebits) - 1;
    const int max_normal_exponent = infinite_exponent - 1;
  
    // Apply the exponent bias to encode it for the result. Doing this early makes
    // it easy to detect values that will be infinite or subnormal.
    exponent += max_normal_exponent >> 1;
  
    if (exponent > max_normal_exponent) {
      // Overflow: the input is too large for the result type to represent.
      if (round_mode == FPTieEven) {
        // FPTieEven rounding mode handles overflows using infinities.
        exponent = infinite_exponent;
        mantissa = 0;
      } else {
        DCHECK_EQ(round_mode, FPRoundOdd);
        // FPRoundOdd rounding mode handles overflows using the largest magnitude
        // normal number.
        exponent = max_normal_exponent;
        mantissa = (UINT64_C(1) << exponent_offset) - 1;
      }
      return static_cast<T>((sign << sign_offset) |
                            (exponent << exponent_offset) |
                            (mantissa << mantissa_offset));
    }
  
    // Calculate the shift required to move the top mantissa bit to the proper
    // place in the destination type.
    const int highest_significant_bit = 63 - CountLeadingZeros(mantissa, 64);
    int shift = highest_significant_bit - mbits;
  
    if (exponent <= 0) {
      // The output will be subnormal (before rounding).
      // 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;
  
      // Handle inputs that would produce a zero output.
      //
      // Shifts higher than highest_significant_bit+1 will always produce a zero
      // result. A shift of exactly highest_significant_bit+1 might produce a
      // non-zero result after rounding.
      if (shift > (highest_significant_bit + 1)) {
        if (round_mode == FPTieEven) {
          // The result will always be +/-0.0.
          return static_cast<T>(sign << sign_offset);
        } else {
          DCHECK_EQ(round_mode, FPRoundOdd);
          DCHECK_NE(mantissa, 0U);
          // For FPRoundOdd, if the mantissa is too small to represent and
          // non-zero return the next "odd" value.
          return static_cast<T>((sign << sign_offset) | 1);
        }
      }
  
      // Properly encode the exponent for a subnormal output.
      exponent = 0;
    } else {
      // Clear the topmost mantissa bit, since this is not encoded in IEEE-754
      // normal values.
      mantissa &= ~(UINT64_C(1) << highest_significant_bit);
    }
  
    if (shift > 0) {
      if (round_mode == FPTieEven) {
        // We have to shift the mantissa to the right. Some precision is lost, so
        // we need to apply rounding.
        uint64_t onebit_mantissa = (mantissa >> (shift)) & 1;
        uint64_t halfbit_mantissa = (mantissa >> (shift - 1)) & 1;
        uint64_t adjustment = (halfbit_mantissa & ~onebit_mantissa);
        uint64_t adjusted = mantissa - adjustment;
        T halfbit_adjusted = (adjusted >> (shift - 1)) & 1;
  
        T result =
            static_cast<T>((sign << sign_offset) | (exponent << exponent_offset) |
                           ((mantissa >> shift) << mantissa_offset));
  
        // A very large mantissa can overflow during rounding. If this happens,
        // the exponent should be incremented and the mantissa set to 1.0
        // (encoded as 0). Applying halfbit_adjusted after assembling the float
        // has the nice side-effect that this case is handled for free.
        //
        // This also handles cases where a very large finite value overflows to
        // infinity, or where a very large subnormal value overflows to become
        // normal.
        return result + halfbit_adjusted;
      } else {
        DCHECK_EQ(round_mode, FPRoundOdd);
        // If any bits at position halfbit or below are set, onebit (ie. the
        // bottom bit of the resulting mantissa) must be set.
        uint64_t fractional_bits = mantissa & ((UINT64_C(1) << shift) - 1);
        if (fractional_bits != 0) {
          mantissa |= UINT64_C(1) << shift;
        }
  
        return static_cast<T>((sign << sign_offset) |
                              (exponent << exponent_offset) |
                              ((mantissa >> shift) << mantissa_offset));
      }
    } else {
      // We have to shift the mantissa to the left (or not at all). The input
      // mantissa is exactly representable in the output mantissa, so apply no
      // rounding correction.
      return static_cast<T>((sign << sign_offset) |
                            (exponent << exponent_offset) |
                            ((mantissa << -shift) << mantissa_offset));
    }
  }
  
  class CachePage {
    // TODO(all): Simulate instruction cache.
  };
  
  // Representation of memory, with typed getters and setters for access.
  class SimMemory {
   public:
    template <typename T>
    static T AddressUntag(T address) {
      // Cast the address using a C-style cast. A reinterpret_cast would be
      // appropriate, but it can't cast one integral type to another.
      uint64_t bits = (uint64_t)address;
      return (T)(bits & ~kAddressTagMask);
    }
  
    template <typename T, typename A>
    static T Read(A address) {
      T value;
      address = AddressUntag(address);
      DCHECK((sizeof(value) == 1) || (sizeof(value) == 2) ||
             (sizeof(value) == 4) || (sizeof(value) == 8) ||
             (sizeof(value) == 16));
      memcpy(&value, reinterpret_cast<const char*>(address), sizeof(value));
      return value;
    }
  
    template <typename T, typename A>
    static void Write(A address, T value) {
      address = AddressUntag(address);
      DCHECK((sizeof(value) == 1) || (sizeof(value) == 2) ||
             (sizeof(value) == 4) || (sizeof(value) == 8) ||
             (sizeof(value) == 16));
      memcpy(reinterpret_cast<char*>(address), &value, sizeof(value));
    }
  };
  
  // 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) {}
  
    uint32_t RawValue() const { return value_; }
  
    void SetRawValue(uint32_t new_value) {
      value_ = (value_ & write_ignore_mask_) | (new_value & ~write_ignore_mask_);
    }
  
    uint32_t Bits(int msb, int lsb) const {
      return unsigned_bitextract_32(msb, lsb, value_);
    }
  
    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, Type)                 \
    Type Name() const { return static_cast<Type>(Func(HighBit, LowBit)); } \
    void Set##Name(Type bits) {                                            \
      SetBits(HighBit, LowBit, static_cast<Type>(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:
    template <typename T>
    void Set(T new_value) {
      static_assert(sizeof(new_value) <= kSizeInBytes,
                    "Size of new_value must be <= size of template type.");
      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(T));
      NotifyRegisterWrite();
    }
  
    // Insert a typed value into a register, leaving the rest of the register
    // unchanged. The lane parameter indicates where in the register the value
    // should be inserted, in the range [ 0, sizeof(value_) / sizeof(T) ), where
    // 0 represents the least significant bits.
    template <typename T>
    void Insert(int lane, T new_value) {
      DCHECK_GE(lane, 0);
      DCHECK_LE(sizeof(new_value) + (lane * sizeof(new_value)),
                static_cast<unsigned>(kSizeInBytes));
      memcpy(&value_[lane * sizeof(new_value)], &new_value, sizeof(new_value));
      NotifyRegisterWrite();
    }
  
    template <typename T>
    T Get(int lane = 0) const {
      T result;
      DCHECK_GE(lane, 0);
      DCHECK_LE(sizeof(result) + (lane * sizeof(result)),
                static_cast<unsigned>(kSizeInBytes));
      memcpy(&result, &value_[lane * sizeof(result)], sizeof(result));
      return result;
    }
  
    // TODO(all): Make this return a map of updated bytes, so that we can
    // highlight updated lanes for load-and-insert. (That never happens for scalar
    // code, but NEON has some instructions that can update individual lanes.)
    bool WrittenSinceLastLog() const { return written_since_last_log_; }
  
    void NotifyRegisterLogged() { written_since_last_log_ = false; }
  
   protected:
    uint8_t value_[kSizeInBytes];
  
    // Helpers to aid with register tracing.
    bool written_since_last_log_;
  
    void NotifyRegisterWrite() { written_since_last_log_ = true; }
  };
  
  using SimRegister = SimRegisterBase<kXRegSize>;   // r0-r31
  using SimVRegister = SimRegisterBase<kQRegSize>;  // v0-v31
  
  // Representation of a vector register, with typed getters and setters for lanes
  // and additional information to represent lane state.
  class LogicVRegister {
   public:
    inline LogicVRegister(SimVRegister& other)  // NOLINT
        : register_(other) {
      for (unsigned i = 0; i < arraysize(saturated_); i++) {
        saturated_[i] = kNotSaturated;
      }
      for (unsigned i = 0; i < arraysize(round_); i++) {
        round_[i] = false;
      }
    }
  
    int64_t Int(VectorFormat vform, int index) const {
      int64_t element;
      switch (LaneSizeInBitsFromFormat(vform)) {
        case 8:
          element = register_.Get<int8_t>(index);
          break;
        case 16:
          element = register_.Get<int16_t>(index);
          break;
        case 32:
          element = register_.Get<int32_t>(index);
          break;
        case 64:
          element = register_.Get<int64_t>(index);
          break;
        default:
          UNREACHABLE();
          return 0;
      }
      return element;
    }
  
    uint64_t Uint(VectorFormat vform, int index) const {
      uint64_t element;
      switch (LaneSizeInBitsFromFormat(vform)) {
        case 8:
          element = register_.Get<uint8_t>(index);
          break;
        case 16:
          element = register_.Get<uint16_t>(index);
          break;
        case 32:
          element = register_.Get<uint32_t>(index);
          break;
        case 64:
          element = register_.Get<uint64_t>(index);
          break;
        default:
          UNREACHABLE();
          return 0;
      }
      return element;
    }
  
    uint64_t UintLeftJustified(VectorFormat vform, int index) const {
      return Uint(vform, index) << (64 - LaneSizeInBitsFromFormat(vform));
    }
  
    int64_t IntLeftJustified(VectorFormat vform, int index) const {
      uint64_t value = UintLeftJustified(vform, index);
      int64_t result;
      memcpy(&result, &value, sizeof(result));
      return result;
    }
  
    void SetInt(VectorFormat vform, int index, int64_t value) const {
      switch (LaneSizeInBitsFromFormat(vform)) {
        case 8:
          register_.Insert(index, static_cast<int8_t>(value));
          break;
        case 16:
          register_.Insert(index, static_cast<int16_t>(value));
          break;
        case 32:
          register_.Insert(index, static_cast<int32_t>(value));
          break;
        case 64:
          register_.Insert(index, static_cast<int64_t>(value));
          break;
        default:
          UNREACHABLE();
          return;
      }
    }
  
    void SetIntArray(VectorFormat vform, const int64_t* src) const {
      ClearForWrite(vform);
      for (int i = 0; i < LaneCountFromFormat(vform); i++) {
        SetInt(vform, i, src[i]);
      }
    }
  
    void SetUint(VectorFormat vform, int index, uint64_t value) const {
      switch (LaneSizeInBitsFromFormat(vform)) {
        case 8:
          register_.Insert(index, static_cast<uint8_t>(value));
          break;
        case 16:
          register_.Insert(index, static_cast<uint16_t>(value));
          break;
        case 32:
          register_.Insert(index, static_cast<uint32_t>(value));
          break;
        case 64:
          register_.Insert(index, static_cast<uint64_t>(value));
          break;
        default:
          UNREACHABLE();
          return;
      }
    }
  
    void SetUintArray(VectorFormat vform, const uint64_t* src) const {
      ClearForWrite(vform);
      for (int i = 0; i < LaneCountFromFormat(vform); i++) {
        SetUint(vform, i, src[i]);
      }
    }
  
    void ReadUintFromMem(VectorFormat vform, int index, uint64_t addr) const;
  
    void WriteUintToMem(VectorFormat vform, int index, uint64_t addr) const;
  
    template <typename T>
    T Float(int index) const {
      return register_.Get<T>(index);
    }
  
    template <typename T>
    void SetFloat(int index, T value) const {
      register_.Insert(index, value);
    }
  
    // When setting a result in a register of size less than Q, the top bits of
    // the Q register must be cleared.
    void ClearForWrite(VectorFormat vform) const {
      unsigned size = RegisterSizeInBytesFromFormat(vform);
      for (unsigned i = size; i < kQRegSize; i++) {
        SetUint(kFormat16B, i, 0);
      }
    }
  
    // Saturation state for each lane of a vector.
    enum Saturation {
      kNotSaturated = 0,
      kSignedSatPositive = 1 << 0,
      kSignedSatNegative = 1 << 1,
      kSignedSatMask = kSignedSatPositive | kSignedSatNegative,
      kSignedSatUndefined = kSignedSatMask,
      kUnsignedSatPositive = 1 << 2,
      kUnsignedSatNegative = 1 << 3,
      kUnsignedSatMask = kUnsignedSatPositive | kUnsignedSatNegative,
      kUnsignedSatUndefined = kUnsignedSatMask
    };
  
    // Getters for saturation state.
    Saturation GetSignedSaturation(int index) {
      return static_cast<Saturation>(saturated_[index] & kSignedSatMask);
    }
  
    Saturation GetUnsignedSaturation(int index) {
      return static_cast<Saturation>(saturated_[index] & kUnsignedSatMask);
    }
  
    // Setters for saturation state.
    void ClearSat(int index) { saturated_[index] = kNotSaturated; }
  
    void SetSignedSat(int index, bool positive) {
      SetSatFlag(index, positive ? kSignedSatPositive : kSignedSatNegative);
    }
  
    void SetUnsignedSat(int index, bool positive) {
      SetSatFlag(index, positive ? kUnsignedSatPositive : kUnsignedSatNegative);
    }
  
    void SetSatFlag(int index, Saturation sat) {
      saturated_[index] = static_cast<Saturation>(saturated_[index] | sat);
      DCHECK_NE(sat & kUnsignedSatMask, kUnsignedSatUndefined);
      DCHECK_NE(sat & kSignedSatMask, kSignedSatUndefined);
    }
  
    // Saturate lanes of a vector based on saturation state.
    LogicVRegister& SignedSaturate(VectorFormat vform) {
      for (int i = 0; i < LaneCountFromFormat(vform); i++) {
        Saturation sat = GetSignedSaturation(i);
        if (sat == kSignedSatPositive) {
          SetInt(vform, i, MaxIntFromFormat(vform));
        } else if (sat == kSignedSatNegative) {
          SetInt(vform, i, MinIntFromFormat(vform));
        }
      }
      return *this;
    }
  
    LogicVRegister& UnsignedSaturate(VectorFormat vform) {
      for (int i = 0; i < LaneCountFromFormat(vform); i++) {
        Saturation sat = GetUnsignedSaturation(i);
        if (sat == kUnsignedSatPositive) {
          SetUint(vform, i, MaxUintFromFormat(vform));
        } else if (sat == kUnsignedSatNegative) {
          SetUint(vform, i, 0);
        }
      }
      return *this;
    }
  
    // Getter for rounding state.
    bool GetRounding(int index) { return round_[index]; }
  
    // Setter for rounding state.
    void SetRounding(int index, bool round) { round_[index] = round; }
  
    // Round lanes of a vector based on rounding state.
    LogicVRegister& Round(VectorFormat vform) {
      for (int i = 0; i < LaneCountFromFormat(vform); i++) {
        SetUint(vform, i, Uint(vform, i) + (GetRounding(i) ? 1 : 0));
      }
      return *this;
    }
  
    // Unsigned halve lanes of a vector, and use the saturation state to set the
    // top bit.
    LogicVRegister& Uhalve(VectorFormat vform) {
      for (int i = 0; i < LaneCountFromFormat(vform); i++) {
        uint64_t val = Uint(vform, i);
        SetRounding(i, (val & 1) == 1);
        val >>= 1;
        if (GetUnsignedSaturation(i) != kNotSaturated) {
          // If the operation causes unsigned saturation, the bit shifted into the
          // most significant bit must be set.
          val |= (MaxUintFromFormat(vform) >> 1) + 1;
        }
        SetInt(vform, i, val);
      }
      return *this;
    }
  
    // Signed halve lanes of a vector, and use the carry state to set the top bit.
    LogicVRegister& Halve(VectorFormat vform) {
      for (int i = 0; i < LaneCountFromFormat(vform); i++) {
        int64_t val = Int(vform, i);
        SetRounding(i, (val & 1) == 1);
        val >>= 1;
        if (GetSignedSaturation(i) != kNotSaturated) {
          // If the operation causes signed saturation, the sign bit must be
          // inverted.
          val ^= (MaxUintFromFormat(vform) >> 1) + 1;
        }
        SetInt(vform, i, val);
      }
      return *this;
    }
  
   private:
    SimVRegister& register_;
  
    // Allocate one saturation state entry per lane; largest register is type Q,
    // and lanes can be a minimum of one byte wide.
    Saturation saturated_[kQRegSize];
  
    // Allocate one rounding state entry per lane.
    bool round_[kQRegSize];
  };
  
  // Using multiple inheritance here is permitted because {DecoderVisitor} is a
  // pure interface class with only pure virtual methods.
  class Simulator : public DecoderVisitor, public SimulatorBase {
   public:
    static void SetRedirectInstruction(Instruction* instruction);
    static bool ICacheMatch(void* one, void* two) { return false; }
    static void FlushICache(base::CustomMatcherHashMap* i_cache, void* start,
                            size_t size) {
      USE(i_cache);
      USE(start);
      USE(size);
    }
  
    V8_EXPORT_PRIVATE explicit Simulator(
        Decoder<DispatchingDecoderVisitor>* decoder, Isolate* isolate = nullptr,
        FILE* stream = stderr);
    Simulator();
    V8_EXPORT_PRIVATE ~Simulator();
  
    // System functions.
  
    V8_EXPORT_PRIVATE static Simulator* current(v8::internal::Isolate* isolate);
  
    // A wrapper class that stores an argument for one of the above Call
    // functions.
    //
    // Only arguments up to 64 bits in size are supported.
    class CallArgument {
     public:
      template <typename T>
      explicit CallArgument(T argument) {
        bits_ = 0;
        DCHECK(sizeof(argument) <= sizeof(bits_));
        memcpy(&bits_, &argument, sizeof(argument));
        type_ = X_ARG;
      }
  
      explicit CallArgument(double argument) {
        DCHECK(sizeof(argument) == sizeof(bits_));
        memcpy(&bits_, &argument, sizeof(argument));
        type_ = D_ARG;
      }
  
      explicit CallArgument(float argument) {
        // TODO(all): CallArgument(float) is untested, remove this check once
        //            tested.
        UNIMPLEMENTED();
        // Make the D register a NaN to try to trap errors if the callee expects a
        // double. If it expects a float, the callee should ignore the top word.
        DCHECK(sizeof(kFP64SignallingNaN) == sizeof(bits_));
        memcpy(&bits_, &kFP64SignallingNaN, sizeof(kFP64SignallingNaN));
        // Write the float payload to the S register.
        DCHECK(sizeof(argument) <= sizeof(bits_));
        memcpy(&bits_, &argument, sizeof(argument));
        type_ = D_ARG;
      }
  
      // This indicates the end of the arguments list, so that CallArgument
      // objects can be passed into varargs functions.
      static CallArgument End() { return CallArgument(); }
  
      int64_t bits() const { return bits_; }
      bool IsEnd() const { return type_ == NO_ARG; }
      bool IsX() const { return type_ == X_ARG; }
      bool IsD() const { return type_ == D_ARG; }
  
     private:
      enum CallArgumentType { X_ARG, D_ARG, NO_ARG };
  
      // All arguments are aligned to at least 64 bits and we don't support
      // passing bigger arguments, so the payload size can be fixed at 64 bits.
      int64_t bits_;
      CallArgumentType type_;
  
      CallArgument() { type_ = NO_ARG; }
    };
  
    // Call an arbitrary function taking an arbitrary number of arguments.
    template <typename Return, typename... Args>
    Return Call(Address entry, Args... args) {
      // Convert all arguments to CallArgument.
      CallArgument call_args[] = {CallArgument(args)..., CallArgument::End()};
      CallImpl(entry, call_args);
      return ReadReturn<Return>();
    }
  
    // Start the debugging command line.
    void Debug();
  
    // Executes a single debug command. Takes ownership of the command (so that it
    // can store it for repeat executions), and returns true if the debugger
    // should resume execution after this command completes.
    bool ExecDebugCommand(ArrayUniquePtr<char> command);
  
    bool GetValue(const char* desc, int64_t* value);
  
    bool PrintValue(const char* desc);
  
    // Push an address onto the JS stack.
    uintptr_t PushAddress(uintptr_t address);
  
    // Pop an address from the JS stack.
    uintptr_t PopAddress();
  
    // Accessor to the internal simulator stack area.
    uintptr_t StackLimit(uintptr_t c_limit) const;
  
    V8_EXPORT_PRIVATE void ResetState();
  
    void DoRuntimeCall(Instruction* instr);
  
    // Run the simulator.
    static const Instruction* kEndOfSimAddress;
    void DecodeInstruction();
    void Run();
    V8_EXPORT_PRIVATE void RunFrom(Instruction* start);
  
    // Simulation helpers.
    template <typename T>
    void set_pc(T new_pc) {
      static_assert(sizeof(T) == sizeof(pc_));
      memcpy(&pc_, &new_pc, sizeof(T));
      pc_modified_ = true;
    }
    Instruction* pc() { return pc_; }
  
    void increment_pc() {
      if (!pc_modified_) {
        pc_ = pc_->following();
      }
  
      pc_modified_ = false;
    }
  
    virtual void Decode(Instruction* instr) { decoder_->Decode(instr); }
  
    // Branch Target Identification (BTI)
    //
    // Executing an instruction updates PSTATE.BTYPE, as described in the table
    // below. Execution of an instruction on a guarded page is allowed if either:
    // * PSTATE.BTYPE is 00, or
    // * it is a BTI or PACI[AB]SP instruction that accepts the current value of
    //   PSTATE.BTYPE (as described in the table below), or
    // * it is BRK or HLT instruction that causes some higher-priority exception.
    //
    //  --------------------------------------------------------------------------
    //  | Last-executed instruction    | Sets     | Accepted by                  |
    //  |                              | BTYPE to | BTI | BTI j | BTI c | BTI jc |
    //  --------------------------------------------------------------------------
    //  | - BR from an unguarded page. |          |     |       |       |        |
    //  | - BR from guarded page,      |          |     |       |       |        |
    //  |   to x16 or x17.             |    01    |     |   X   |   X   |   X    |
    //  --------------------------------------------------------------------------
    //  | BR from guarded page,        |          |     |       |       |        |
    //  | not to x16 or x17.           |    11    |     |   X   |       |   X    |
    //  --------------------------------------------------------------------------
    //  | BLR                          |    10    |     |       |   X   |   X    |
    //  --------------------------------------------------------------------------
    //  | Any other instruction        |          |     |       |       |        |
    //  |(including RET).              |    00    |  X  |   X   |   X   |   X    |
    //  --------------------------------------------------------------------------
    //
    // PACI[AB]SP is treated either like "BTI c" or "BTI jc", according to the
    // value of SCTLR_EL1.BT0. Details available in ARM DDI 0487E.a, D5-2580.
  
    enum BType {
      // Set when executing any instruction, except those cases listed below.
      DefaultBType = 0,
  
      // Set when an indirect branch is taken from an unguarded page, or from a
      // guarded page to ip0 or ip1 (x16 or x17), eg "br ip0".
      BranchFromUnguardedOrToIP = 1,
  
      // Set when an indirect branch and link (call) is taken, eg. "blr x0".
      BranchAndLink = 2,
  
      // Set when an indirect branch is taken from a guarded page to a register
      // that is not ip0 or ip1 (x16 or x17), eg, "br x0".
      BranchFromGuardedNotToIP = 3
    };
  
    BType btype() const { return btype_; }
    void ResetBType() { btype_ = DefaultBType; }
    void set_btype(BType btype) { btype_ = btype; }
  
    // Helper function to determine BType for branches.
    BType GetBTypeFromInstruction(const Instruction* instr) const;
  
    bool PcIsInGuardedPage() const { return guard_pages_; }
    void SetGuardedPages(bool guard_pages) { guard_pages_ = guard_pages; }
  
    void CheckBTypeForPAuth() {
      DCHECK(pc_->IsPAuth());
      Instr instr = pc_->Mask(SystemPAuthMask);
      // Only PACI[AB]SP allowed here, and we only support PACIBSP.
      CHECK(instr == PACIBSP);
      // Check BType allows PACI[AB]SP instructions.
      switch (btype()) {
        case BranchFromGuardedNotToIP:
          // This case depends on the value of SCTLR_EL1.BT0, which we assume
          // here to be set. This makes PACI[AB]SP behave like "BTI c",
          // disallowing its execution when BTYPE is BranchFromGuardedNotToIP
          // (0b11).
          FATAL("Executing PACIBSP with wrong BType.");
        case BranchFromUnguardedOrToIP:
        case BranchAndLink:
          break;
        case DefaultBType:
          UNREACHABLE();
      }
    }
  
    void CheckBTypeForBti() {
      DCHECK(pc_->IsBti());
      switch (pc_->ImmHint()) {
        case BTI_jc:
          break;
        case BTI: {
          DCHECK(btype() != DefaultBType);
          FATAL("Executing BTI with wrong BType (expected 0, got %d).", btype());
          break;
        }
        case BTI_c:
          if (btype() == BranchFromGuardedNotToIP) {
            FATAL("Executing BTI c with wrong BType (3).");
          }
          break;
        case BTI_j:
          if (btype() == BranchAndLink) {
            FATAL("Executing BTI j with wrong BType (2).");
          }
          break;
        default:
          UNIMPLEMENTED();
      }
    }
  
    void CheckBType() {
      // On guarded pages, if BType is not zero, take an exception on any
      // instruction other than BTI, PACI[AB]SP, HLT or BRK.
      if (PcIsInGuardedPage() && (btype() != DefaultBType)) {
        if (pc_->IsPAuth()) {
          CheckBTypeForPAuth();
        } else if (pc_->IsBti()) {
          CheckBTypeForBti();
        } else if (!pc_->IsException()) {
          FATAL("Executing non-BTI instruction with wrong BType.");
        }
      }
    }
  
    void ExecuteInstruction() {
      DCHECK(IsAligned(reinterpret_cast<uintptr_t>(pc_), kInstrSize));
      CheckBType();
      ResetBType();
      CheckBreakNext();
      Decode(pc_);
      increment_pc();
      LogAllWrittenRegisters();
      CheckBreakpoints();
    }
  
  // Declare all Visitor functions.
  #define DECLARE(A) void Visit##A(Instruction* instr);
    VISITOR_LIST(DECLARE)
  #undef DECLARE
  
    bool IsZeroRegister(unsigned code, Reg31Mode r31mode) const {
      return ((code == 31) && (r31mode == Reg31IsZeroRegister));
    }
  
    // Register accessors.
    // Return 'size' bits of the value of an integer register, as the specified
    // type. The value is zero-extended to fill the result.
    //
    template <typename T>
    T reg(unsigned code, Reg31Mode r31mode = Reg31IsZeroRegister) const {
      DCHECK_LT(code, static_cast<unsigned>(kNumberOfRegisters));
      if (IsZeroRegister(code, r31mode)) {
        return 0;
      }
      return registers_[code].Get<T>();
    }
  
    // Common specialized accessors for the reg() template.
    int32_t wreg(unsigned code, Reg31Mode r31mode = Reg31IsZeroRegister) const {
      return reg<int32_t>(code, r31mode);
    }
  
    int64_t xreg(unsigned code, Reg31Mode r31mode = Reg31IsZeroRegister) const {
      return reg<int64_t>(code, r31mode);
    }
  
    enum RegLogMode { LogRegWrites, NoRegLog };
  
    // Write 'value' into an integer register. The value is zero-extended. This
    // behaviour matches AArch64 register writes.
    template <typename T>
    void set_reg(unsigned code, T value,
                 Reg31Mode r31mode = Reg31IsZeroRegister) {
      set_reg_no_log(code, value, r31mode);
      LogRegister(code, r31mode);
    }
  
    // Common specialized accessors for the set_reg() template.
    void set_wreg(unsigned code, int32_t value,
                  Reg31Mode r31mode = Reg31IsZeroRegister) {
      set_reg(code, value, r31mode);
    }
  
    void set_xreg(unsigned code, int64_t value,
                  Reg31Mode r31mode = Reg31IsZeroRegister) {
      set_reg(code, value, r31mode);
    }
  
    // As above, but don't automatically log the register update.
    template <typename T>
    void set_reg_no_log(unsigned code, T value,
                        Reg31Mode r31mode = Reg31IsZeroRegister) {
      DCHECK_LT(code, static_cast<unsigned>(kNumberOfRegisters));
      if (!IsZeroRegister(code, r31mode)) {
        registers_[code].Set(value);
      }
    }
  
    void set_wreg_no_log(unsigned code, int32_t value,
                         Reg31Mode r31mode = Reg31IsZeroRegister) {
      set_reg_no_log(code, value, r31mode);
    }
  
    void set_xreg_no_log(unsigned code, int64_t value,
                         Reg31Mode r31mode = Reg31IsZeroRegister) {
      set_reg_no_log(code, value, r31mode);
    }
  
    // Commonly-used special cases.
    template <typename T>
    void set_lr(T value) {
      DCHECK_EQ(sizeof(T), static_cast<unsigned>(kSystemPointerSize));
      set_reg(kLinkRegCode, value);
    }
  
    template <typename T>
    void set_sp(T value) {
      DCHECK_EQ(sizeof(T), static_cast<unsigned>(kSystemPointerSize));
      set_reg(31, value, Reg31IsStackPointer);
    }
  
    // Vector register accessors.
    // These are equivalent to the integer register accessors, but for vector
    // registers.
  
    // A structure for representing a 128-bit Q register.
    struct qreg_t {
      uint8_t val[kQRegSize];
    };
  
    // Basic accessor: read the register as the specified type.
    template <typename T>
    T vreg(unsigned code) const {
      static_assert((sizeof(T) == kBRegSize) || (sizeof(T) == kHRegSize) ||
                        (sizeof(T) == kSRegSize) || (sizeof(T) == kDRegSize) ||
                        (sizeof(T) == kQRegSize),
                    "Template type must match size of register.");
      DCHECK_LT(code, static_cast<unsigned>(kNumberOfVRegisters));
  
      return vregisters_[code].Get<T>();
    }
  
    inline SimVRegister& vreg(unsigned code) { return vregisters_[code]; }
  
    int64_t sp() { return xreg(31, Reg31IsStackPointer); }
    int64_t fp() { return xreg(kFramePointerRegCode, Reg31IsStackPointer); }
    Instruction* lr() { return reg<Instruction*>(kLinkRegCode); }
  
    Address get_sp() const { return reg<Address>(31, Reg31IsStackPointer); }
  
    // Common specialized accessors for the vreg() template.
    uint8_t breg(unsigned code) const { return vreg<uint8_t>(code); }
  
    float hreg(unsigned code) const { return vreg<uint16_t>(code); }
  
    float sreg(unsigned code) const { return vreg<float>(code); }
  
    uint32_t sreg_bits(unsigned code) const { return vreg<uint32_t>(code); }
  
    double dreg(unsigned code) const { return vreg<double>(code); }
  
    uint64_t dreg_bits(unsigned code) const { return vreg<uint64_t>(code); }
  
    qreg_t qreg(unsigned code) const { return vreg<qreg_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>
    T vreg(unsigned size, unsigned code) const {
      uint64_t raw = 0;
      T result;
  
      switch (size) {
        case kSRegSize:
          raw = vreg<uint32_t>(code);
          break;
        case kDRegSize:
          raw = vreg<uint64_t>(code);
          break;
        default:
          UNREACHABLE();
      }
  
      static_assert(sizeof(result) <= sizeof(raw),
                    "Template type must be <= 64 bits.");
      // Copy the result and truncate to fit. This assumes a little-endian host.
      memcpy(&result, &raw, sizeof(result));
      return result;
    }
  
    // Write 'value' into a floating-point register. The value is zero-extended.
    // This behaviour matches AArch64 register writes.
    template <typename T>
    void set_vreg(unsigned code, T value, RegLogMode log_mode = LogRegWrites) {
      static_assert(
          (sizeof(value) == kBRegSize) || (sizeof(value) == kHRegSize) ||
              (sizeof(value) == kSRegSize) || (sizeof(value) == kDRegSize) ||
              (sizeof(value) == kQRegSize),
          "Template type must match size of register.");
      DCHECK_LT(code, static_cast<unsigned>(kNumberOfVRegisters));
      vregisters_[code].Set(value);
  
      if (log_mode == LogRegWrites) {
        LogVRegister(code, GetPrintRegisterFormat(value));
      }
    }
  
    // Common specialized accessors for the set_vreg() template.
    void set_breg(unsigned code, int8_t value,
                  RegLogMode log_mode = LogRegWrites) {
      set_vreg(code, value, log_mode);
    }
  
    void set_hreg(unsigned code, int16_t value,
                  RegLogMode log_mode = LogRegWrites) {
      set_vreg(code, value, log_mode);
    }
  
    void set_sreg(unsigned code, float value,
                  RegLogMode log_mode = LogRegWrites) {
      set_vreg(code, value, log_mode);
    }
  
    void set_sreg_bits(unsigned code, uint32_t value,
                       RegLogMode log_mode = LogRegWrites) {
      set_vreg(code, value, log_mode);
    }
  
    void set_dreg(unsigned code, double value,
                  RegLogMode log_mode = LogRegWrites) {
      set_vreg(code, value, log_mode);
    }
  
    void set_dreg_bits(unsigned code, uint64_t value,
                       RegLogMode log_mode = LogRegWrites) {
      set_vreg(code, value, log_mode);
    }
  
    void set_qreg(unsigned code, qreg_t value,
                  RegLogMode log_mode = LogRegWrites) {
      set_vreg(code, value, log_mode);
    }
  
    // As above, but don't automatically log the register update.
    template <typename T>
    void set_vreg_no_log(unsigned code, T value) {
      static_assert((sizeof(value) == kBRegSize) ||
                    (sizeof(value) == kHRegSize) ||
                    (sizeof(value) == kSRegSize) ||
                    (sizeof(value) == kDRegSize) || (sizeof(value) == kQRegSize));
      DCHECK_LT(code, static_cast<unsigned>(kNumberOfVRegisters));
      vregisters_[code].Set(value);
    }
  
    void set_breg_no_log(unsigned code, uint8_t value) {
      set_vreg_no_log(code, value);
    }
  
    void set_hreg_no_log(unsigned code, uint16_t value) {
      set_vreg_no_log(code, value);
    }
  
    void set_sreg_no_log(unsigned code, float value) {
      set_vreg_no_log(code, value);
    }
  
    void set_dreg_no_log(unsigned code, double value) {
      set_vreg_no_log(code, value);
    }
  
    void set_qreg_no_log(unsigned code, qreg_t value) {
      set_vreg_no_log(code, value);
    }
  
    SimSystemRegister& nzcv() { return nzcv_; }
    SimSystemRegister& fpcr() { return fpcr_; }
    FPRounding RMode() { return static_cast<FPRounding>(fpcr_.RMode()); }
    bool DN() { return fpcr_.DN() != 0; }
  
    // Debug helpers
  
    // Simulator breakpoints.
    struct Breakpoint {
      Instruction* location;
      bool enabled;
    };
    std::vector<Breakpoint> breakpoints_;
    void SetBreakpoint(Instruction* breakpoint);
    void ListBreakpoints();
    void CheckBreakpoints();
  
    // Helpers for the 'next' command.
    // When this is set, the Simulator will insert a breakpoint after the next BL
    // instruction it meets.
    bool break_on_next_;
    // Check if the Simulator should insert a break after the current instruction
    // for the 'next' command.
    void CheckBreakNext();
  
    // Disassemble instruction at the given address.
    void PrintInstructionsAt(Instruction* pc, uint64_t count);
  
    // Print all registers of the specified types.
    void PrintRegisters();
    void PrintVRegisters();
    void PrintSystemRegisters();
  
    // As above, but only print the registers that have been updated.
    void PrintWrittenRegisters();
    void PrintWrittenVRegisters();
  
    // As above, but respect LOG_REG and LOG_VREG.
    void LogWrittenRegisters() {
      if (log_parameters() & LOG_REGS) PrintWrittenRegisters();
    }
    void LogWrittenVRegisters() {
      if (log_parameters() & LOG_VREGS) PrintWrittenVRegisters();
    }
    void LogAllWrittenRegisters() {
      LogWrittenRegisters();
      LogWrittenVRegisters();
    }
  
    // Specify relevant register formats for Print(V)Register and related helpers.
    enum PrintRegisterFormat {
      // The lane size.
      kPrintRegLaneSizeB = 0 << 0,
      kPrintRegLaneSizeH = 1 << 0,
      kPrintRegLaneSizeS = 2 << 0,
      kPrintRegLaneSizeW = kPrintRegLaneSizeS,
      kPrintRegLaneSizeD = 3 << 0,
      kPrintRegLaneSizeX = kPrintRegLaneSizeD,
      kPrintRegLaneSizeQ = 4 << 0,
  
      kPrintRegLaneSizeOffset = 0,
      kPrintRegLaneSizeMask = 7 << 0,
  
      // The lane count.
      kPrintRegAsScalar = 0,
      kPrintRegAsDVector = 1 << 3,
      kPrintRegAsQVector = 2 << 3,
  
      kPrintRegAsVectorMask = 3 << 3,
  
      // Indicate floating-point format lanes. (This flag is only supported for S-
      // and D-sized lanes.)
      kPrintRegAsFP = 1 << 5,
  
      // Supported combinations.
  
      kPrintXReg = kPrintRegLaneSizeX | kPrintRegAsScalar,
      kPrintWReg = kPrintRegLaneSizeW | kPrintRegAsScalar,
      kPrintSReg = kPrintRegLaneSizeS | kPrintRegAsScalar | kPrintRegAsFP,
      kPrintDReg = kPrintRegLaneSizeD | kPrintRegAsScalar | kPrintRegAsFP,
  
      kPrintReg1B = kPrintRegLaneSizeB | kPrintRegAsScalar,
      kPrintReg8B = kPrintRegLaneSizeB | kPrintRegAsDVector,
      kPrintReg16B = kPrintRegLaneSizeB | kPrintRegAsQVector,
      kPrintReg1H = kPrintRegLaneSizeH | kPrintRegAsScalar,
      kPrintReg4H = kPrintRegLaneSizeH | kPrintRegAsDVector,
      kPrintReg8H = kPrintRegLaneSizeH | kPrintRegAsQVector,
      kPrintReg1S = kPrintRegLaneSizeS | kPrintRegAsScalar,
      kPrintReg2S = kPrintRegLaneSizeS | kPrintRegAsDVector,
      kPrintReg4S = kPrintRegLaneSizeS | kPrintRegAsQVector,
      kPrintReg1SFP = kPrintRegLaneSizeS | kPrintRegAsScalar | kPrintRegAsFP,
      kPrintReg2SFP = kPrintRegLaneSizeS | kPrintRegAsDVector | kPrintRegAsFP,
      kPrintReg4SFP = kPrintRegLaneSizeS | kPrintRegAsQVector | kPrintRegAsFP,
      kPrintReg1D = kPrintRegLaneSizeD | kPrintRegAsScalar,
      kPrintReg2D = kPrintRegLaneSizeD | kPrintRegAsQVector,
      kPrintReg1DFP = kPrintRegLaneSizeD | kPrintRegAsScalar | kPrintRegAsFP,
      kPrintReg2DFP = kPrintRegLaneSizeD | kPrintRegAsQVector | kPrintRegAsFP,
      kPrintReg1Q = kPrintRegLaneSizeQ | kPrintRegAsScalar
    };
  
    unsigned GetPrintRegLaneSizeInBytesLog2(PrintRegisterFormat format) {
      return (format & kPrintRegLaneSizeMask) >> kPrintRegLaneSizeOffset;
    }
  
    unsigned GetPrintRegLaneSizeInBytes(PrintRegisterFormat format) {
      return 1 << GetPrintRegLaneSizeInBytesLog2(format);
    }
  
    unsigned GetPrintRegSizeInBytesLog2(PrintRegisterFormat format) {
      if (format & kPrintRegAsDVector) return kDRegSizeLog2;
      if (format & kPrintRegAsQVector) return kQRegSizeLog2;
  
      // Scalar types.
      return GetPrintRegLaneSizeInBytesLog2(format);
    }
  
    unsigned GetPrintRegSizeInBytes(PrintRegisterFormat format) {
      return 1 << GetPrintRegSizeInBytesLog2(format);
    }
  
    unsigned GetPrintRegLaneCount(PrintRegisterFormat format) {
      unsigned reg_size_log2 = GetPrintRegSizeInBytesLog2(format);
      unsigned lane_size_log2 = GetPrintRegLaneSizeInBytesLog2(format);
      DCHECK_GE(reg_size_log2, lane_size_log2);
      return 1 << (reg_size_log2 - lane_size_log2);
    }
  
    template <typename T>
    PrintRegisterFormat GetPrintRegisterFormat(T value) {
      return GetPrintRegisterFormatForSize(sizeof(value));
    }
  
    PrintRegisterFormat GetPrintRegisterFormat(double value) {
      static_assert(sizeof(value) == kDRegSize,
                    "D register must be size of double.");
      return GetPrintRegisterFormatForSizeFP(sizeof(value));
    }
  
    PrintRegisterFormat GetPrintRegisterFormat(float value) {
      static_assert(sizeof(value) == kSRegSize,
                    "S register must be size of float.");
      return GetPrintRegisterFormatForSizeFP(sizeof(value));
    }
  
    PrintRegisterFormat GetPrintRegisterFormat(VectorFormat vform);
    PrintRegisterFormat GetPrintRegisterFormatFP(VectorFormat vform);
  
    PrintRegisterFormat GetPrintRegisterFormatForSize(size_t reg_size,
                                                      size_t lane_size);
  
    PrintRegisterFormat GetPrintRegisterFormatForSize(size_t size) {
      return GetPrintRegisterFormatForSize(size, size);
    }
  
    PrintRegisterFormat GetPrintRegisterFormatForSizeFP(size_t size) {
      switch (size) {
        default:
          UNREACHABLE();
        case kDRegSize:
          return kPrintDReg;
        case kSRegSize:
          return kPrintSReg;
      }
    }
  
    PrintRegisterFormat GetPrintRegisterFormatTryFP(PrintRegisterFormat format) {
      if ((GetPrintRegLaneSizeInBytes(format) == kSRegSize) ||
          (GetPrintRegLaneSizeInBytes(format) == kDRegSize)) {
        return static_cast<PrintRegisterFormat>(format | kPrintRegAsFP);
      }
      return format;
    }
  
    // Print individual register values (after update).
    void PrintRegister(unsigned code, Reg31Mode r31mode = Reg31IsStackPointer);
    void PrintVRegister(unsigned code, PrintRegisterFormat sizes);
    void PrintSystemRegister(SystemRegister id);
  
    // Like Print* (above), but respect log_parameters().
    void LogRegister(unsigned code, Reg31Mode r31mode = Reg31IsStackPointer) {
      if (log_parameters() & LOG_REGS) PrintRegister(code, r31mode);
    }
    void LogVRegister(unsigned code, PrintRegisterFormat format) {
      if (log_parameters() & LOG_VREGS) PrintVRegister(code, format);
    }
    void LogSystemRegister(SystemRegister id) {
      if (log_parameters() & LOG_SYS_REGS) PrintSystemRegister(id);
    }
  
    // Print memory accesses.
    void PrintRead(uintptr_t address, unsigned reg_code,
                   PrintRegisterFormat format);
    void PrintWrite(uintptr_t address, unsigned reg_code,
                    PrintRegisterFormat format);
    void PrintVRead(uintptr_t address, unsigned reg_code,
                    PrintRegisterFormat format, unsigned lane);
    void PrintVWrite(uintptr_t address, unsigned reg_code,
                     PrintRegisterFormat format, unsigned lane);
  
    // Like Print* (above), but respect log_parameters().
    void LogRead(uintptr_t address, unsigned reg_code,
                 PrintRegisterFormat format) {
      if (log_parameters() & LOG_REGS) PrintRead(address, reg_code, format);
    }
    void LogWrite(uintptr_t address, unsigned reg_code,
                  PrintRegisterFormat format) {
      if (log_parameters() & LOG_WRITE) PrintWrite(address, reg_code, format);
    }
    void LogVRead(uintptr_t address, unsigned reg_code,
                  PrintRegisterFormat format, unsigned lane = 0) {
      if (log_parameters() & LOG_VREGS) {
        PrintVRead(address, reg_code, format, lane);
      }
    }
    void LogVWrite(uintptr_t address, unsigned reg_code,
                   PrintRegisterFormat format, unsigned lane = 0) {
      if (log_parameters() & LOG_WRITE) {
        PrintVWrite(address, reg_code, format, lane);
      }
    }
  
    int log_parameters() { return log_parameters_; }
    void set_log_parameters(int new_parameters) {
      log_parameters_ = new_parameters;
      if (!decoder_) {
        if (new_parameters & LOG_DISASM) {
          PrintF("Run --debug-sim to dynamically turn on disassembler\n");
        }
        return;
      }
      if (new_parameters & LOG_DISASM) {
        decoder_->InsertVisitorBefore(print_disasm_, this);
      } else {
        decoder_->RemoveVisitor(print_disasm_);
      }
    }
  
    // Helper functions for register tracing.
    void PrintRegisterRawHelper(unsigned code, Reg31Mode r31mode,
                                int size_in_bytes = kXRegSize);
    void PrintVRegisterRawHelper(unsigned code, int bytes = kQRegSize,
                                 int lsb = 0);
    void PrintVRegisterFPHelper(unsigned code, unsigned lane_size_in_bytes,
                                int lane_count = 1, int rightmost_lane = 0);
  
    static inline const char* WRegNameForCode(
        unsigned code, Reg31Mode mode = Reg31IsZeroRegister);
    static inline const char* XRegNameForCode(
        unsigned code, Reg31Mode mode = Reg31IsZeroRegister);
    static inline const char* SRegNameForCode(unsigned code);
    static inline const char* DRegNameForCode(unsigned code);
    static inline const char* VRegNameForCode(unsigned code);
    static inline int CodeFromName(const char* name);
  
    enum PointerType { kDataPointer, kInstructionPointer };
  
    struct PACKey {
      uint64_t high;
      uint64_t low;
      int number;
    };
  
    static const PACKey kPACKeyIB;
  
    // Current implementation is that all pointers are tagged.
    static bool HasTBI(uint64_t ptr, PointerType type) {
      USE(ptr, type);
      return true;
    }
  
    // Current implementation uses 48-bit virtual addresses.
    static int GetBottomPACBit(uint64_t ptr, int ttbr) {
      USE(ptr, ttbr);
      DCHECK((ttbr == 0) || (ttbr == 1));
      return 48;
    }
  
    // The top PAC bit is 55 for the purposes of relative bit fields with TBI,
    // however bit 55 is the TTBR bit regardless of TBI so isn't part of the PAC
    // codes in pointers.
    static int GetTopPACBit(uint64_t ptr, PointerType type) {
      return HasTBI(ptr, type) ? 55 : 63;
    }
  
    // Armv8.3 Pointer authentication helpers.
    V8_EXPORT_PRIVATE static uint64_t CalculatePACMask(uint64_t ptr,
                                                       PointerType type,
                                                       int ext_bit);
    V8_EXPORT_PRIVATE static uint64_t ComputePAC(uint64_t data, uint64_t context,
                                                 PACKey key);
    V8_EXPORT_PRIVATE static uint64_t AuthPAC(uint64_t ptr, uint64_t context,
                                              PACKey key, PointerType type);
    V8_EXPORT_PRIVATE static uint64_t AddPAC(uint64_t ptr, uint64_t context,
                                             PACKey key, PointerType type);
    V8_EXPORT_PRIVATE static uint64_t StripPAC(uint64_t ptr, PointerType type);
  
   protected:
    // Simulation helpers ------------------------------------
    bool ConditionPassed(Condition cond) {
      SimSystemRegister& flags = nzcv();
      switch (cond) {
        case eq:
          return flags.Z();
        case ne:
          return !flags.Z();
        case hs:
          return flags.C();
        case lo:
          return !flags.C();
        case mi:
          return flags.N();
        case pl:
          return !flags.N();
        case vs:
          return flags.V();
        case vc:
          return !flags.V();
        case hi:
          return flags.C() && !flags.Z();
        case ls:
          return !(flags.C() && !flags.Z());
        case ge:
          return flags.N() == flags.V();
        case lt:
          return flags.N() != flags.V();
        case gt:
          return !flags.Z() && (flags.N() == flags.V());
        case le:
          return !(!flags.Z() && (flags.N() == flags.V()));
        case nv:  // Fall through.
        case al:
          return true;
        default:
          UNREACHABLE();
      }
    }
  
    bool ConditionFailed(Condition cond) { return !ConditionPassed(cond); }
  
    template <typename T>
    void AddSubHelper(Instruction* instr, T op2);
    template <typename T>
    T AddWithCarry(bool set_flags, T left, T right, int carry_in = 0);
    template <typename T>
    void AddSubWithCarry(Instruction* instr);
    template <typename T>
    void LogicalHelper(Instruction* instr, T op2);
    template <typename T>
    void ConditionalCompareHelper(Instruction* instr, T op2);
    void LoadStoreHelper(Instruction* instr, int64_t offset, AddrMode addrmode);
    void LoadStorePairHelper(Instruction* instr, AddrMode addrmode);
    uintptr_t LoadStoreAddress(unsigned addr_reg, int64_t offset,
                               AddrMode addrmode);
    void LoadStoreWriteBack(unsigned addr_reg, int64_t offset, AddrMode addrmode);
    void NEONLoadStoreMultiStructHelper(const Instruction* instr,
                                        AddrMode addr_mode);
    void NEONLoadStoreSingleStructHelper(const Instruction* instr,
                                         AddrMode addr_mode);
    void CheckMemoryAccess(uintptr_t address, uintptr_t stack);
  
    // "Probe" if an address range can be read. This is currently implemented
    // by doing a 1-byte read of the last accessed byte, since the assumption is
    // that if the last byte is accessible, also all lower bytes are accessible
    // (which holds true for Wasm).
    // Returns true if the access was successful, false if the access raised a
    // signal which was then handled by the trap handler (also see
    // {trap_handler::ProbeMemory}). If the access raises a signal which is not
    // handled by the trap handler (e.g. because the current PC is not registered
    // as a protected instruction), the signal will propagate and make the process
    // crash. If no trap handler is available, this always returns true.
    bool ProbeMemory(uintptr_t address, uintptr_t access_size);
  
    // Memory read helpers.
    template <typename T, typename A>
    T MemoryRead(A address) {
      T value;
      static_assert((sizeof(value) == 1) || (sizeof(value) == 2) ||
                    (sizeof(value) == 4) || (sizeof(value) == 8) ||
                    (sizeof(value) == 16));
      memcpy(&value, reinterpret_cast<const void*>(address), sizeof(value));
      return value;
    }
  
    // Memory write helpers.
    template <typename T, typename A>
    void MemoryWrite(A address, T value) {
      static_assert((sizeof(value) == 1) || (sizeof(value) == 2) ||
                    (sizeof(value) == 4) || (sizeof(value) == 8) ||
                    (sizeof(value) == 16));
      memcpy(reinterpret_cast<void*>(address), &value, sizeof(value));
    }
  
    template <typename T>
    T ShiftOperand(T value, Shift shift_type, unsigned amount);
    template <typename T>
    T ExtendValue(T value, Extend extend_type, unsigned left_shift = 0);
    template <typename T>
    void Extract(Instruction* instr);
    template <typename T>
    void DataProcessing2Source(Instruction* instr);
    template <typename T>
    void BitfieldHelper(Instruction* instr);
    uint16_t PolynomialMult(uint8_t op1, uint8_t op2);
  
    void ld1(VectorFormat vform, LogicVRegister dst, uint64_t addr);
    void ld1(VectorFormat vform, LogicVRegister dst, int index, uint64_t addr);
    void ld1r(VectorFormat vform, LogicVRegister dst, uint64_t addr);
    void ld2(VectorFormat vform, LogicVRegister dst1, LogicVRegister dst2,
             uint64_t addr);
    void ld2(VectorFormat vform, LogicVRegister dst1, LogicVRegister dst2,
             int index, uint64_t addr);
    void ld2r(VectorFormat vform, LogicVRegister dst1, LogicVRegister dst2,
              uint64_t addr);
    void ld3(VectorFormat vform, LogicVRegister dst1, LogicVRegister dst2,
             LogicVRegister dst3, uint64_t addr);
    void ld3(VectorFormat vform, LogicVRegister dst1, LogicVRegister dst2,
             LogicVRegister dst3, int index, uint64_t addr);
    void ld3r(VectorFormat vform, LogicVRegister dst1, LogicVRegister dst2,
              LogicVRegister dst3, uint64_t addr);
    void ld4(VectorFormat vform, LogicVRegister dst1, LogicVRegister dst2,
             LogicVRegister dst3, LogicVRegister dst4, uint64_t addr);
    void ld4(VectorFormat vform, LogicVRegister dst1, LogicVRegister dst2,
             LogicVRegister dst3, LogicVRegister dst4, int index, uint64_t addr);
    void ld4r(VectorFormat vform, LogicVRegister dst1, LogicVRegister dst2,
              LogicVRegister dst3, LogicVRegister dst4, uint64_t addr);
    void st1(VectorFormat vform, LogicVRegister src, uint64_t addr);
    void st1(VectorFormat vform, LogicVRegister src, int index, uint64_t addr);
    void st2(VectorFormat vform, LogicVRegister src, LogicVRegister src2,
             uint64_t addr);
    void st2(VectorFormat vform, LogicVRegister src, LogicVRegister src2,
             int index, uint64_t addr);
    void st3(VectorFormat vform, LogicVRegister src, LogicVRegister src2,
             LogicVRegister src3, uint64_t addr);
    void st3(VectorFormat vform, LogicVRegister src, LogicVRegister src2,
             LogicVRegister src3, int index, uint64_t addr);
    void st4(VectorFormat vform, LogicVRegister src, LogicVRegister src2,
             LogicVRegister src3, LogicVRegister src4, uint64_t addr);
    void st4(VectorFormat vform, LogicVRegister src, LogicVRegister src2,
             LogicVRegister src3, LogicVRegister src4, int index, uint64_t addr);
    LogicVRegister cmp(VectorFormat vform, LogicVRegister dst,
                       const LogicVRegister& src1, const LogicVRegister& src2,
                       Condition cond);
    LogicVRegister cmp(VectorFormat vform, LogicVRegister dst,
                       const LogicVRegister& src1, int imm, Condition cond);
    LogicVRegister cmptst(VectorFormat vform, LogicVRegister dst,
                          const LogicVRegister& src1, const LogicVRegister& src2);
    LogicVRegister add(VectorFormat vform, LogicVRegister dst,
                       const LogicVRegister& src1, const LogicVRegister& src2);
    LogicVRegister addp(VectorFormat vform, LogicVRegister dst,
                        const LogicVRegister& src1, const LogicVRegister& src2);
    LogicVRegister mla(VectorFormat vform, LogicVRegister dst,
                       const LogicVRegister& src1, const LogicVRegister& src2);
    LogicVRegister mls(VectorFormat vform, LogicVRegister dst,
                       const LogicVRegister& src1, const LogicVRegister& src2);
    LogicVRegister mul(VectorFormat vform, LogicVRegister dst,
                       const LogicVRegister& src1, const LogicVRegister& src2);
    LogicVRegister mul(VectorFormat vform, LogicVRegister dst,
                       const LogicVRegister& src1, const LogicVRegister& src2,
                       int index);
    LogicVRegister mla(VectorFormat vform, LogicVRegister dst,
                       const LogicVRegister& src1, const LogicVRegister& src2,
                       int index);
    LogicVRegister mls(VectorFormat vform, LogicVRegister dst,
                       const LogicVRegister& src1, const LogicVRegister& src2,
                       int index);
    LogicVRegister pmul(VectorFormat vform, LogicVRegister dst,
                        const LogicVRegister& src1, const LogicVRegister& src2);
  
    using ByElementOp = LogicVRegister (Simulator::*)(VectorFormat vform,
                                                      LogicVRegister dst,
                                                      const LogicVRegister& src1,
                                                      const LogicVRegister& src2,
                                                      int index);
    LogicVRegister fmul(VectorFormat vform, LogicVRegister dst,
                        const LogicVRegister& src1, const LogicVRegister& src2,
                        int index);
    LogicVRegister fmla(VectorFormat vform, LogicVRegister dst,
                        const LogicVRegister& src1, const LogicVRegister& src2,
                        int index);
    LogicVRegister fmls(VectorFormat vform, LogicVRegister dst,
                        const LogicVRegister& src1, const LogicVRegister& src2,
                        int index);
    LogicVRegister fmulx(VectorFormat vform, LogicVRegister dst,
                         const LogicVRegister& src1, const LogicVRegister& src2,
                         int index);
    LogicVRegister smull(VectorFormat vform, LogicVRegister dst,
                         const LogicVRegister& src1, const LogicVRegister& src2,
                         int index);
    LogicVRegister smull2(VectorFormat vform, LogicVRegister dst,
                          const LogicVRegister& src1, const LogicVRegister& src2,
                          int index);
    LogicVRegister umull(VectorFormat vform, LogicVRegister dst,
                         const LogicVRegister& src1, const LogicVRegister& src2,
                         int index);
    LogicVRegister umull2(VectorFormat vform, LogicVRegister dst,
                          const LogicVRegister& src1, const LogicVRegister& src2,
                          int index);
    LogicVRegister smlal(VectorFormat vform, LogicVRegister dst,
                         const LogicVRegister& src1, const LogicVRegister& src2,
                         int index);
    LogicVRegister smlal2(VectorFormat vform, LogicVRegister dst,
                          const LogicVRegister& src1, const LogicVRegister& src2,
                          int index);
    LogicVRegister umlal(VectorFormat vform, LogicVRegister dst,
                         const LogicVRegister& src1, const LogicVRegister& src2,
                         int index);
    LogicVRegister umlal2(VectorFormat vform, LogicVRegister dst,
                          const LogicVRegister& src1, const LogicVRegister& src2,
                          int index);
    LogicVRegister smlsl(VectorFormat vform, LogicVRegister dst,
                         const LogicVRegister& src1, const LogicVRegister& src2,
                         int index);
    LogicVRegister smlsl2(VectorFormat vform, LogicVRegister dst,
                          const LogicVRegister& src1, const LogicVRegister& src2,
                          int index);
    LogicVRegister umlsl(VectorFormat vform, LogicVRegister dst,
                         const LogicVRegister& src1, const LogicVRegister& src2,
                         int index);
    LogicVRegister umlsl2(VectorFormat vform, LogicVRegister dst,
                          const LogicVRegister& src1, const LogicVRegister& src2,
                          int index);
    LogicVRegister sqdmull(VectorFormat vform, LogicVRegister dst,
                           const LogicVRegister& src1, const LogicVRegister& src2,
                           int index);
    LogicVRegister sqdmull2(VectorFormat vform, LogicVRegister dst,
                            const LogicVRegister& src1,
                            const LogicVRegister& src2, int index);
    LogicVRegister sqdmlal(VectorFormat vform, LogicVRegister dst,
                           const LogicVRegister& src1, const LogicVRegister& src2,
                           int index);
    LogicVRegister sqdmlal2(VectorFormat vform, LogicVRegister dst,
                            const LogicVRegister& src1,
                            const LogicVRegister& src2, int index);
    LogicVRegister sqdmlsl(VectorFormat vform, LogicVRegister dst,
                           const LogicVRegister& src1, const LogicVRegister& src2,
                           int index);
    LogicVRegister sqdmlsl2(VectorFormat vform, LogicVRegister dst,
                            const LogicVRegister& src1,
                            const LogicVRegister& src2, int index);
    LogicVRegister sqdmulh(VectorFormat vform, LogicVRegister dst,
                           const LogicVRegister& src1, const LogicVRegister& src2,
                           int index);
    LogicVRegister sqrdmulh(VectorFormat vform, LogicVRegister dst,
                            const LogicVRegister& src1,
                            const LogicVRegister& src2, int index);
    LogicVRegister sub(VectorFormat vform, LogicVRegister dst,
                       const LogicVRegister& src1, const LogicVRegister& src2);
    LogicVRegister and_(VectorFormat vform, LogicVRegister dst,
                        const LogicVRegister& src1, const LogicVRegister& src2);
    LogicVRegister orr(VectorFormat vform, LogicVRegister dst,
                       const LogicVRegister& src1, const LogicVRegister& src2);
    LogicVRegister orn(VectorFormat vform, LogicVRegister dst,
                       const LogicVRegister& src1, const LogicVRegister& src2);
    LogicVRegister eor(VectorFormat vform, LogicVRegister dst,
                       const LogicVRegister& src1, const LogicVRegister& src2);
    LogicVRegister bic(VectorFormat vform, LogicVRegister dst,
                       const LogicVRegister& src1, const LogicVRegister& src2);
    LogicVRegister bic(VectorFormat vform, LogicVRegister dst,
                       const LogicVRegister& src, uint64_t imm);
    LogicVRegister bif(VectorFormat vform, LogicVRegister dst,
                       const LogicVRegister& src1, const LogicVRegister& src2);
    LogicVRegister bit(VectorFormat vform, LogicVRegister dst,
                       const LogicVRegister& src1, const LogicVRegister& src2);
    LogicVRegister bsl(VectorFormat vform, LogicVRegister dst,
                       const LogicVRegister& src1, const LogicVRegister& src2);
    LogicVRegister cls(VectorFormat vform, LogicVRegister dst,
                       const LogicVRegister& src);
    LogicVRegister clz(VectorFormat vform, LogicVRegister dst,
                       const LogicVRegister& src);
    LogicVRegister cnt(VectorFormat vform, LogicVRegister dst,
                       const LogicVRegister& src);
    LogicVRegister not_(VectorFormat vform, LogicVRegister dst,
                        const LogicVRegister& src);
    LogicVRegister rbit(VectorFormat vform, LogicVRegister dst,
                        const LogicVRegister& src);
    LogicVRegister rev(VectorFormat vform, LogicVRegister dst,
                       const LogicVRegister& src, int revSize);
    LogicVRegister rev16(VectorFormat vform, LogicVRegister dst,
                         const LogicVRegister& src);
    LogicVRegister rev32(VectorFormat vform, LogicVRegister dst,
                         const LogicVRegister& src);
    LogicVRegister rev64(VectorFormat vform, LogicVRegister dst,
                         const LogicVRegister& src);
    LogicVRegister addlp(VectorFormat vform, LogicVRegister dst,
                         const LogicVRegister& src, bool is_signed,
                         bool do_accumulate);
    LogicVRegister saddlp(VectorFormat vform, LogicVRegister dst,
                          const LogicVRegister& src);
    LogicVRegister uaddlp(VectorFormat vform, LogicVRegister dst,
                          const LogicVRegister& src);
    LogicVRegister sadalp(VectorFormat vform, LogicVRegister dst,
                          const LogicVRegister& src);
    LogicVRegister uadalp(VectorFormat vform, LogicVRegister dst,
                          const LogicVRegister& src);
    LogicVRegister ext(VectorFormat vform, LogicVRegister dst,
                       const LogicVRegister& src1, const LogicVRegister& src2,
                       int index);
    LogicVRegister ins_element(VectorFormat vform, LogicVRegister dst,
                               int dst_index, const LogicVRegister& src,
                               int src_index);
    LogicVRegister ins_immediate(VectorFormat vform, LogicVRegister dst,
                                 int dst_index, uint64_t imm);
    LogicVRegister dup_element(VectorFormat vform, LogicVRegister dst,
                               const LogicVRegister& src, int src_index);
    LogicVRegister dup_immediate(VectorFormat vform, LogicVRegister dst,
                                 uint64_t imm);
    LogicVRegister movi(VectorFormat vform, LogicVRegister dst, uint64_t imm);
    LogicVRegister mvni(VectorFormat vform, LogicVRegister dst, uint64_t imm);
    LogicVRegister orr(VectorFormat vform, LogicVRegister dst,
                       const LogicVRegister& src, uint64_t imm);
    LogicVRegister sshl(VectorFormat vform, LogicVRegister dst,
                        const LogicVRegister& src1, const LogicVRegister& src2);
    LogicVRegister ushl(VectorFormat vform, LogicVRegister dst,
                        const LogicVRegister& src1, const LogicVRegister& src2);
    LogicVRegister SMinMax(VectorFormat vform, LogicVRegister dst,
                           const LogicVRegister& src1, const LogicVRegister& src2,
                           bool max);
    LogicVRegister smax(VectorFormat vform, LogicVRegister dst,
                        const LogicVRegister& src1, const LogicVRegister& src2);
    LogicVRegister smin(VectorFormat vform, LogicVRegister dst,
                        const LogicVRegister& src1, const LogicVRegister& src2);
    LogicVRegister SMinMaxP(VectorFormat vform, LogicVRegister dst,
                            const LogicVRegister& src1,
                            const LogicVRegister& src2, bool max);
    LogicVRegister smaxp(VectorFormat vform, LogicVRegister dst,
                         const LogicVRegister& src1, const LogicVRegister& src2);
    LogicVRegister sminp(VectorFormat vform, LogicVRegister dst,
                         const LogicVRegister& src1, const LogicVRegister& src2);
    LogicVRegister addp(VectorFormat vform, LogicVRegister dst,
                        const LogicVRegister& src);
    LogicVRegister addv(VectorFormat vform, LogicVRegister dst,
                        const LogicVRegister& src);
    LogicVRegister uaddlv(VectorFormat vform, LogicVRegister dst,
                          const LogicVRegister& src);
    LogicVRegister saddlv(VectorFormat vform, LogicVRegister dst,
                          const LogicVRegister& src);
    LogicVRegister SMinMaxV(VectorFormat vform, LogicVRegister dst,
                            const LogicVRegister& src, bool max);
    LogicVRegister smaxv(VectorFormat vform, LogicVRegister dst,
                         const LogicVRegister& src);
    LogicVRegister sminv(VectorFormat vform, LogicVRegister dst,
                         const LogicVRegister& src);
    LogicVRegister uxtl(VectorFormat vform, LogicVRegister dst,
                        const LogicVRegister& src);
    LogicVRegister uxtl2(VectorFormat vform, LogicVRegister dst,
                         const LogicVRegister& src);
    LogicVRegister sxtl(VectorFormat vform, LogicVRegister dst,
                        const LogicVRegister& src);
    LogicVRegister sxtl2(VectorFormat vform, LogicVRegister dst,
                         const LogicVRegister& src);
    LogicVRegister Table(VectorFormat vform, LogicVRegister dst,
                         const LogicVRegister& ind, bool zero_out_of_bounds,
                         const LogicVRegister* tab1,
                         const LogicVRegister* tab2 = nullptr,
                         const LogicVRegister* tab3 = nullptr,
                         const LogicVRegister* tab4 = nullptr);
    LogicVRegister tbl(VectorFormat vform, LogicVRegister dst,
                       const LogicVRegister& tab, const LogicVRegister& ind);
    LogicVRegister tbl(VectorFormat vform, LogicVRegister dst,
                       const LogicVRegister& tab, const LogicVRegister& tab2,
                       const LogicVRegister& ind);
    LogicVRegister tbl(VectorFormat vform, LogicVRegister dst,
                       const LogicVRegister& tab, const LogicVRegister& tab2,
                       const LogicVRegister& tab3, const LogicVRegister& ind);
    LogicVRegister tbl(VectorFormat vform, LogicVRegister dst,
                       const LogicVRegister& tab, const LogicVRegister& tab2,
                       const LogicVRegister& tab3, const LogicVRegister& tab4,
                       const LogicVRegister& ind);
    LogicVRegister tbx(VectorFormat vform, LogicVRegister dst,
                       const LogicVRegister& tab, const LogicVRegister& ind);
    LogicVRegister tbx(VectorFormat vform, LogicVRegister dst,
                       const LogicVRegister& tab, const LogicVRegister& tab2,
                       const LogicVRegister& ind);
    LogicVRegister tbx(VectorFormat vform, LogicVRegister dst,
                       const LogicVRegister& tab, const LogicVRegister& tab2,
                       const LogicVRegister& tab3, const LogicVRegister& ind);
    LogicVRegister tbx(VectorFormat vform, LogicVRegister dst,
                       const LogicVRegister& tab, const LogicVRegister& tab2,
                       const LogicVRegister& tab3, const LogicVRegister& tab4,
                       const LogicVRegister& ind);
    LogicVRegister uaddl(VectorFormat vform, LogicVRegister dst,
                         const LogicVRegister& src1, const LogicVRegister& src2);
    LogicVRegister uaddl2(VectorFormat vform, LogicVRegister dst,
                          const LogicVRegister& src1, const LogicVRegister& src2);
    LogicVRegister uaddw(VectorFormat vform, LogicVRegister dst,
                         const LogicVRegister& src1, const LogicVRegister& src2);
    LogicVRegister uaddw2(VectorFormat vform, LogicVRegister dst,
                          const LogicVRegister& src1, const LogicVRegister& src2);
    LogicVRegister saddl(VectorFormat vform, LogicVRegister dst,
                         const LogicVRegister& src1, const LogicVRegister& src2);
    LogicVRegister saddl2(VectorFormat vform, LogicVRegister dst,
                          const LogicVRegister& src1, const LogicVRegister& src2);
    LogicVRegister saddw(VectorFormat vform, LogicVRegister dst,
                         const LogicVRegister& src1, const LogicVRegister& src2);
    LogicVRegister saddw2(VectorFormat vform, LogicVRegister dst,
                          const LogicVRegister& src1, const LogicVRegister& src2);
    LogicVRegister usubl(VectorFormat vform, LogicVRegister dst,
                         const LogicVRegister& src1, const LogicVRegister& src2);
    LogicVRegister usubl2(VectorFormat vform, LogicVRegister dst,
                          const LogicVRegister& src1, const LogicVRegister& src2);
    LogicVRegister usubw(VectorFormat vform, LogicVRegister dst,
                         const LogicVRegister& src1, const LogicVRegister& src2);
    LogicVRegister usubw2(VectorFormat vform, LogicVRegister dst,
                          const LogicVRegister& src1, const LogicVRegister& src2);
    LogicVRegister ssubl(VectorFormat vform, LogicVRegister dst,
                         const LogicVRegister& src1, const LogicVRegister& src2);
    LogicVRegister ssubl2(VectorFormat vform, LogicVRegister dst,
                          const LogicVRegister& src1, const LogicVRegister& src2);
    LogicVRegister ssubw(VectorFormat vform, LogicVRegister dst,
                         const LogicVRegister& src1, const LogicVRegister& src2);
    LogicVRegister ssubw2(VectorFormat vform, LogicVRegister dst,
                          const LogicVRegister& src1, const LogicVRegister& src2);
    LogicVRegister UMinMax(VectorFormat vform, LogicVRegister dst,
                           const LogicVRegister& src1, const LogicVRegister& src2,
                           bool max);
    LogicVRegister umax(VectorFormat vform, LogicVRegister dst,
                        const LogicVRegister& src1, const LogicVRegister& src2);
    LogicVRegister umin(VectorFormat vform, LogicVRegister dst,
                        const LogicVRegister& src1, const LogicVRegister& src2);
    LogicVRegister UMinMaxP(VectorFormat vform, LogicVRegister dst,
                            const LogicVRegister& src1,
                            const LogicVRegister& src2, bool max);
    LogicVRegister umaxp(VectorFormat vform, LogicVRegister dst,
                         const LogicVRegister& src1, const LogicVRegister& src2);
    LogicVRegister uminp(VectorFormat vform, LogicVRegister dst,
                         const LogicVRegister& src1, const LogicVRegister& src2);
    LogicVRegister UMinMaxV(VectorFormat vform, LogicVRegister dst,
                            const LogicVRegister& src, bool max);
    LogicVRegister umaxv(VectorFormat vform, LogicVRegister dst,
                         const LogicVRegister& src);
    LogicVRegister uminv(VectorFormat vform, LogicVRegister dst,
                         const LogicVRegister& src);
    LogicVRegister trn1(VectorFormat vform, LogicVRegister dst,
                        const LogicVRegister& src1, const LogicVRegister& src2);
    LogicVRegister trn2(VectorFormat vform, LogicVRegister dst,
                        const LogicVRegister& src1, const LogicVRegister& src2);
    LogicVRegister zip1(VectorFormat vform, LogicVRegister dst,
                        const LogicVRegister& src1, const LogicVRegister& src2);
    LogicVRegister zip2(VectorFormat vform, LogicVRegister dst,
                        const LogicVRegister& src1, const LogicVRegister& src2);
    LogicVRegister uzp1(VectorFormat vform, LogicVRegister dst,
                        const LogicVRegister& src1, const LogicVRegister& src2);
    LogicVRegister uzp2(VectorFormat vform, LogicVRegister dst,
                        const LogicVRegister& src1, const LogicVRegister& src2);
    LogicVRegister shl(VectorFormat vform, LogicVRegister dst,
                       const LogicVRegister& src, int shift);
    LogicVRegister scvtf(VectorFormat vform, LogicVRegister dst,
                         const LogicVRegister& src, int fbits,
                         FPRounding rounding_mode);
    LogicVRegister ucvtf(VectorFormat vform, LogicVRegister dst,
                         const LogicVRegister& src, int fbits,
                         FPRounding rounding_mode);
    LogicVRegister sshll(VectorFormat vform, LogicVRegister dst,
                         const LogicVRegister& src, int shift);
    LogicVRegister sshll2(VectorFormat vform, LogicVRegister dst,
                          const LogicVRegister& src, int shift);
    LogicVRegister shll(VectorFormat vform, LogicVRegister dst,
                        const LogicVRegister& src);
    LogicVRegister shll2(VectorFormat vform, LogicVRegister dst,
                         const LogicVRegister& src);
    LogicVRegister ushll(VectorFormat vform, LogicVRegister dst,
                         const LogicVRegister& src, int shift);
    LogicVRegister ushll2(VectorFormat vform, LogicVRegister dst,
                          const LogicVRegister& src, int shift);
    LogicVRegister sli(VectorFormat vform, LogicVRegister dst,
                       const LogicVRegister& src, int shift);
    LogicVRegister sri(VectorFormat vform, LogicVRegister dst,
                       const LogicVRegister& src, int shift);
    LogicVRegister sshr(VectorFormat vform, LogicVRegister dst,
                        const LogicVRegister& src, int shift);
    LogicVRegister ushr(VectorFormat vform, LogicVRegister dst,
                        const LogicVRegister& src, int shift);
    LogicVRegister ssra(VectorFormat vform, LogicVRegister dst,
                        const LogicVRegister& src, int shift);
    LogicVRegister usra(VectorFormat vform, LogicVRegister dst,
                        const LogicVRegister& src, int shift);
    LogicVRegister srsra(VectorFormat vform, LogicVRegister dst,
                         const LogicVRegister& src, int shift);
    LogicVRegister ursra(VectorFormat vform, LogicVRegister dst,
                         const LogicVRegister& src, int shift);
    LogicVRegister suqadd(VectorFormat vform, LogicVRegister dst,
                          const LogicVRegister& src);
    LogicVRegister usqadd(VectorFormat vform, LogicVRegister dst,
                          const LogicVRegister& src);
    LogicVRegister sqshl(VectorFormat vform, LogicVRegister dst,
                         const LogicVRegister& src, int shift);
    LogicVRegister uqshl(VectorFormat vform, LogicVRegister dst,
                         const LogicVRegister& src, int shift);
    LogicVRegister sqshlu(VectorFormat vform, LogicVRegister dst,
                          const LogicVRegister& src, int shift);
    LogicVRegister abs(VectorFormat vform, LogicVRegister dst,
                       const LogicVRegister& src);
    LogicVRegister neg(VectorFormat vform, LogicVRegister dst,
                       const LogicVRegister& src);
    LogicVRegister ExtractNarrow(VectorFormat vform, LogicVRegister dst,
                                 bool dstIsSigned, const LogicVRegister& src,
                                 bool srcIsSigned);
    LogicVRegister xtn(VectorFormat vform, LogicVRegister dst,
                       const LogicVRegister& src);
    LogicVRegister sqxtn(VectorFormat vform, LogicVRegister dst,
                         const LogicVRegister& src);
    LogicVRegister uqxtn(VectorFormat vform, LogicVRegister dst,
                         const LogicVRegister& src);
    LogicVRegister sqxtun(VectorFormat vform, LogicVRegister dst,
                          const LogicVRegister& src);
    LogicVRegister AbsDiff(VectorFormat vform, LogicVRegister dst,
                           const LogicVRegister& src1, const LogicVRegister& src2,
                           bool issigned);
    LogicVRegister saba(VectorFormat vform, LogicVRegister dst,
                        const LogicVRegister& src1, const LogicVRegister& src2);
    LogicVRegister uaba(VectorFormat vform, LogicVRegister dst,
                        const LogicVRegister& src1, const LogicVRegister& src2);
    LogicVRegister shrn(VectorFormat vform, LogicVRegister dst,
                        const LogicVRegister& src, int shift);
    LogicVRegister shrn2(VectorFormat vform, LogicVRegister dst,
                         const LogicVRegister& src, int shift);
    LogicVRegister rshrn(VectorFormat vform, LogicVRegister dst,
                         const LogicVRegister& src, int shift);
    LogicVRegister rshrn2(VectorFormat vform, LogicVRegister dst,
                          const LogicVRegister& src, int shift);
    LogicVRegister uqshrn(VectorFormat vform, LogicVRegister dst,
                          const LogicVRegister& src, int shift);
    LogicVRegister uqshrn2(VectorFormat vform, LogicVRegister dst,
                           const LogicVRegister& src, int shift);
    LogicVRegister uqrshrn(VectorFormat vform, LogicVRegister dst,
                           const LogicVRegister& src, int shift);
    LogicVRegister uqrshrn2(VectorFormat vform, LogicVRegister dst,
                            const LogicVRegister& src, int shift);
    LogicVRegister sqshrn(VectorFormat vform, LogicVRegister dst,
                          const LogicVRegister& src, int shift);
    LogicVRegister sqshrn2(VectorFormat vform, LogicVRegister dst,
                           const LogicVRegister& src, int shift);
    LogicVRegister sqrshrn(VectorFormat vform, LogicVRegister dst,
                           const LogicVRegister& src, int shift);
    LogicVRegister sqrshrn2(VectorFormat vform, LogicVRegister dst,
                            const LogicVRegister& src, int shift);
    LogicVRegister sqshrun(VectorFormat vform, LogicVRegister dst,
                           const LogicVRegister& src, int shift);
    LogicVRegister sqshrun2(VectorFormat vform, LogicVRegister dst,
                            const LogicVRegister& src, int shift);
    LogicVRegister sqrshrun(VectorFormat vform, LogicVRegister dst,
                            const LogicVRegister& src, int shift);
    LogicVRegister sqrshrun2(VectorFormat vform, LogicVRegister dst,
                             const LogicVRegister& src, int shift);
    LogicVRegister sqrdmulh(VectorFormat vform, LogicVRegister dst,
                            const LogicVRegister& src1,
                            const LogicVRegister& src2, bool round = true);
    LogicVRegister sqdmulh(VectorFormat vform, LogicVRegister dst,
                           const LogicVRegister& src1,
                           const LogicVRegister& src2);
  #define NEON_3VREG_LOGIC_LIST(V) \
    V(addhn)                       \
    V(addhn2)                      \
    V(raddhn)                      \
    V(raddhn2)                     \
    V(subhn)                       \
    V(subhn2)                      \
    V(rsubhn)                      \
    V(rsubhn2)                     \
    V(pmull)                       \
    V(pmull2)                      \
    V(sabal)                       \
    V(sabal2)                      \
    V(uabal)                       \
    V(uabal2)                      \
    V(sabdl)                       \
    V(sabdl2)                      \
    V(uabdl)                       \
    V(uabdl2)                      \
    V(smull)                       \
    V(smull2)                      \
    V(umull)                       \
    V(umull2)                      \
    V(smlal)                       \
    V(smlal2)                      \
    V(umlal)                       \
    V(umlal2)                      \
    V(smlsl)                       \
    V(smlsl2)                      \
    V(umlsl)                       \
    V(umlsl2)                      \
    V(sqdmlal)                     \
    V(sqdmlal2)                    \
    V(sqdmlsl)                     \
    V(sqdmlsl2)                    \
    V(sqdmull)                     \
    V(sqdmull2)
  
  #define DEFINE_LOGIC_FUNC(FXN)                               \
    LogicVRegister FXN(VectorFormat vform, LogicVRegister dst, \
                       const LogicVRegister& src1, const LogicVRegister& src2);
    NEON_3VREG_LOGIC_LIST(DEFINE_LOGIC_FUNC)
  #undef DEFINE_LOGIC_FUNC
  
  #define NEON_FP3SAME_LIST(V) \
    V(fadd, FPAdd, false)      \
    V(fsub, FPSub, true)       \
    V(fmul, FPMul, true)       \
    V(fmulx, FPMulx, true)     \
    V(fdiv, FPDiv, true)       \
    V(fmax, FPMax, false)      \
    V(fmin, FPMin, false)      \
    V(fmaxnm, FPMaxNM, false)  \
    V(fminnm, FPMinNM, false)
  
  #define DECLARE_NEON_FP_VECTOR_OP(FN, OP, PROCNAN)                           \
    template <typename T>                                                      \
    LogicVRegister FN(VectorFormat vform, LogicVRegister dst,                  \
                      const LogicVRegister& src1, const LogicVRegister& src2); \
    LogicVRegister FN(VectorFormat vform, LogicVRegister dst,                  \
                      const LogicVRegister& src1, const LogicVRegister& src2);
    NEON_FP3SAME_LIST(DECLARE_NEON_FP_VECTOR_OP)
  #undef DECLARE_NEON_FP_VECTOR_OP
  
  #define NEON_FPPAIRWISE_LIST(V) \
    V(faddp, fadd, FPAdd)         \
    V(fmaxp, fmax, FPMax)         \
    V(fmaxnmp, fmaxnm, FPMaxNM)   \
    V(fminp, fmin, FPMin)         \
    V(fminnmp, fminnm, FPMinNM)
  
  #define DECLARE_NEON_FP_PAIR_OP(FNP, FN, OP)                                  \
    LogicVRegister FNP(VectorFormat vform, LogicVRegister dst,                  \
                       const LogicVRegister& src1, const LogicVRegister& src2); \
    LogicVRegister FNP(VectorFormat vform, LogicVRegister dst,                  \
                       const LogicVRegister& src);
    NEON_FPPAIRWISE_LIST(DECLARE_NEON_FP_PAIR_OP)
  #undef DECLARE_NEON_FP_PAIR_OP
  
    template <typename T>
    LogicVRegister frecps(VectorFormat vform, LogicVRegister dst,
                          const LogicVRegister& src1, const LogicVRegister& src2);
    LogicVRegister frecps(VectorFormat vform, LogicVRegister dst,
                          const LogicVRegister& src1, const LogicVRegister& src2);
    template <typename T>
    LogicVRegister frsqrts(VectorFormat vform, LogicVRegister dst,
                           const LogicVRegister& src1,
                           const LogicVRegister& src2);
    LogicVRegister frsqrts(VectorFormat vform, LogicVRegister dst,
                           const LogicVRegister& src1,
                           const LogicVRegister& src2);
    template <typename T>
    LogicVRegister fmla(VectorFormat vform, LogicVRegister dst,
                        const LogicVRegister& src1, const LogicVRegister& src2);
    LogicVRegister fmla(VectorFormat vform, LogicVRegister dst,
                        const LogicVRegister& src1, const LogicVRegister& src2);
    template <typename T>
    LogicVRegister fmls(VectorFormat vform, LogicVRegister dst,
                        const LogicVRegister& src1, const LogicVRegister& src2);
    LogicVRegister fmls(VectorFormat vform, LogicVRegister dst,
                        const LogicVRegister& src1, const LogicVRegister& src2);
    LogicVRegister fnmul(VectorFormat vform, LogicVRegister dst,
                         const LogicVRegister& src1, const LogicVRegister& src2);
  
    template <typename T>
    LogicVRegister fcmp(VectorFormat vform, LogicVRegister dst,
                        const LogicVRegister& src1, const LogicVRegister& src2,
                        Condition cond);
    LogicVRegister fcmp(VectorFormat vform, LogicVRegister dst,
                        const LogicVRegister& src1, const LogicVRegister& src2,
                        Condition cond);
    LogicVRegister fabscmp(VectorFormat vform, LogicVRegister dst,
                           const LogicVRegister& src1, const LogicVRegister& src2,
                           Condition cond);
    LogicVRegister fcmp_zero(VectorFormat vform, LogicVRegister dst,
                             const LogicVRegister& src, Condition cond);
  
    template <typename T>
    LogicVRegister fneg(VectorFormat vform, LogicVRegister dst,
                        const LogicVRegister& src);
    LogicVRegister fneg(VectorFormat vform, LogicVRegister dst,
                        const LogicVRegister& src);
    template <typename T>
    LogicVRegister frecpx(VectorFormat vform, LogicVRegister dst,
                          const LogicVRegister& src);
    LogicVRegister frecpx(VectorFormat vform, LogicVRegister dst,
                          const LogicVRegister& src);
    template <typename T>
    LogicVRegister fabs_(VectorFormat vform, LogicVRegister dst,
                         const LogicVRegister& src);
    LogicVRegister fabs_(VectorFormat vform, LogicVRegister dst,
                         const LogicVRegister& src);
    LogicVRegister fabd(VectorFormat vform, LogicVRegister dst,
                        const LogicVRegister& src1, const LogicVRegister& src2);
    LogicVRegister frint(VectorFormat vform, LogicVRegister dst,
                         const LogicVRegister& src, FPRounding rounding_mode,
                         bool inexact_exception = false);
    LogicVRegister fcvts(VectorFormat vform, LogicVRegister dst,
                         const LogicVRegister& src, FPRounding rounding_mode,
                         int fbits = 0);
    LogicVRegister fcvtu(VectorFormat vform, LogicVRegister dst,
                         const LogicVRegister& src, FPRounding rounding_mode,
                         int fbits = 0);
    LogicVRegister fcvtl(VectorFormat vform, LogicVRegister dst,
                         const LogicVRegister& src);
    LogicVRegister fcvtl2(VectorFormat vform, LogicVRegister dst,
                          const LogicVRegister& src);
    LogicVRegister fcvtn(VectorFormat vform, LogicVRegister dst,
                         const LogicVRegister& src);
    LogicVRegister fcvtn2(VectorFormat vform, LogicVRegister dst,
                          const LogicVRegister& src);
    LogicVRegister fcvtxn(VectorFormat vform, LogicVRegister dst,
                          const LogicVRegister& src);
    LogicVRegister fcvtxn2(VectorFormat vform, LogicVRegister dst,
                           const LogicVRegister& src);
    LogicVRegister fsqrt(VectorFormat vform, LogicVRegister dst,
                         const LogicVRegister& src);
    LogicVRegister frsqrte(VectorFormat vform, LogicVRegister dst,
                           const LogicVRegister& src);
    LogicVRegister frecpe(VectorFormat vform, LogicVRegister dst,
                          const LogicVRegister& src, FPRounding rounding);
    LogicVRegister ursqrte(VectorFormat vform, LogicVRegister dst,
                           const LogicVRegister& src);
    LogicVRegister urecpe(VectorFormat vform, LogicVRegister dst,
                          const LogicVRegister& src);
  
    using FPMinMaxOp = float (Simulator::*)(float a, float b);
  
    LogicVRegister FMinMaxV(VectorFormat vform, LogicVRegister dst,
                            const LogicVRegister& src, FPMinMaxOp Op);
  
    LogicVRegister fminv(VectorFormat vform, LogicVRegister dst,
                         const LogicVRegister& src);
    LogicVRegister fmaxv(VectorFormat vform, LogicVRegister dst,
                         const LogicVRegister& src);
    LogicVRegister fminnmv(VectorFormat vform, LogicVRegister dst,
                           const LogicVRegister& src);
    LogicVRegister fmaxnmv(VectorFormat vform, LogicVRegister dst,
                           const LogicVRegister& src);
  
    template <typename T>
    T FPRecipSqrtEstimate(T op);
    template <typename T>
    T FPRecipEstimate(T op, FPRounding rounding);
    template <typename T, typename R>
    R FPToFixed(T op, int fbits, bool is_signed, FPRounding rounding);
  
    void FPCompare(double val0, double val1);
    double FPRoundInt(double value, FPRounding round_mode);
    double FPToDouble(float value);
    float FPToFloat(double value, FPRounding round_mode);
    float FPToFloat(float16 value);
    float16 FPToFloat16(float value, FPRounding round_mode);
    float16 FPToFloat16(double value, FPRounding round_mode);
    double recip_sqrt_estimate(double a);
    double recip_estimate(double a);
    double FPRecipSqrtEstimate(double a);
    double FPRecipEstimate(double a);
    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);
    int32_t FPToFixedJS(double value);
  
    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 FPMulx(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);
  
    template <typename T>
    T FPRecipStepFused(T op1, T op2);
  
    template <typename T>
    T FPRSqrtStepFused(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.
    bool FPProcessNaNs(Instruction* instr);
  
    void CheckStackAlignment();
  
    inline void CheckPCSComplianceAndRun();
  
  #ifdef DEBUG
    // Corruption values should have their least significant byte cleared to
    // allow the code of the register being corrupted to be inserted.
    static const uint64_t kCallerSavedRegisterCorruptionValue =
        0xca11edc0de000000UL;
    // This value is a NaN in both 32-bit and 64-bit FP.
    static const uint64_t kCallerSavedVRegisterCorruptionValue =
        0x7ff000007f801000UL;
    // This value is a mix of 32/64-bits NaN and "verbose" immediate.
    static const uint64_t kDefaultCPURegisterCorruptionValue =
        0x7ffbad007f8bad00UL;
  
    void CorruptRegisters(CPURegList* list,
                          uint64_t value = kDefaultCPURegisterCorruptionValue);
    void CorruptAllCallerSavedCPURegisters();
  #endif
  
    // Pseudo Printf instruction
    void DoPrintf(Instruction* instr);
  
    // Processor state ---------------------------------------
  
    // Output stream.
    FILE* stream_;
    PrintDisassembler* print_disasm_;
    void PRINTF_FORMAT(2, 3) TraceSim(const char* format, ...);
  
    // General purpose registers. Register 31 is the stack pointer.
    SimRegister registers_[kNumberOfRegisters];
  
    // Floating point registers
    SimVRegister vregisters_[kNumberOfVRegisters];
  
    // Processor state
    // 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() {
      DCHECK_EQ(fpcr().FZ(), 0);            // No flush-to-zero support.
      DCHECK(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.
    }
  
    template <typename T>
    static int CalcNFlag(T result) {
      return (result >> (sizeof(T) * 8 - 1)) & 1;
    }
  
    static int CalcZFlag(uint64_t result) { return result == 0; }
  
    static const uint32_t kConditionFlagsMask = 0xf0000000;
  
    // Stack
    uintptr_t stack_;
    static const size_t stack_protection_size_ = KB;
    size_t stack_size_;
    uintptr_t stack_limit_;
  
    Decoder<DispatchingDecoderVisitor>* decoder_;
    Decoder<DispatchingDecoderVisitor>* disassembler_decoder_;
  
    // Indicates if the pc has been modified by the instruction and should not be
    // automatically incremented.
    bool pc_modified_;
    Instruction* pc_;
  
    // Branch type register, used for branch target identification.
    BType btype_;
  
    // Global flag for enabling guarded pages.
    // TODO(arm64): implement guarding at page granularity, rather than globally.
    bool guard_pages_;
  
    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[];
  
    // Debugger input.
    void set_last_debugger_input(ArrayUniquePtr<char> input) {
      last_debugger_input_ = std::move(input);
    }
    const char* last_debugger_input() { return last_debugger_input_.get(); }
    ArrayUniquePtr<char> last_debugger_input_;
  
    // Synchronization primitives. See ARM DDI 0487A.a, B2.10. Pair types not
    // implemented.
    enum class MonitorAccess {
      Open,
      Exclusive,
    };
  
    enum class TransactionSize {
      None = 0,
      Byte = 1,
      HalfWord = 2,
      Word = 4,
      DoubleWord = 8,
    };
  
    TransactionSize get_transaction_size(unsigned size);
  
    // The least-significant bits of the address are ignored. The number of bits
    // is implementation-defined, between 3 and 11. See ARM DDI 0487A.a, B2.10.3.
    static const uintptr_t kExclusiveTaggedAddrMask = ~((1 << 11) - 1);
  
    class LocalMonitor {
     public:
      LocalMonitor();
  
      // These functions manage the state machine for the local monitor, but do
      // not actually perform loads and stores. NotifyStoreExcl only returns
      // true if the exclusive store is allowed; the global monitor will still
      // have to be checked to see whether the memory should be updated.
      void NotifyLoad();
      void NotifyLoadExcl(uintptr_t addr, TransactionSize size);
      void NotifyStore();
      bool NotifyStoreExcl(uintptr_t addr, TransactionSize size);
  
     private:
      void Clear();
  
      MonitorAccess access_state_;
      uintptr_t tagged_addr_;
      TransactionSize size_;
    };
  
    class GlobalMonitor {
     public:
      class Processor {
       public:
        Processor();
  
       private:
        friend class GlobalMonitor;
        // These functions manage the state machine for the global monitor, but do
        // not actually perform loads and stores.
        void Clear_Locked();
        void NotifyLoadExcl_Locked(uintptr_t addr);
        void NotifyStore_Locked(bool is_requesting_processor);
        bool NotifyStoreExcl_Locked(uintptr_t addr, bool is_requesting_processor);
  
        MonitorAccess access_state_;
        uintptr_t tagged_addr_;
        Processor* next_;
        Processor* prev_;
        // A stxr can fail due to background cache evictions. Rather than
        // simulating this, we'll just occasionally introduce cases where an
        // exclusive store fails. This will happen once after every
        // kMaxFailureCounter exclusive stores.
        static const int kMaxFailureCounter = 5;
        int failure_counter_;
      };
  
      // Exposed so it can be accessed by Simulator::{Read,Write}Ex*.
      base::Mutex mutex;
  
      void NotifyLoadExcl_Locked(uintptr_t addr, Processor* processor);
      void NotifyStore_Locked(Processor* processor);
      bool NotifyStoreExcl_Locked(uintptr_t addr, Processor* processor);
  
      // Called when the simulator is destroyed.
      void RemoveProcessor(Processor* processor);
  
      static GlobalMonitor* Get();
  
     private:
      // Private constructor. Call {GlobalMonitor::Get()} to get the singleton.
      GlobalMonitor() = default;
      friend class base::LeakyObject<GlobalMonitor>;
  
      bool IsProcessorInLinkedList_Locked(Processor* processor) const;
      void PrependProcessor_Locked(Processor* processor);
  
      Processor* head_ = nullptr;
    };
  
    LocalMonitor local_monitor_;
    GlobalMonitor::Processor global_monitor_processor_;
  
   private:
    void Init(FILE* stream);
  
    V8_EXPORT_PRIVATE void CallImpl(Address entry, CallArgument* args);
  
    void CallAnyCTypeFunction(Address target_address,
                              const EncodedCSignature& signature);
  
    // Read floating point return values.
    template <typename T>
    typename std::enable_if<std::is_floating_point<T>::value, T>::type
    ReadReturn() {
      return static_cast<T>(dreg(0));
    }
    // Read non-float return values.
    template <typename T>
    typename std::enable_if<!std::is_floating_point<T>::value, T>::type
    ReadReturn() {
      return ConvertReturn<T>(xreg(0));
    }
  
    template <typename T>
    static T FPDefaultNaN();
  
    template <typename T>
    T FPProcessNaN(T op) {
      DCHECK(std::isnan(op));
      return fpcr().DN() ? FPDefaultNaN<T>() : ToQuietNaN(op);
    }
  
    template <typename T>
    T FPProcessNaNs(T op1, T op2) {
      if (IsSignallingNaN(op1)) {
        return FPProcessNaN(op1);
      } else if (IsSignallingNaN(op2)) {
        return FPProcessNaN(op2);
      } else if (std::isnan(op1)) {
        DCHECK(IsQuietNaN(op1));
        return FPProcessNaN(op1);
      } else if (std::isnan(op2)) {
        DCHECK(IsQuietNaN(op2));
        return FPProcessNaN(op2);
      } else {
        return 0.0;
      }
    }
  
    template <typename T>
    T FPProcessNaNs3(T op1, T op2, T op3) {
      if (IsSignallingNaN(op1)) {
        return FPProcessNaN(op1);
      } else if (IsSignallingNaN(op2)) {
        return FPProcessNaN(op2);
      } else if (IsSignallingNaN(op3)) {
        return FPProcessNaN(op3);
      } else if (std::isnan(op1)) {
        DCHECK(IsQuietNaN(op1));
        return FPProcessNaN(op1);
      } else if (std::isnan(op2)) {
        DCHECK(IsQuietNaN(op2));
        return FPProcessNaN(op2);
      } else if (std::isnan(op3)) {
        DCHECK(IsQuietNaN(op3));
        return FPProcessNaN(op3);
      } else {
        return 0.0;
      }
    }
  
    int log_parameters_;
    // Instruction counter only valid if FLAG_stop_sim_at isn't 0.
    int icount_for_stop_sim_at_;
    Isolate* isolate_;
  };
  
  template <>
  inline double Simulator::FPDefaultNaN<double>() {
    return kFP64DefaultNaN;
  }
  
  template <>
  inline float Simulator::FPDefaultNaN<float>() {
    return kFP32DefaultNaN;
  }
  
  }  // namespace internal
  }  // namespace v8
  
  #endif  // defined(USE_SIMULATOR)
  #endif  // V8_EXECUTION_ARM64_SIMULATOR_ARM64_H_
  