|  | // 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" | 
|  |  | 
|  | 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_; | 
|  | Random random_; | 
|  | 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_ |