C++程序  |  669行  |  21.04 KB

// Copyright 2013, ARM Limited
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//   * Redistributions of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//   * Neither the name of ARM Limited nor the names of its contributors may be
//     used to endorse or promote products derived from this software without
//     specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#ifndef VIXL_A64_SIMULATOR_A64_H_
#define VIXL_A64_SIMULATOR_A64_H_

#include "globals-vixl.h"
#include "utils-vixl.h"
#include "a64/instructions-a64.h"
#include "a64/assembler-a64.h"
#include "a64/disasm-a64.h"
#include "a64/instrument-a64.h"

namespace vixl {

enum ReverseByteMode {
  Reverse16 = 0,
  Reverse32 = 1,
  Reverse64 = 2
};

// Printf. See debugger-a64.h for more information on pseudo instructions.
//  - arg_count: The number of arguments.
//  - arg_pattern: A set of PrintfArgPattern values, packed into two-bit fields.
//
// Simulate a call to printf.
//
// Floating-point and integer arguments are passed in separate sets of registers
// in AAPCS64 (even for varargs functions), so it is not possible to determine
// the type of each argument without some information about the values that were
// passed in. This information could be retrieved from the printf format string,
// but the format string is not trivial to parse so we encode the relevant
// information with the HLT instruction.
//
// The interface is as follows:
//    x0: The format string
// x1-x7: Optional arguments, if type == CPURegister::kRegister
// d0-d7: Optional arguments, if type == CPURegister::kFPRegister
const Instr kPrintfOpcode = 0xdeb1;
const unsigned kPrintfArgCountOffset = 1 * kInstructionSize;
const unsigned kPrintfArgPatternListOffset = 2 * kInstructionSize;
const unsigned kPrintfLength = 3 * kInstructionSize;

const unsigned kPrintfMaxArgCount = 4;

// The argument pattern is a set of two-bit-fields, each with one of the
// following values:
enum PrintfArgPattern {
  kPrintfArgW = 1,
  kPrintfArgX = 2,
  // There is no kPrintfArgS because floats are always converted to doubles in C
  // varargs calls.
  kPrintfArgD = 3
};
static const unsigned kPrintfArgPatternBits = 2;


// The proper way to initialize a simulated system register (such as NZCV) is as
// follows:
//  SimSystemRegister nzcv = SimSystemRegister::DefaultValueFor(NZCV);
class SimSystemRegister {
 public:
  // The default constructor represents a register which has no writable bits.
  // It is not possible to set its value to anything other than 0.
  SimSystemRegister() : value_(0), write_ignore_mask_(0xffffffff) { }

  inline uint32_t RawValue() const {
    return value_;
  }

  inline void SetRawValue(uint32_t new_value) {
    value_ = (value_ & write_ignore_mask_) | (new_value & ~write_ignore_mask_);
  }

  inline uint32_t Bits(int msb, int lsb) const {
    return unsigned_bitextract_32(msb, lsb, value_);
  }

  inline int32_t SignedBits(int msb, int lsb) const {
    return signed_bitextract_32(msb, lsb, value_);
  }

  void SetBits(int msb, int lsb, uint32_t bits);

  // Default system register values.
  static SimSystemRegister DefaultValueFor(SystemRegister id);

#define DEFINE_GETTER(Name, HighBit, LowBit, Func)                            \
  inline uint32_t Name() const { return Func(HighBit, LowBit); }              \
  inline void Set##Name(uint32_t bits) { SetBits(HighBit, LowBit, bits); }
#define DEFINE_WRITE_IGNORE_MASK(Name, Mask)                                  \
  static const uint32_t Name##WriteIgnoreMask = ~static_cast<uint32_t>(Mask);

  SYSTEM_REGISTER_FIELDS_LIST(DEFINE_GETTER, DEFINE_WRITE_IGNORE_MASK)

#undef DEFINE_ZERO_BITS
#undef DEFINE_GETTER

 protected:
  // Most system registers only implement a few of the bits in the word. Other
  // bits are "read-as-zero, write-ignored". The write_ignore_mask argument
  // describes the bits which are not modifiable.
  SimSystemRegister(uint32_t value, uint32_t write_ignore_mask)
      : value_(value), write_ignore_mask_(write_ignore_mask) { }

  uint32_t value_;
  uint32_t write_ignore_mask_;
};


// Represent a register (r0-r31, v0-v31).
template<int kSizeInBytes>
class SimRegisterBase {
 public:
  template<typename T>
  void Set(T new_value, unsigned size = sizeof(T)) {
    VIXL_ASSERT(size <= kSizeInBytes);
    VIXL_ASSERT(size <= sizeof(new_value));
    // All AArch64 registers are zero-extending; Writing a W register clears the
    // top bits of the corresponding X register.
    memset(value_, 0, kSizeInBytes);
    memcpy(value_, &new_value, size);
  }

  // Copy 'size' bytes of the register to the result, and zero-extend to fill
  // the result.
  template<typename T>
  T Get(unsigned size = sizeof(T)) const {
    VIXL_ASSERT(size <= kSizeInBytes);
    T result;
    memset(&result, 0, sizeof(result));
    memcpy(&result, value_, size);
    return result;
  }

 protected:
  uint8_t value_[kSizeInBytes];
};
typedef SimRegisterBase<kXRegSizeInBytes> SimRegister;      // r0-r31
typedef SimRegisterBase<kDRegSizeInBytes> SimFPRegister;    // v0-v31


class Simulator : public DecoderVisitor {
 public:
  explicit Simulator(Decoder* decoder, FILE* stream = stdout);
  ~Simulator();

  void ResetState();

  // Run the simulator.
  virtual void Run();
  void RunFrom(Instruction* first);

  // Simulation helpers.
  inline Instruction* pc() { return pc_; }
  inline void set_pc(Instruction* new_pc) {
    pc_ = new_pc;
    pc_modified_ = true;
  }

  inline void increment_pc() {
    if (!pc_modified_) {
      pc_ = pc_->NextInstruction();
    }

    pc_modified_ = false;
  }

  inline void ExecuteInstruction() {
    // The program counter should always be aligned.
    VIXL_ASSERT(IsWordAligned(pc_));
    decoder_->Decode(pc_);
    increment_pc();
  }

  // Declare all Visitor functions.
  #define DECLARE(A)  void Visit##A(Instruction* instr);
  VISITOR_LIST(DECLARE)
  #undef DECLARE

  // 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.
  //
  // The only supported values of 'size' are kXRegSize and kWRegSize.
  template<typename T>
  inline T reg(unsigned size, unsigned code,
               Reg31Mode r31mode = Reg31IsZeroRegister) const {
    unsigned size_in_bytes = size / 8;
    VIXL_ASSERT(size_in_bytes <= sizeof(T));
    VIXL_ASSERT((size == kXRegSize) || (size == kWRegSize));
    VIXL_ASSERT(code < kNumberOfRegisters);

    if ((code == 31) && (r31mode == Reg31IsZeroRegister)) {
      T result;
      memset(&result, 0, sizeof(result));
      return result;
    }
    return registers_[code].Get<T>(size_in_bytes);
  }

  // Like reg(), but infer the access size from the template type.
  template<typename T>
  inline T reg(unsigned code, Reg31Mode r31mode = Reg31IsZeroRegister) const {
    return reg<T>(sizeof(T) * 8, code, r31mode);
  }

  // Common specialized accessors for the reg() template.
  inline int32_t wreg(unsigned code,
                      Reg31Mode r31mode = Reg31IsZeroRegister) const {
    return reg<int32_t>(code, r31mode);
  }

  inline int64_t xreg(unsigned code,
                      Reg31Mode r31mode = Reg31IsZeroRegister) const {
    return reg<int64_t>(code, r31mode);
  }

  inline int64_t reg(unsigned size, unsigned code,
                     Reg31Mode r31mode = Reg31IsZeroRegister) const {
    return reg<int64_t>(size, code, r31mode);
  }

  // Write 'size' bits of 'value' into an integer register. The value is
  // zero-extended. This behaviour matches AArch64 register writes.
  //
  // The only supported values of 'size' are kXRegSize and kWRegSize.
  template<typename T>
  inline void set_reg(unsigned size, unsigned code, T value,
                      Reg31Mode r31mode = Reg31IsZeroRegister) {
    unsigned size_in_bytes = size / 8;
    VIXL_ASSERT(size_in_bytes <= sizeof(T));
    VIXL_ASSERT((size == kXRegSize) || (size == kWRegSize));
    VIXL_ASSERT(code < kNumberOfRegisters);

    if ((code == 31) && (r31mode == Reg31IsZeroRegister)) {
      return;
    }
    return registers_[code].Set(value, size_in_bytes);
  }

  // Like set_reg(), but infer the access size from the template type.
  template<typename T>
  inline void set_reg(unsigned code, T value,
                      Reg31Mode r31mode = Reg31IsZeroRegister) {
    set_reg(sizeof(value) * 8, code, value, r31mode);
  }

  // Common specialized accessors for the set_reg() template.
  inline void set_wreg(unsigned code, int32_t value,
                       Reg31Mode r31mode = Reg31IsZeroRegister) {
    set_reg(kWRegSize, code, value, r31mode);
  }

  inline void set_xreg(unsigned code, int64_t value,
                       Reg31Mode r31mode = Reg31IsZeroRegister) {
    set_reg(kXRegSize, code, value, r31mode);
  }

  // Commonly-used special cases.
  template<typename T>
  inline void set_lr(T value) {
    set_reg(kLinkRegCode, value);
  }

  template<typename T>
  inline void set_sp(T value) {
    set_reg(31, value, Reg31IsStackPointer);
  }

  // Return 'size' bits of the value of a floating-point register, as the
  // specified type. The value is zero-extended to fill the result.
  //
  // The only supported values of 'size' are kDRegSize and kSRegSize.
  template<typename T>
  inline T fpreg(unsigned size, unsigned code) const {
    unsigned size_in_bytes = size / 8;
    VIXL_ASSERT(size_in_bytes <= sizeof(T));
    VIXL_ASSERT((size == kDRegSize) || (size == kSRegSize));
    VIXL_ASSERT(code < kNumberOfFPRegisters);
    return fpregisters_[code].Get<T>(size_in_bytes);
  }

  // Like fpreg(), but infer the access size from the template type.
  template<typename T>
  inline T fpreg(unsigned code) const {
    return fpreg<T>(sizeof(T) * 8, code);
  }

  // Common specialized accessors for the fpreg() template.
  inline float sreg(unsigned code) const {
    return fpreg<float>(code);
  }

  inline uint32_t sreg_bits(unsigned code) const {
    return fpreg<uint32_t>(code);
  }

  inline double dreg(unsigned code) const {
    return fpreg<double>(code);
  }

  inline uint64_t dreg_bits(unsigned code) const {
    return fpreg<uint64_t>(code);
  }

  inline double fpreg(unsigned size, unsigned code) const {
    switch (size) {
      case kSRegSize: return sreg(code);
      case kDRegSize: return dreg(code);
      default:
        VIXL_UNREACHABLE();
        return 0.0;
    }
  }

  // Write 'value' into a floating-point register. The value is zero-extended.
  // This behaviour matches AArch64 register writes.
  template<typename T>
  inline void set_fpreg(unsigned code, T value) {
    VIXL_ASSERT((sizeof(value) == kDRegSizeInBytes) ||
           (sizeof(value) == kSRegSizeInBytes));
    VIXL_ASSERT(code < kNumberOfFPRegisters);
    fpregisters_[code].Set(value, sizeof(value));
  }

  // Common specialized accessors for the set_fpreg() template.
  inline void set_sreg(unsigned code, float value) {
    set_fpreg(code, value);
  }

  inline void set_sreg_bits(unsigned code, uint32_t value) {
    set_fpreg(code, value);
  }

  inline void set_dreg(unsigned code, double value) {
    set_fpreg(code, value);
  }

  inline void set_dreg_bits(unsigned code, uint64_t value) {
    set_fpreg(code, value);
  }

  bool N() { return nzcv_.N() != 0; }
  bool Z() { return nzcv_.Z() != 0; }
  bool C() { return nzcv_.C() != 0; }
  bool V() { return nzcv_.V() != 0; }
  SimSystemRegister& nzcv() { return nzcv_; }

  // TODO(jbramley): Find a way to make the fpcr_ members return the proper
  // types, so these accessors are not necessary.
  FPRounding RMode() { return static_cast<FPRounding>(fpcr_.RMode()); }
  bool DN() { return fpcr_.DN() != 0; }
  SimSystemRegister& fpcr() { return fpcr_; }

  // Debug helpers
  void PrintSystemRegisters(bool print_all = false);
  void PrintRegisters(bool print_all_regs = false);
  void PrintFPRegisters(bool print_all_regs = false);
  void PrintProcessorState();

  static const char* WRegNameForCode(unsigned code,
                                     Reg31Mode mode = Reg31IsZeroRegister);
  static const char* XRegNameForCode(unsigned code,
                                     Reg31Mode mode = Reg31IsZeroRegister);
  static const char* SRegNameForCode(unsigned code);
  static const char* DRegNameForCode(unsigned code);
  static const char* VRegNameForCode(unsigned code);

  inline bool coloured_trace() { return coloured_trace_; }
  void set_coloured_trace(bool value);

  inline bool disasm_trace() { return disasm_trace_; }
  inline void set_disasm_trace(bool value) {
    if (value != disasm_trace_) {
      if (value) {
        decoder_->InsertVisitorBefore(print_disasm_, this);
      } else {
        decoder_->RemoveVisitor(print_disasm_);
      }
      disasm_trace_ = value;
    }
  }
  inline void set_instruction_stats(bool value) {
    if (value != instruction_stats_) {
      if (value) {
        decoder_->AppendVisitor(instrumentation_);
      } else {
        decoder_->RemoveVisitor(instrumentation_);
      }
      instruction_stats_ = value;
    }
  }

 protected:
  const char* clr_normal;
  const char* clr_flag_name;
  const char* clr_flag_value;
  const char* clr_reg_name;
  const char* clr_reg_value;
  const char* clr_fpreg_name;
  const char* clr_fpreg_value;
  const char* clr_memory_value;
  const char* clr_memory_address;
  const char* clr_debug_number;
  const char* clr_debug_message;
  const char* clr_printf;

  // Simulation helpers ------------------------------------
  bool ConditionPassed(Condition cond) {
    switch (cond) {
      case eq:
        return Z();
      case ne:
        return !Z();
      case hs:
        return C();
      case lo:
        return !C();
      case mi:
        return N();
      case pl:
        return !N();
      case vs:
        return V();
      case vc:
        return !V();
      case hi:
        return C() && !Z();
      case ls:
        return !(C() && !Z());
      case ge:
        return N() == V();
      case lt:
        return N() != V();
      case gt:
        return !Z() && (N() == V());
      case le:
        return !(!Z() && (N() == V()));
      case nv:  // Fall through.
      case al:
        return true;
      default:
        VIXL_UNREACHABLE();
        return false;
    }
  }

  bool ConditionFailed(Condition cond) {
    return !ConditionPassed(cond);
  }

  void AddSubHelper(Instruction* instr, int64_t op2);
  int64_t AddWithCarry(unsigned reg_size,
                       bool set_flags,
                       int64_t src1,
                       int64_t src2,
                       int64_t carry_in = 0);
  void LogicalHelper(Instruction* instr, int64_t op2);
  void ConditionalCompareHelper(Instruction* instr, int64_t op2);
  void LoadStoreHelper(Instruction* instr,
                       int64_t offset,
                       AddrMode addrmode);
  void LoadStorePairHelper(Instruction* instr, AddrMode addrmode);
  uint8_t* AddressModeHelper(unsigned addr_reg,
                             int64_t offset,
                             AddrMode addrmode);

  uint64_t MemoryRead(const uint8_t* address, unsigned num_bytes);
  uint8_t MemoryRead8(uint8_t* address);
  uint16_t MemoryRead16(uint8_t* address);
  uint32_t MemoryRead32(uint8_t* address);
  float MemoryReadFP32(uint8_t* address);
  uint64_t MemoryRead64(uint8_t* address);
  double MemoryReadFP64(uint8_t* address);

  void MemoryWrite(uint8_t* address, uint64_t value, unsigned num_bytes);
  void MemoryWrite32(uint8_t* address, uint32_t value);
  void MemoryWriteFP32(uint8_t* address, float value);
  void MemoryWrite64(uint8_t* address, uint64_t value);
  void MemoryWriteFP64(uint8_t* address, double value);

  int64_t ShiftOperand(unsigned reg_size,
                       int64_t value,
                       Shift shift_type,
                       unsigned amount);
  int64_t Rotate(unsigned reg_width,
                 int64_t value,
                 Shift shift_type,
                 unsigned amount);
  int64_t ExtendValue(unsigned reg_width,
                      int64_t value,
                      Extend extend_type,
                      unsigned left_shift = 0);

  uint64_t ReverseBits(uint64_t value, unsigned num_bits);
  uint64_t ReverseBytes(uint64_t value, ReverseByteMode mode);

  template <typename T>
  T FPDefaultNaN() const;

  void FPCompare(double val0, double val1);
  double FPRoundInt(double value, FPRounding round_mode);
  double FPToDouble(float value);
  float FPToFloat(double value, FPRounding round_mode);
  double FixedToDouble(int64_t src, int fbits, FPRounding round_mode);
  double UFixedToDouble(uint64_t src, int fbits, FPRounding round_mode);
  float FixedToFloat(int64_t src, int fbits, FPRounding round_mode);
  float UFixedToFloat(uint64_t src, int fbits, FPRounding round_mode);
  int32_t FPToInt32(double value, FPRounding rmode);
  int64_t FPToInt64(double value, FPRounding rmode);
  uint32_t FPToUInt32(double value, FPRounding rmode);
  uint64_t FPToUInt64(double value, FPRounding rmode);

  template <typename T>
  T FPAdd(T op1, T op2);

  template <typename T>
  T FPDiv(T op1, T op2);

  template <typename T>
  T FPMax(T a, T b);

  template <typename T>
  T FPMaxNM(T a, T b);

  template <typename T>
  T FPMin(T a, T b);

  template <typename T>
  T FPMinNM(T a, T b);

  template <typename T>
  T FPMul(T op1, T op2);

  template <typename T>
  T FPMulAdd(T a, T op1, T op2);

  template <typename T>
  T FPSqrt(T op);

  template <typename T>
  T FPSub(T op1, T op2);

  // This doesn't do anything at the moment. We'll need it if we want support
  // for cumulative exception bits or floating-point exceptions.
  void FPProcessException() { }

  // Standard NaN processing.
  template <typename T>
  T FPProcessNaN(T op);

  bool FPProcessNaNs(Instruction* instr);

  template <typename T>
  T FPProcessNaNs(T op1, T op2);

  template <typename T>
  T FPProcessNaNs3(T op1, T op2, T op3);

  // Pseudo Printf instruction
  void DoPrintf(Instruction* instr);

  // Processor state ---------------------------------------

  // Output stream.
  FILE* stream_;
  PrintDisassembler* print_disasm_;

  // Instruction statistics instrumentation.
  Instrument* instrumentation_;

  // General purpose registers. Register 31 is the stack pointer.
  SimRegister registers_[kNumberOfRegisters];

  // Floating point registers
  SimFPRegister fpregisters_[kNumberOfFPRegisters];

  // Program Status Register.
  // bits[31, 27]: Condition flags N, Z, C, and V.
  //               (Negative, Zero, Carry, Overflow)
  SimSystemRegister nzcv_;

  // Floating-Point Control Register
  SimSystemRegister fpcr_;

  // Only a subset of FPCR features are supported by the simulator. This helper
  // checks that the FPCR settings are supported.
  //
  // This is checked when floating-point instructions are executed, not when
  // FPCR is set. This allows generated code to modify FPCR for external
  // functions, or to save and restore it when entering and leaving generated
  // code.
  void AssertSupportedFPCR() {
    VIXL_ASSERT(fpcr().FZ() == 0);             // No flush-to-zero support.
    VIXL_ASSERT(fpcr().RMode() == FPTieEven);  // Ties-to-even rounding only.

    // The simulator does not support half-precision operations so fpcr().AHP()
    // is irrelevant, and is not checked here.
  }

  static inline int CalcNFlag(uint64_t result, unsigned reg_size) {
    return (result >> (reg_size - 1)) & 1;
  }

  static inline int CalcZFlag(uint64_t result) {
    return result == 0;
  }

  static const uint32_t kConditionFlagsMask = 0xf0000000;

  // Stack
  byte* stack_;
  static const int stack_protection_size_ = 256;
  // 2 KB stack.
  static const int stack_size_ = 2 * 1024 + 2 * stack_protection_size_;
  byte* stack_limit_;

  Decoder* decoder_;
  // Indicates if the pc has been modified by the instruction and should not be
  // automatically incremented.
  bool pc_modified_;
  Instruction* pc_;

  static const char* xreg_names[];
  static const char* wreg_names[];
  static const char* sreg_names[];
  static const char* dreg_names[];
  static const char* vreg_names[];

  static const Instruction* kEndOfSimAddress;

 private:
  bool coloured_trace_;

  // Indicates whether the disassembly trace is active.
  bool disasm_trace_;

  // Indicates whether the instruction instrumentation is active.
  bool instruction_stats_;
};
}  // namespace vixl

#endif  // VIXL_A64_SIMULATOR_A64_H_