blob: d49fed968c955cf5976f7d76a1176db684b3c021 [file] [log] [blame]
// 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"
namespace dart {
class Isolate;
class Mutex;
class SimulatorSetjmpBuffer;
class Thread;
// TODO(riscv): Introduce random LR/SC failures.
// TODO(riscv): Dynamic rounding mode and other FSCR state.
class Simulator {
public:
static const 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);
}
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);
}
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 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];
};
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 InterpretOP32(Instr instr);
void InterpretOP32_0(Instr instr);
void InterpretOP32_SUB(Instr instr);
void InterpretOP32_MULDIV(Instr instr);
void InterpretMISCMEM(Instr instr);
void InterpretSYSTEM(Instr instr);
void InterpretECALL(Instr instr);
void InterpretEBREAK(Instr instr);
void InterpretEBREAK(CInstr instr);
void InterpretAMO(Instr instr);
void InterpretAMO32(Instr instr);
void InterpretAMO64(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);
void InterpretLOADFP(Instr instr);
void InterpretSTOREFP(Instr instr);
void InterpretFMADD(Instr instr);
void InterpretFMSUB(Instr instr);
void InterpretFNMADD(Instr instr);
void InterpretFNMSUB(Instr instr);
void InterpretOPFP(Instr instr);
DART_NORETURN void IllegalInstruction(Instr instr);
DART_NORETURN void IllegalInstruction(CInstr instr);
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(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);
}
// 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_;
uintx_t xregs_[kNumberOfCpuRegisters];
uint64_t instret_; // "Instructions retired" - mandatory counter.
// A state.
uintx_t reserved_address_;
uintx_t reserved_value_;
// F/D state.
double fregs_[kNumberOfFpuRegisters];
uint32_t fcsr_;
// Simulator support.
char* stack_;
uword stack_limit_;
uword overflow_stack_limit_;
uword stack_base_;
Random random_;
SimulatorSetjmpBuffer* last_setjmp_buffer_;
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_