mirror of
https://github.com/dart-lang/sdk
synced 2024-11-02 14:32:24 +00:00
d29235cdb5
Cf. 01a7f207c3
.
TEST=ci
Change-Id: I8b6f9b827c1725d85013de70a1346303bdd9129e
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/283200
Reviewed-by: Alexander Aprelev <aam@google.com>
Commit-Queue: Ryan Macnak <rmacnak@google.com>
264 lines
8.7 KiB
C++
264 lines
8.7 KiB
C++
// Copyright (c) 2014, 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.
|
|
|
|
// Declares a Simulator for ARM64 instructions if we are not generating a native
|
|
// ARM64 binary. This Simulator allows us to run and debug ARM64 code generation
|
|
// on regular desktop machines.
|
|
// Dart calls into generated code by "calling" the InvokeDartCode stub,
|
|
// which will start execution in the Simulator or forwards to the real entry
|
|
// on a ARM64 HW platform.
|
|
|
|
#ifndef RUNTIME_VM_SIMULATOR_ARM64_H_
|
|
#define RUNTIME_VM_SIMULATOR_ARM64_H_
|
|
|
|
#ifndef RUNTIME_VM_SIMULATOR_H_
|
|
#error Do not include simulator_arm64.h directly; use simulator.h.
|
|
#endif
|
|
|
|
#include "vm/constants.h"
|
|
|
|
namespace dart {
|
|
|
|
class Isolate;
|
|
class Mutex;
|
|
class SimulatorSetjmpBuffer;
|
|
class Thread;
|
|
|
|
typedef struct {
|
|
union {
|
|
int64_t i64[2];
|
|
int32_t i32[4];
|
|
} bits;
|
|
} simd_value_t;
|
|
|
|
class Simulator {
|
|
public:
|
|
static constexpr uword kSimulatorStackUnderflowSize = 64;
|
|
|
|
Simulator();
|
|
~Simulator();
|
|
|
|
// The currently executing Simulator instance, which is associated to the
|
|
// current isolate
|
|
static Simulator* Current();
|
|
|
|
// Accessors for register state.
|
|
// The default value for R31Type has to be R31IsSP because get_register is
|
|
// accessed from architecture independent code through SPREG without
|
|
// specifying the type. We also can't translate a dummy value for SPREG into
|
|
// a real value because the architecture independent code expects SPREG to
|
|
// be a real register value.
|
|
void set_register(Instr* instr,
|
|
Register reg,
|
|
int64_t value,
|
|
R31Type r31t = R31IsSP);
|
|
int64_t get_register(Register reg, R31Type r31t = R31IsSP) const;
|
|
void set_wregister(Register reg, int32_t value, R31Type r31t = R31IsSP);
|
|
int32_t get_wregister(Register reg, R31Type r31t = R31IsSP) const;
|
|
|
|
int32_t get_vregisters(VRegister reg, int idx) const;
|
|
void set_vregisters(VRegister reg, int idx, int32_t value);
|
|
|
|
int64_t get_vregisterd(VRegister reg, int idx) const;
|
|
void set_vregisterd(VRegister reg, int idx, int64_t value);
|
|
|
|
void get_vregister(VRegister reg, simd_value_t* value) const;
|
|
void set_vregister(VRegister reg, const simd_value_t& value);
|
|
|
|
int64_t get_sp() const { return get_register(SPREG); }
|
|
int64_t get_lr() const { return get_register(R30); }
|
|
|
|
uint64_t get_pc() const;
|
|
uint64_t get_last_pc() const;
|
|
void set_pc(uint64_t pc);
|
|
|
|
// 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 icount_; }
|
|
|
|
// Call on program start.
|
|
static void Init();
|
|
|
|
// 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 R0. The parameters are placed in
|
|
// R0-3.
|
|
int64_t Call(int64_t entry,
|
|
int64_t parameter0,
|
|
int64_t parameter1,
|
|
int64_t parameter2,
|
|
int64_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);
|
|
|
|
private:
|
|
// 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;
|
|
|
|
// CPU state.
|
|
int64_t registers_[kNumberOfCpuRegisters];
|
|
bool n_flag_;
|
|
bool z_flag_;
|
|
bool c_flag_;
|
|
bool v_flag_;
|
|
|
|
simd_value_t vregisters_[kNumberOfVRegisters];
|
|
|
|
// Simulator support.
|
|
int64_t last_pc_;
|
|
int64_t pc_;
|
|
char* stack_;
|
|
uword stack_limit_;
|
|
uword overflow_stack_limit_;
|
|
uword stack_base_;
|
|
bool pc_modified_;
|
|
uint64_t icount_;
|
|
static int64_t flag_stop_sim_at_;
|
|
SimulatorSetjmpBuffer* last_setjmp_buffer_;
|
|
|
|
// Registered breakpoints.
|
|
Instr* break_pc_;
|
|
int64_t break_instr_;
|
|
|
|
// Illegal memory access support.
|
|
static bool IsIllegalAddress(uword addr) { return addr < 64 * 1024; }
|
|
void HandleIllegalAccess(uword addr, Instr* instr);
|
|
|
|
// Handles an unaligned memory access.
|
|
void UnalignedAccess(const char* msg, uword addr, Instr* instr);
|
|
|
|
// Handles a legal instruction that the simulator does not implement.
|
|
void UnimplementedInstruction(Instr* instr);
|
|
|
|
// Unsupported instructions use Format to print an error and stop execution.
|
|
void Format(Instr* instr, const char* format);
|
|
|
|
inline uint8_t ReadBU(uword addr);
|
|
inline int8_t ReadB(uword addr);
|
|
inline void WriteB(uword addr, uint8_t value);
|
|
|
|
inline uint16_t ReadHU(uword addr, Instr* instr);
|
|
inline int16_t ReadH(uword addr, Instr* instr);
|
|
inline void WriteH(uword addr, uint16_t value, Instr* instr);
|
|
|
|
inline uint32_t ReadWU(uword addr,
|
|
Instr* instr,
|
|
bool must_be_aligned = false);
|
|
inline int32_t ReadW(uword addr, Instr* instr);
|
|
inline void WriteW(uword addr, uint32_t value, Instr* instr);
|
|
|
|
inline intptr_t ReadX(uword addr, Instr* instr, bool must_be_aligned = false);
|
|
inline void WriteX(uword addr, intptr_t value, Instr* instr);
|
|
|
|
// Synchronization primitives support.
|
|
void ClearExclusive();
|
|
intptr_t ReadExclusiveX(uword addr, Instr* instr);
|
|
intptr_t WriteExclusiveX(uword addr, intptr_t value, Instr* instr);
|
|
// 32 bit versions.
|
|
intptr_t ReadExclusiveW(uword addr, Instr* instr);
|
|
intptr_t WriteExclusiveW(uword addr, intptr_t value, Instr* instr);
|
|
|
|
// Load Acquire & Store Release.
|
|
intptr_t ReadAcquire(uword addr, Instr* instr);
|
|
uint32_t ReadAcquireW(uword addr, Instr* instr);
|
|
void WriteRelease(uword addr, intptr_t value, Instr* instr);
|
|
void WriteReleaseW(uword addr, uint32_t value, Instr* instr);
|
|
|
|
// Exclusive access reservation: address and value observed during
|
|
// load-exclusive. Store-exclusive verifies that address is the same and
|
|
// performs atomic compare-and-swap with remembered value to observe value
|
|
// changes. This implementation of ldxr/stxr instructions does not detect
|
|
// ABA situation and our uses of ldxr/stxr don't need this detection.
|
|
uword exclusive_access_addr_;
|
|
uword exclusive_access_value_;
|
|
|
|
// Helper functions to set the conditional flags in the architecture state.
|
|
void SetNZFlagsW(int32_t val);
|
|
bool CarryFromW(int32_t left, int32_t right, int32_t carry);
|
|
bool OverflowFromW(int32_t left, int32_t right, int32_t carry);
|
|
|
|
void SetNZFlagsX(int64_t val);
|
|
bool CarryFromX(int64_t alu_out, int64_t left, int64_t right, bool addition);
|
|
bool OverflowFromX(int64_t alu_out,
|
|
int64_t left,
|
|
int64_t right,
|
|
bool addition);
|
|
|
|
void SetCFlag(bool val);
|
|
void SetVFlag(bool val);
|
|
|
|
int64_t ShiftOperand(uint8_t reg_size,
|
|
int64_t value,
|
|
Shift shift_type,
|
|
uint8_t amount);
|
|
|
|
int64_t ExtendOperand(uint8_t reg_size,
|
|
int64_t value,
|
|
Extend extend_type,
|
|
uint8_t amount);
|
|
|
|
int64_t DecodeShiftExtendOperand(Instr* instr);
|
|
|
|
bool ConditionallyExecute(Instr* instr);
|
|
|
|
void DoRedirectedCall(Instr* instr);
|
|
|
|
// Decode instructions.
|
|
void InstructionDecode(Instr* instr);
|
|
void InstructionDecodeImpl(Instr* instr);
|
|
#define DECODE_OP(op) void Decode##op(Instr* instr);
|
|
APPLY_OP_LIST(DECODE_OP)
|
|
#undef DECODE_OP
|
|
|
|
// Executes ARM64 instructions until the PC reaches kEndSimulatingPC.
|
|
void Execute();
|
|
void ExecuteNoTrace();
|
|
void ExecuteTrace();
|
|
|
|
void ClobberVolatileRegisters();
|
|
|
|
// 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 SimulatorDebugger;
|
|
friend class SimulatorSetjmpBuffer;
|
|
DISALLOW_COPY_AND_ASSIGN(Simulator);
|
|
};
|
|
|
|
} // namespace dart
|
|
|
|
#endif // RUNTIME_VM_SIMULATOR_ARM64_H_
|