| // 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" | |
| #include "vm/random.h" | |
| #include "vm/simulator_memory.h" | |
| namespace dart { | |
| class Isolate; | |
| class Mutex; | |
| class SimulatorSetjmpBuffer; | |
| class Thread; | |
| struct CallbackContext; | |
| struct CallbackMetadata; | |
| 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 thread. | |
| 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); | |
| #if defined(SIMULATOR_FFI) && defined(HOST_ARCH_ARM64) | |
| void DoCompiledFfiCallback(Thread* thread, | |
| CallbackContext* ctxt, | |
| const CallbackMetadata* metadata); | |
| void DoRedirectedFfiCallback(Thread* thread, | |
| CallbackContext* ctxt, | |
| const CallbackMetadata* metadata); | |
| #endif | |
| // 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_; | |
| Random random_; | |
| SimulatorSetjmpBuffer* last_setjmp_buffer_; | |
| SimulatorMemory memory_; | |
| // 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. | |
| uint32_t ReadExclusiveW(uword addr, Instr* instr); | |
| intptr_t WriteExclusiveW(uword addr, uint32_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_ = 0; | |
| uword exclusive_access_value_ = 0; | |
| // 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); | |
| void DoRedirectedFfiCall(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_ |