dart-sdk/runtime/vm/simulator_riscv.h
Ryan Macnak fb24668989 [vm] Avoid confusing foreign stack walkers at the Dart entry frame.
On architectures with a link register, don't generate a profiler marker into what a foreign stack walker will interpret as the caller's frame area. If the caller is unwound based on SP instead of FP, this will cause it incorrectly identify its own caller.

TEST=ci (--profile_vm=false)
Bug: b/220804295
Change-Id: I3cc9d579f9c95476e23040c84574d2965b5eb7ac
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/235164
Reviewed-by: Ben Konyi <bkonyi@google.com>
Commit-Queue: Ryan Macnak <rmacnak@google.com>
2022-03-07 22:49:23 +00:00

345 lines
10 KiB
C++

// 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();
// 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_