| // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file | |
| // for details. All rights reserved. Use of this source code is governed by a | |
| // BSD-style license that can be found in the LICENSE file. | |
| #ifndef RUNTIME_VM_SIMULATOR_RISCV_H_ | |
| #define RUNTIME_VM_SIMULATOR_RISCV_H_ | |
| #ifndef RUNTIME_VM_SIMULATOR_H_ | |
| #error Do not include simulator_riscv.h directly; use simulator.h. | |
| #endif | |
| #include "vm/constants.h" | |
| #include "vm/random.h" | |
| #include "vm/simulator_memory.h" | |
| namespace dart { | |
| class Isolate; | |
| class Mutex; | |
| class SimulatorSetjmpBuffer; | |
| class Thread; | |
| #define ELEN 64 | |
| #define VLEN 128 | |
| COMPILE_ASSERT(ELEN >= 8); | |
| COMPILE_ASSERT(Utils::IsPowerOfTwo(ELEN)); | |
| COMPILE_ASSERT(VLEN >= ELEN); | |
| COMPILE_ASSERT(Utils::IsPowerOfTwo(VLEN)); | |
| COMPILE_ASSERT(VLEN <= 0x10000); | |
| // TODO(riscv): Dynamic rounding mode and other FSCR state. | |
| class Simulator { | |
| public: | |
| static constexpr uword kSimulatorStackUnderflowSize = 64; | |
| Simulator(); | |
| ~Simulator(); | |
| static Simulator* Current(); | |
| intx_t CallX(intx_t function, | |
| intx_t arg0 = 0, | |
| intx_t arg1 = 0, | |
| intx_t arg2 = 0, | |
| intx_t arg3 = 0) { | |
| PreservedRegisters preserved; | |
| PrepareCall(&preserved); | |
| set_xreg(A0, arg0); | |
| set_xreg(A1, arg1); | |
| set_xreg(A2, arg2); | |
| set_xreg(A3, arg3); | |
| RunCall(function, &preserved); | |
| return get_xreg(A0); | |
| } | |
| intx_t CallI(intx_t function, double arg0, double arg1 = 0.0) { | |
| PreservedRegisters preserved; | |
| PrepareCall(&preserved); | |
| set_fregd(FA0, arg0); | |
| set_fregd(FA1, arg1); | |
| RunCall(function, &preserved); | |
| return get_xreg(A0); | |
| } | |
| intx_t CallI(intx_t function, float arg0, float arg1 = 0.0f) { | |
| PreservedRegisters preserved; | |
| PrepareCall(&preserved); | |
| set_fregs(FA0, arg0); | |
| set_fregs(FA1, arg1); | |
| RunCall(function, &preserved); | |
| return get_xreg(A0); | |
| } | |
| int64_t CallI64(intx_t function, double arg0, double arg1 = 0.0) { | |
| PreservedRegisters preserved; | |
| PrepareCall(&preserved); | |
| set_fregd(FA0, arg0); | |
| set_fregd(FA1, arg1); | |
| RunCall(function, &preserved); | |
| #if XLEN == 32 | |
| uint64_t hi = static_cast<uint32_t>(get_xreg(A1)); | |
| uint64_t lo = static_cast<uint32_t>(get_xreg(A0)); | |
| return (hi << 32) | lo; | |
| #else | |
| return get_xreg(A0); | |
| #endif | |
| } | |
| double CallD(intx_t function, intx_t arg0, intx_t arg1 = 0) { | |
| PreservedRegisters preserved; | |
| PrepareCall(&preserved); | |
| set_xreg(A0, arg0); | |
| set_xreg(A1, arg1); | |
| RunCall(function, &preserved); | |
| return get_fregd(FA0); | |
| } | |
| #if XLEN == 32 | |
| double CallD(intx_t function, int64_t arg0) { | |
| PreservedRegisters preserved; | |
| PrepareCall(&preserved); | |
| set_xreg(A0, static_cast<uint64_t>(arg0) & 0xFFFFFFFF); | |
| set_xreg(A1, static_cast<uint64_t>(arg0) >> 32); | |
| RunCall(function, &preserved); | |
| return get_fregd(FA0); | |
| } | |
| #endif | |
| double CallD(intx_t function, | |
| double arg0, | |
| double arg1 = 0.0, | |
| double arg2 = 0.0) { | |
| PreservedRegisters preserved; | |
| PrepareCall(&preserved); | |
| set_fregd(FA0, arg0); | |
| set_fregd(FA1, arg1); | |
| set_fregd(FA2, arg2); | |
| RunCall(function, &preserved); | |
| return get_fregd(FA0); | |
| } | |
| double CallD(intx_t function, intx_t arg0, double arg1) { | |
| PreservedRegisters preserved; | |
| PrepareCall(&preserved); | |
| set_xreg(A0, arg0); | |
| set_fregd(FA0, arg1); | |
| RunCall(function, &preserved); | |
| return get_fregd(FA0); | |
| } | |
| double CallD(intx_t function, float arg0) { | |
| PreservedRegisters preserved; | |
| PrepareCall(&preserved); | |
| set_fregs(FA0, arg0); | |
| RunCall(function, &preserved); | |
| return get_fregd(FA0); | |
| } | |
| float CallF(intx_t function, intx_t arg0, intx_t arg1 = 0) { | |
| PreservedRegisters preserved; | |
| SavePreservedRegisters(&preserved); | |
| set_xreg(A0, arg0); | |
| set_xreg(A1, arg1); | |
| RunCall(function, &preserved); | |
| return get_fregs(FA0); | |
| } | |
| float CallF(intx_t function, | |
| float arg0, | |
| float arg1 = 0.0f, | |
| float arg2 = 0.0f) { | |
| PreservedRegisters preserved; | |
| PrepareCall(&preserved); | |
| set_fregs(FA0, arg0); | |
| set_fregs(FA1, arg1); | |
| set_fregs(FA2, arg2); | |
| RunCall(function, &preserved); | |
| return get_fregs(FA0); | |
| } | |
| float CallF(intx_t function, intx_t arg0, float arg1) { | |
| PreservedRegisters preserved; | |
| PrepareCall(&preserved); | |
| set_xreg(A0, arg0); | |
| set_fregs(FA0, arg1); | |
| RunCall(function, &preserved); | |
| return get_fregs(FA0); | |
| } | |
| float CallF(intx_t function, double arg0) { | |
| PreservedRegisters preserved; | |
| PrepareCall(&preserved); | |
| set_fregd(FA0, arg0); | |
| RunCall(function, &preserved); | |
| return get_fregs(FA0); | |
| } | |
| // Dart generally calls into generated code with 4 parameters. This is a | |
| // convenience function, which sets up the simulator state and grabs the | |
| // result on return. The return value is A0. The parameters are placed in | |
| // A0-3. | |
| int64_t Call(intx_t entry, | |
| intx_t parameter0, | |
| intx_t parameter1, | |
| intx_t parameter2, | |
| intx_t parameter3, | |
| bool fp_return = false, | |
| bool fp_args = false); | |
| // Runtime and native call support. | |
| enum CallKind { | |
| kRuntimeCall, | |
| kLeafRuntimeCall, | |
| kLeafFloatRuntimeCall, | |
| kNativeCallWrapper | |
| }; | |
| static uword RedirectExternalReference(uword function, | |
| CallKind call_kind, | |
| int argument_count); | |
| static uword FunctionForRedirect(uword redirect); | |
| void JumpToFrame(uword pc, uword sp, uword fp, Thread* thread); | |
| uintx_t get_register(Register rs) const { return get_xreg(rs); } | |
| uintx_t get_pc() const { return pc_; } | |
| uintx_t get_sp() const { return get_xreg(SP); } | |
| uintx_t get_fp() const { return get_xreg(FP); } | |
| uintx_t get_lr() const { return get_xreg(RA); } | |
| void set_lp_enabled(bool value) { lp_enabled_ = value; } | |
| void set_ss_enabled(bool value) { ss_enabled_ = value; } | |
| void PrintRegisters(); | |
| void PrintStack(); | |
| // High address. | |
| uword stack_base() const { return stack_base_; } | |
| // Limit for StackOverflowError. | |
| uword overflow_stack_limit() const { return overflow_stack_limit_; } | |
| // Low address. | |
| uword stack_limit() const { return stack_limit_; } | |
| // Accessor to the instruction counter. | |
| uint64_t get_icount() const { return instret_; } | |
| // Call on program start. | |
| static void Init(); | |
| private: | |
| struct PreservedRegisters { | |
| uintx_t xregs[kNumberOfCpuRegisters]; | |
| double fregs[kNumberOfFpuRegisters]; | |
| uintx_t ssp; | |
| }; | |
| void PrepareCall(PreservedRegisters* preserved); | |
| void ClobberVolatileRegisters(); | |
| void SavePreservedRegisters(PreservedRegisters* preserved); | |
| void CheckPreservedRegisters(PreservedRegisters* preserved); | |
| void RunCall(intx_t function, PreservedRegisters* preserved); | |
| void Interpret(Instr instr); | |
| void Interpret(CInstr instr); | |
| void InterpretLUI(Instr instr); | |
| void InterpretAUIPC(Instr instr); | |
| void InterpretJAL(Instr instr); | |
| void InterpretJALR(Instr instr); | |
| void InterpretBRANCH(Instr instr); | |
| void InterpretLOAD(Instr instr); | |
| void InterpretSTORE(Instr instr); | |
| void InterpretOPIMM(Instr instr); | |
| void InterpretOPIMM32(Instr instr); | |
| void InterpretOP(Instr instr); | |
| void InterpretOP_0(Instr instr); | |
| void InterpretOP_SUB(Instr instr); | |
| void InterpretOP_MULDIV(Instr instr); | |
| void InterpretOP_SHADD(Instr instr); | |
| void InterpretOP_MINMAXCLMUL(Instr instr); | |
| void InterpretOP_ROTATE(Instr instr); | |
| void InterpretOP_BCLRBEXT(Instr instr); | |
| void InterpretOP_CZERO(Instr instr); | |
| void InterpretOP32(Instr instr); | |
| void InterpretOP32_0(Instr instr); | |
| void InterpretOP32_SUB(Instr instr); | |
| void InterpretOP32_MULDIV(Instr instr); | |
| void InterpretOP32_SHADD(Instr instr); | |
| void InterpretOP32_ADDUW(Instr instr); | |
| void InterpretOP32_ROTATE(Instr instr); | |
| void InterpretMISCMEM(Instr instr); | |
| void InterpretSYSTEM(Instr instr); | |
| void InterpretECALL(Instr instr); | |
| void InterpretAMO(Instr instr); | |
| void InterpretAMO8(Instr instr); | |
| void InterpretAMO16(Instr instr); | |
| void InterpretAMO32(Instr instr); | |
| void InterpretAMO64(Instr instr); | |
| void InterpretAMO128(Instr instr); | |
| template <typename type> | |
| void InterpretLR(Instr instr); | |
| template <typename type> | |
| void InterpretSC(Instr instr); | |
| template <typename type> | |
| void InterpretAMOSWAP(Instr instr); | |
| template <typename type> | |
| void InterpretAMOADD(Instr instr); | |
| template <typename type> | |
| void InterpretAMOXOR(Instr instr); | |
| template <typename type> | |
| void InterpretAMOAND(Instr instr); | |
| template <typename type> | |
| void InterpretAMOOR(Instr instr); | |
| template <typename type> | |
| void InterpretAMOMIN(Instr instr); | |
| template <typename type> | |
| void InterpretAMOMAX(Instr instr); | |
| template <typename type> | |
| void InterpretAMOMINU(Instr instr); | |
| template <typename type> | |
| void InterpretAMOMAXU(Instr instr); | |
| template <typename type> | |
| void InterpretLOADORDERED(Instr instr); | |
| template <typename type> | |
| void InterpretSTOREORDERED(Instr instr); | |
| template <typename type> | |
| void InterpretAMOCAS(Instr instr); | |
| void InterpretLOADFP(Instr instr); | |
| template <typename type> | |
| void InterpretLOADV(Instr instr); | |
| void InterpretSTOREFP(Instr instr); | |
| template <typename type> | |
| void InterpretSTOREV(Instr instr); | |
| void InterpretFMADD(Instr instr); | |
| void InterpretFMSUB(Instr instr); | |
| void InterpretFNMADD(Instr instr); | |
| void InterpretFNMSUB(Instr instr); | |
| void InterpretOPFP(Instr instr); | |
| void InterpretOPV(Instr instr); | |
| void InterpretOPV_IVV(Instr instr); | |
| void InterpretOPV_FVV(Instr instr); | |
| void InterpretOPV_MVV(Instr instr); | |
| void InterpretOPV_IVI(Instr instr); | |
| void InterpretOPV_IVX(Instr instr); | |
| template <typename sew_t> | |
| void InterpretOPV_IVX(Instr instr); | |
| void InterpretOPV_FVF(Instr instr); | |
| void InterpretOPV_MVX(Instr instr); | |
| void InterpretOPV_CFG(Instr instr); | |
| DART_NORETURN void IllegalInstruction(Instr instr); | |
| DART_NORETURN void IllegalInstruction(CInstr instr); | |
| DART_NORETURN void Fault(const char* message); | |
| void CheckLandingPad(Register rs1); | |
| template <typename type> | |
| type MemoryRead(uintx_t address, Register base); | |
| template <typename type> | |
| void MemoryWrite(uintx_t address, type value, Register base); | |
| intx_t CSRRead(uint16_t csr); | |
| void CSRWrite(uint16_t csr, intx_t value); | |
| void CSRSet(uint16_t csr, intx_t mask); | |
| void CSRClear(uint16_t csr, intx_t mask); | |
| uintx_t get_xreg(Register rs) const { return xregs_[rs]; } | |
| void set_xreg(Register rd, uintx_t value) { | |
| if (rd != ZR) { | |
| xregs_[rd] = value; | |
| } | |
| } | |
| double get_fregd(FRegister rs) const { return fregs_[rs]; } | |
| void set_fregd(FRegister rd, double value) { fregs_[rd] = value; } | |
| static constexpr uint64_t kNaNBox = 0xFFFFFFFF00000000; | |
| float get_fregs_raw(FRegister rs) const { | |
| uint64_t bits64 = bit_cast<uint64_t>(fregs_[rs]); | |
| uint32_t bits32 = static_cast<uint32_t>(bits64); | |
| return bit_cast<float>(bits32); | |
| } | |
| float get_fregs(FRegister rs) const { | |
| uint64_t bits64 = bit_cast<uint64_t>(fregs_[rs]); | |
| if ((bits64 & kNaNBox) != kNaNBox) { | |
| // When the register value isn't a valid NaN, the canonical NaN is used | |
| // instead. | |
| return bit_cast<float>(0x7fc00000); | |
| } | |
| uint32_t bits32 = static_cast<uint32_t>(bits64); | |
| return bit_cast<float>(bits32); | |
| } | |
| void set_fregs(FRegister rd, float value) { | |
| uint32_t bits32 = bit_cast<uint32_t>(value); | |
| uint64_t bits64 = static_cast<uint64_t>(bits32); | |
| bits64 |= kNaNBox; | |
| fregs_[rd] = bit_cast<double>(bits64); | |
| } | |
| template <typename T> | |
| T* ref_vreg(VRegister vd) { | |
| return reinterpret_cast<T*>(&vregs_[vd][0]); | |
| } | |
| LengthMultiplier vlmul() const { | |
| return static_cast<LengthMultiplier>((vtype_ >> 0) & 7); | |
| } | |
| ElementWidth vsew() const { | |
| return static_cast<ElementWidth>((vtype_ >> 3) & 7); | |
| } | |
| // Known bad pc value to ensure that the simulator does not execute | |
| // without being properly setup. | |
| static constexpr uword kBadLR = -1; | |
| // A pc value used to signal the simulator to stop execution. Generally | |
| // the lr is set to this value on transition from native C code to | |
| // simulated execution, so that the simulator can "return" to the native | |
| // C code. | |
| static constexpr uword kEndSimulatingPC = -2; | |
| // I state. | |
| uintx_t pc_ = 0; | |
| uintx_t xregs_[kNumberOfCpuRegisters]; | |
| uint64_t instret_ = 0; // "Instructions retired" - mandatory counter. | |
| // A state. | |
| uintx_t reserved_address_ = 0; | |
| uintx_t reserved_value_ = 0; | |
| // F/D state. | |
| double fregs_[kNumberOfFpuRegisters]; | |
| uint32_t fcsr_ = 0; | |
| // Zicfilp/ss state | |
| bool lp_enabled_ = false; | |
| bool ss_enabled_ = false; | |
| uintx_t ssp_ = 0; | |
| // V state | |
| uint8_t vregs_[kNumberOfVectorRegisters][VLEN / 8]; | |
| uintx_t vl_; | |
| uintx_t vtype_; | |
| // Simulator support. | |
| char* stack_; | |
| char* shadow_stack_; | |
| uword stack_limit_; | |
| uword overflow_stack_limit_; | |
| uword stack_base_; | |
| Random random_; | |
| SimulatorSetjmpBuffer* last_setjmp_buffer_ = nullptr; | |
| SimulatorMemory memory_; | |
| static bool IsIllegalAddress(uword addr) { return addr < 64 * 1024; } | |
| // Executes RISC-V instructions until the PC reaches kEndSimulatingPC. | |
| void Execute(); | |
| void ExecuteNoTrace(); | |
| void ExecuteTrace(); | |
| // Returns true if tracing of executed instructions is enabled. | |
| bool IsTracingExecution() const; | |
| // Longjmp support for exceptions. | |
| SimulatorSetjmpBuffer* last_setjmp_buffer() { return last_setjmp_buffer_; } | |
| void set_last_setjmp_buffer(SimulatorSetjmpBuffer* buffer) { | |
| last_setjmp_buffer_ = buffer; | |
| } | |
| friend class SimulatorSetjmpBuffer; | |
| DISALLOW_COPY_AND_ASSIGN(Simulator); | |
| }; | |
| } // namespace dart | |
| #endif // RUNTIME_VM_SIMULATOR_RISCV_H_ |