// Copyright 2013 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef V8_ARM64_SIMULATOR_ARM64_H_ #define V8_ARM64_SIMULATOR_ARM64_H_ #include <stdarg.h> #include <vector> #include "src/allocation.h" #include "src/arm64/assembler-arm64.h" #include "src/arm64/decoder-arm64.h" #include "src/arm64/disasm-arm64.h" #include "src/arm64/instrument-arm64.h" #include "src/assembler.h" #include "src/base/compiler-specific.h" #include "src/globals.h" #include "src/utils.h" namespace v8 { namespace internal { #if !defined(USE_SIMULATOR) // Running without a simulator on a native ARM64 platform. // When running without a simulator we call the entry directly. #define CALL_GENERATED_CODE(isolate, entry, p0, p1, p2, p3, p4) \ (entry(p0, p1, p2, p3, p4)) typedef int (*arm64_regexp_matcher)(String* input, int64_t start_offset, const byte* input_start, const byte* input_end, int* output, int64_t output_size, Address stack_base, int64_t direct_call, void* return_address, Isolate* isolate); // Call the generated regexp code directly. The code at the entry address // should act as a function matching the type arm64_regexp_matcher. // The ninth argument is a dummy that reserves the space used for // the return address added by the ExitFrame in native calls. #define CALL_GENERATED_REGEXP_CODE(isolate, entry, p0, p1, p2, p3, p4, p5, p6, \ p7, p8) \ (FUNCTION_CAST<arm64_regexp_matcher>(entry)(p0, p1, p2, p3, p4, p5, p6, p7, \ NULL, p8)) // Running without a simulator there is nothing to do. class SimulatorStack : public v8::internal::AllStatic { public: static uintptr_t JsLimitFromCLimit(v8::internal::Isolate* isolate, uintptr_t c_limit) { USE(isolate); return c_limit; } static uintptr_t RegisterCTryCatch(v8::internal::Isolate* isolate, uintptr_t try_catch_address) { USE(isolate); return try_catch_address; } static void UnregisterCTryCatch(v8::internal::Isolate* isolate) { USE(isolate); } }; #else // !defined(USE_SIMULATOR) // The proper way to initialize a simulated system register (such as NZCV) is as // follows: // SimSystemRegister nzcv = SimSystemRegister::DefaultValueFor(NZCV); class SimSystemRegister { public: // The default constructor represents a register which has no writable bits. // It is not possible to set its value to anything other than 0. SimSystemRegister() : value_(0), write_ignore_mask_(0xffffffff) { } uint32_t RawValue() const { return value_; } void SetRawValue(uint32_t new_value) { value_ = (value_ & write_ignore_mask_) | (new_value & ~write_ignore_mask_); } uint32_t Bits(int msb, int lsb) const { return unsigned_bitextract_32(msb, lsb, value_); } int32_t SignedBits(int msb, int lsb) const { return signed_bitextract_32(msb, lsb, value_); } void SetBits(int msb, int lsb, uint32_t bits); // Default system register values. static SimSystemRegister DefaultValueFor(SystemRegister id); #define DEFINE_GETTER(Name, HighBit, LowBit, Func, Type) \ Type Name() const { return static_cast<Type>(Func(HighBit, LowBit)); } \ void Set##Name(Type bits) { \ SetBits(HighBit, LowBit, static_cast<Type>(bits)); \ } #define DEFINE_WRITE_IGNORE_MASK(Name, Mask) \ static const uint32_t Name##WriteIgnoreMask = ~static_cast<uint32_t>(Mask); SYSTEM_REGISTER_FIELDS_LIST(DEFINE_GETTER, DEFINE_WRITE_IGNORE_MASK) #undef DEFINE_ZERO_BITS #undef DEFINE_GETTER protected: // Most system registers only implement a few of the bits in the word. Other // bits are "read-as-zero, write-ignored". The write_ignore_mask argument // describes the bits which are not modifiable. SimSystemRegister(uint32_t value, uint32_t write_ignore_mask) : value_(value), write_ignore_mask_(write_ignore_mask) { } uint32_t value_; uint32_t write_ignore_mask_; }; // Represent a register (r0-r31, v0-v31). class SimRegisterBase { public: template<typename T> void Set(T new_value) { value_ = 0; memcpy(&value_, &new_value, sizeof(T)); } template<typename T> T Get() const { T result; memcpy(&result, &value_, sizeof(T)); return result; } protected: int64_t value_; }; typedef SimRegisterBase SimRegister; // r0-r31 typedef SimRegisterBase SimFPRegister; // v0-v31 class Simulator : public DecoderVisitor { public: static void FlushICache(base::CustomMatcherHashMap* i_cache, void* start, size_t size) { USE(i_cache); USE(start); USE(size); } explicit Simulator(Decoder<DispatchingDecoderVisitor>* decoder, Isolate* isolate = NULL, FILE* stream = stderr); Simulator(); ~Simulator(); // System functions. static void Initialize(Isolate* isolate); static void TearDown(base::CustomMatcherHashMap* i_cache, Redirection* first); static Simulator* current(v8::internal::Isolate* isolate); class CallArgument; // Call an arbitrary function taking an arbitrary number of arguments. The // varargs list must be a set of arguments with type CallArgument, and // terminated by CallArgument::End(). void CallVoid(byte* entry, CallArgument* args); // Like CallVoid, but expect a return value. int64_t CallInt64(byte* entry, CallArgument* args); double CallDouble(byte* entry, CallArgument* args); // V8 calls into generated JS code with 5 parameters and into // generated RegExp code with 10 parameters. These are convenience functions, // which set up the simulator state and grab the result on return. int64_t CallJS(byte* entry, Object* new_target, Object* target, Object* revc, int64_t argc, Object*** argv); int64_t CallRegExp(byte* entry, String* input, int64_t start_offset, const byte* input_start, const byte* input_end, int* output, int64_t output_size, Address stack_base, int64_t direct_call, void* return_address, Isolate* isolate); // A wrapper class that stores an argument for one of the above Call // functions. // // Only arguments up to 64 bits in size are supported. class CallArgument { public: template<typename T> explicit CallArgument(T argument) { bits_ = 0; DCHECK(sizeof(argument) <= sizeof(bits_)); memcpy(&bits_, &argument, sizeof(argument)); type_ = X_ARG; } explicit CallArgument(double argument) { DCHECK(sizeof(argument) == sizeof(bits_)); memcpy(&bits_, &argument, sizeof(argument)); type_ = D_ARG; } explicit CallArgument(float argument) { // TODO(all): CallArgument(float) is untested, remove this check once // tested. UNIMPLEMENTED(); // Make the D register a NaN to try to trap errors if the callee expects a // double. If it expects a float, the callee should ignore the top word. DCHECK(sizeof(kFP64SignallingNaN) == sizeof(bits_)); memcpy(&bits_, &kFP64SignallingNaN, sizeof(kFP64SignallingNaN)); // Write the float payload to the S register. DCHECK(sizeof(argument) <= sizeof(bits_)); memcpy(&bits_, &argument, sizeof(argument)); type_ = D_ARG; } // This indicates the end of the arguments list, so that CallArgument // objects can be passed into varargs functions. static CallArgument End() { return CallArgument(); } int64_t bits() const { return bits_; } bool IsEnd() const { return type_ == NO_ARG; } bool IsX() const { return type_ == X_ARG; } bool IsD() const { return type_ == D_ARG; } private: enum CallArgumentType { X_ARG, D_ARG, NO_ARG }; // All arguments are aligned to at least 64 bits and we don't support // passing bigger arguments, so the payload size can be fixed at 64 bits. int64_t bits_; CallArgumentType type_; CallArgument() { type_ = NO_ARG; } }; // Start the debugging command line. void Debug(); bool GetValue(const char* desc, int64_t* value); bool PrintValue(const char* desc); // Push an address onto the JS stack. uintptr_t PushAddress(uintptr_t address); // Pop an address from the JS stack. uintptr_t PopAddress(); // Accessor to the internal simulator stack area. uintptr_t StackLimit(uintptr_t c_limit) const; void ResetState(); // Runtime call support. static void* RedirectExternalReference(Isolate* isolate, void* external_function, ExternalReference::Type type); void DoRuntimeCall(Instruction* instr); // Run the simulator. static const Instruction* kEndOfSimAddress; void DecodeInstruction(); void Run(); void RunFrom(Instruction* start); // Simulation helpers. template <typename T> void set_pc(T new_pc) { DCHECK(sizeof(T) == sizeof(pc_)); memcpy(&pc_, &new_pc, sizeof(T)); pc_modified_ = true; } Instruction* pc() { return pc_; } void increment_pc() { if (!pc_modified_) { pc_ = pc_->following(); } pc_modified_ = false; } virtual void Decode(Instruction* instr) { decoder_->Decode(instr); } void ExecuteInstruction() { DCHECK(IsAligned(reinterpret_cast<uintptr_t>(pc_), kInstructionSize)); CheckBreakNext(); Decode(pc_); increment_pc(); CheckBreakpoints(); } // Declare all Visitor functions. #define DECLARE(A) void Visit##A(Instruction* instr); VISITOR_LIST(DECLARE) #undef DECLARE bool IsZeroRegister(unsigned code, Reg31Mode r31mode) const { return ((code == 31) && (r31mode == Reg31IsZeroRegister)); } // Register accessors. // Return 'size' bits of the value of an integer register, as the specified // type. The value is zero-extended to fill the result. // template<typename T> T reg(unsigned code, Reg31Mode r31mode = Reg31IsZeroRegister) const { DCHECK(code < kNumberOfRegisters); if (IsZeroRegister(code, r31mode)) { return 0; } return registers_[code].Get<T>(); } // Common specialized accessors for the reg() template. int32_t wreg(unsigned code, Reg31Mode r31mode = Reg31IsZeroRegister) const { return reg<int32_t>(code, r31mode); } int64_t xreg(unsigned code, Reg31Mode r31mode = Reg31IsZeroRegister) const { return reg<int64_t>(code, r31mode); } // Write 'value' into an integer register. The value is zero-extended. This // behaviour matches AArch64 register writes. template<typename T> void set_reg(unsigned code, T value, Reg31Mode r31mode = Reg31IsZeroRegister) { set_reg_no_log(code, value, r31mode); LogRegister(code, r31mode); } // Common specialized accessors for the set_reg() template. void set_wreg(unsigned code, int32_t value, Reg31Mode r31mode = Reg31IsZeroRegister) { set_reg(code, value, r31mode); } void set_xreg(unsigned code, int64_t value, Reg31Mode r31mode = Reg31IsZeroRegister) { set_reg(code, value, r31mode); } // As above, but don't automatically log the register update. template <typename T> void set_reg_no_log(unsigned code, T value, Reg31Mode r31mode = Reg31IsZeroRegister) { DCHECK(code < kNumberOfRegisters); if (!IsZeroRegister(code, r31mode)) { registers_[code].Set(value); } } void set_wreg_no_log(unsigned code, int32_t value, Reg31Mode r31mode = Reg31IsZeroRegister) { set_reg_no_log(code, value, r31mode); } void set_xreg_no_log(unsigned code, int64_t value, Reg31Mode r31mode = Reg31IsZeroRegister) { set_reg_no_log(code, value, r31mode); } // Commonly-used special cases. template<typename T> void set_lr(T value) { DCHECK(sizeof(T) == kPointerSize); set_reg(kLinkRegCode, value); } template<typename T> void set_sp(T value) { DCHECK(sizeof(T) == kPointerSize); set_reg(31, value, Reg31IsStackPointer); } int64_t sp() { return xreg(31, Reg31IsStackPointer); } int64_t jssp() { return xreg(kJSSPCode, Reg31IsStackPointer); } int64_t fp() { return xreg(kFramePointerRegCode, Reg31IsStackPointer); } Instruction* lr() { return reg<Instruction*>(kLinkRegCode); } Address get_sp() const { return reg<Address>(31, Reg31IsStackPointer); } template<typename T> T fpreg(unsigned code) const { DCHECK(code < kNumberOfRegisters); return fpregisters_[code].Get<T>(); } // Common specialized accessors for the fpreg() template. float sreg(unsigned code) const { return fpreg<float>(code); } uint32_t sreg_bits(unsigned code) const { return fpreg<uint32_t>(code); } double dreg(unsigned code) const { return fpreg<double>(code); } uint64_t dreg_bits(unsigned code) const { return fpreg<uint64_t>(code); } double fpreg(unsigned size, unsigned code) const { switch (size) { case kSRegSizeInBits: return sreg(code); case kDRegSizeInBits: return dreg(code); default: UNREACHABLE(); return 0.0; } } // Write 'value' into a floating-point register. The value is zero-extended. // This behaviour matches AArch64 register writes. template<typename T> void set_fpreg(unsigned code, T value) { set_fpreg_no_log(code, value); if (sizeof(value) <= kSRegSize) { LogFPRegister(code, kPrintSRegValue); } else { LogFPRegister(code, kPrintDRegValue); } } // Common specialized accessors for the set_fpreg() template. void set_sreg(unsigned code, float value) { set_fpreg(code, value); } void set_sreg_bits(unsigned code, uint32_t value) { set_fpreg(code, value); } void set_dreg(unsigned code, double value) { set_fpreg(code, value); } void set_dreg_bits(unsigned code, uint64_t value) { set_fpreg(code, value); } // As above, but don't automatically log the register update. template <typename T> void set_fpreg_no_log(unsigned code, T value) { DCHECK((sizeof(value) == kDRegSize) || (sizeof(value) == kSRegSize)); DCHECK(code < kNumberOfFPRegisters); fpregisters_[code].Set(value); } void set_sreg_no_log(unsigned code, float value) { set_fpreg_no_log(code, value); } void set_dreg_no_log(unsigned code, double value) { set_fpreg_no_log(code, value); } SimSystemRegister& nzcv() { return nzcv_; } SimSystemRegister& fpcr() { return fpcr_; } // Debug helpers // Simulator breakpoints. struct Breakpoint { Instruction* location; bool enabled; }; std::vector<Breakpoint> breakpoints_; void SetBreakpoint(Instruction* breakpoint); void ListBreakpoints(); void CheckBreakpoints(); // Helpers for the 'next' command. // When this is set, the Simulator will insert a breakpoint after the next BL // instruction it meets. bool break_on_next_; // Check if the Simulator should insert a break after the current instruction // for the 'next' command. void CheckBreakNext(); // Disassemble instruction at the given address. void PrintInstructionsAt(Instruction* pc, uint64_t count); // Print all registers of the specified types. void PrintRegisters(); void PrintFPRegisters(); void PrintSystemRegisters(); // Like Print* (above), but respect log_parameters(). void LogSystemRegisters() { if (log_parameters() & LOG_SYS_REGS) PrintSystemRegisters(); } void LogRegisters() { if (log_parameters() & LOG_REGS) PrintRegisters(); } void LogFPRegisters() { if (log_parameters() & LOG_FP_REGS) PrintFPRegisters(); } // Specify relevant register sizes, for PrintFPRegister. // // These values are bit masks; they can be combined in case multiple views of // a machine register are interesting. enum PrintFPRegisterSizes { kPrintDRegValue = 1 << kDRegSize, kPrintSRegValue = 1 << kSRegSize, kPrintAllFPRegValues = kPrintDRegValue | kPrintSRegValue }; // Print individual register values (after update). void PrintRegister(unsigned code, Reg31Mode r31mode = Reg31IsStackPointer); void PrintFPRegister(unsigned code, PrintFPRegisterSizes sizes = kPrintAllFPRegValues); void PrintSystemRegister(SystemRegister id); // Like Print* (above), but respect log_parameters(). void LogRegister(unsigned code, Reg31Mode r31mode = Reg31IsStackPointer) { if (log_parameters() & LOG_REGS) PrintRegister(code, r31mode); } void LogFPRegister(unsigned code, PrintFPRegisterSizes sizes = kPrintAllFPRegValues) { if (log_parameters() & LOG_FP_REGS) PrintFPRegister(code, sizes); } void LogSystemRegister(SystemRegister id) { if (log_parameters() & LOG_SYS_REGS) PrintSystemRegister(id); } // Print memory accesses. void PrintRead(uintptr_t address, size_t size, unsigned reg_code); void PrintReadFP(uintptr_t address, size_t size, unsigned reg_code); void PrintWrite(uintptr_t address, size_t size, unsigned reg_code); void PrintWriteFP(uintptr_t address, size_t size, unsigned reg_code); // Like Print* (above), but respect log_parameters(). void LogRead(uintptr_t address, size_t size, unsigned reg_code) { if (log_parameters() & LOG_REGS) PrintRead(address, size, reg_code); } void LogReadFP(uintptr_t address, size_t size, unsigned reg_code) { if (log_parameters() & LOG_FP_REGS) PrintReadFP(address, size, reg_code); } void LogWrite(uintptr_t address, size_t size, unsigned reg_code) { if (log_parameters() & LOG_WRITE) PrintWrite(address, size, reg_code); } void LogWriteFP(uintptr_t address, size_t size, unsigned reg_code) { if (log_parameters() & LOG_WRITE) PrintWriteFP(address, size, reg_code); } int log_parameters() { return log_parameters_; } void set_log_parameters(int new_parameters) { log_parameters_ = new_parameters; if (!decoder_) { if (new_parameters & LOG_DISASM) { PrintF("Run --debug-sim to dynamically turn on disassembler\n"); } return; } if (new_parameters & LOG_DISASM) { decoder_->InsertVisitorBefore(print_disasm_, this); } else { decoder_->RemoveVisitor(print_disasm_); } } static inline const char* WRegNameForCode(unsigned code, Reg31Mode mode = Reg31IsZeroRegister); static inline const char* XRegNameForCode(unsigned code, Reg31Mode mode = Reg31IsZeroRegister); static inline const char* SRegNameForCode(unsigned code); static inline const char* DRegNameForCode(unsigned code); static inline const char* VRegNameForCode(unsigned code); static inline int CodeFromName(const char* name); protected: // Simulation helpers ------------------------------------ bool ConditionPassed(Condition cond) { SimSystemRegister& flags = nzcv(); switch (cond) { case eq: return flags.Z(); case ne: return !flags.Z(); case hs: return flags.C(); case lo: return !flags.C(); case mi: return flags.N(); case pl: return !flags.N(); case vs: return flags.V(); case vc: return !flags.V(); case hi: return flags.C() && !flags.Z(); case ls: return !(flags.C() && !flags.Z()); case ge: return flags.N() == flags.V(); case lt: return flags.N() != flags.V(); case gt: return !flags.Z() && (flags.N() == flags.V()); case le: return !(!flags.Z() && (flags.N() == flags.V())); case nv: // Fall through. case al: return true; default: UNREACHABLE(); return false; } } bool ConditionFailed(Condition cond) { return !ConditionPassed(cond); } template<typename T> void AddSubHelper(Instruction* instr, T op2); template <typename T> T AddWithCarry(bool set_flags, T left, T right, int carry_in = 0); template<typename T> void AddSubWithCarry(Instruction* instr); template<typename T> void LogicalHelper(Instruction* instr, T op2); template<typename T> void ConditionalCompareHelper(Instruction* instr, T op2); void LoadStoreHelper(Instruction* instr, int64_t offset, AddrMode addrmode); void LoadStorePairHelper(Instruction* instr, AddrMode addrmode); uintptr_t LoadStoreAddress(unsigned addr_reg, int64_t offset, AddrMode addrmode); void LoadStoreWriteBack(unsigned addr_reg, int64_t offset, AddrMode addrmode); void CheckMemoryAccess(uintptr_t address, uintptr_t stack); // Memory read helpers. template <typename T, typename A> T MemoryRead(A address) { T value; STATIC_ASSERT((sizeof(value) == 1) || (sizeof(value) == 2) || (sizeof(value) == 4) || (sizeof(value) == 8)); memcpy(&value, reinterpret_cast<const void*>(address), sizeof(value)); return value; } // Memory write helpers. template <typename T, typename A> void MemoryWrite(A address, T value) { STATIC_ASSERT((sizeof(value) == 1) || (sizeof(value) == 2) || (sizeof(value) == 4) || (sizeof(value) == 8)); memcpy(reinterpret_cast<void*>(address), &value, sizeof(value)); } template <typename T> T ShiftOperand(T value, Shift shift_type, unsigned amount); template <typename T> T ExtendValue(T value, Extend extend_type, unsigned left_shift = 0); template <typename T> void Extract(Instruction* instr); template <typename T> void DataProcessing2Source(Instruction* instr); template <typename T> void BitfieldHelper(Instruction* instr); template <typename T> T FPDefaultNaN() const; void FPCompare(double val0, double val1); double FPRoundInt(double value, FPRounding round_mode); double FPToDouble(float value); float FPToFloat(double value, FPRounding round_mode); double FixedToDouble(int64_t src, int fbits, FPRounding round_mode); double UFixedToDouble(uint64_t src, int fbits, FPRounding round_mode); float FixedToFloat(int64_t src, int fbits, FPRounding round_mode); float UFixedToFloat(uint64_t src, int fbits, FPRounding round_mode); int32_t FPToInt32(double value, FPRounding rmode); int64_t FPToInt64(double value, FPRounding rmode); uint32_t FPToUInt32(double value, FPRounding rmode); uint64_t FPToUInt64(double value, FPRounding rmode); template <typename T> T FPAdd(T op1, T op2); template <typename T> T FPDiv(T op1, T op2); template <typename T> T FPMax(T a, T b); template <typename T> T FPMaxNM(T a, T b); template <typename T> T FPMin(T a, T b); template <typename T> T FPMinNM(T a, T b); template <typename T> T FPMul(T op1, T op2); template <typename T> T FPMulAdd(T a, T op1, T op2); template <typename T> T FPSqrt(T op); template <typename T> T FPSub(T op1, T op2); // Standard NaN processing. template <typename T> T FPProcessNaN(T op); bool FPProcessNaNs(Instruction* instr); template <typename T> T FPProcessNaNs(T op1, T op2); template <typename T> T FPProcessNaNs3(T op1, T op2, T op3); void CheckStackAlignment(); inline void CheckPCSComplianceAndRun(); #ifdef DEBUG // Corruption values should have their least significant byte cleared to // allow the code of the register being corrupted to be inserted. static const uint64_t kCallerSavedRegisterCorruptionValue = 0xca11edc0de000000UL; // This value is a NaN in both 32-bit and 64-bit FP. static const uint64_t kCallerSavedFPRegisterCorruptionValue = 0x7ff000007f801000UL; // This value is a mix of 32/64-bits NaN and "verbose" immediate. static const uint64_t kDefaultCPURegisterCorruptionValue = 0x7ffbad007f8bad00UL; void CorruptRegisters(CPURegList* list, uint64_t value = kDefaultCPURegisterCorruptionValue); void CorruptAllCallerSavedCPURegisters(); #endif // Pseudo Printf instruction void DoPrintf(Instruction* instr); // Processor state --------------------------------------- // Output stream. FILE* stream_; PrintDisassembler* print_disasm_; void PRINTF_FORMAT(2, 3) TraceSim(const char* format, ...); // Instrumentation. Instrument* instrument_; // General purpose registers. Register 31 is the stack pointer. SimRegister registers_[kNumberOfRegisters]; // Floating point registers SimFPRegister fpregisters_[kNumberOfFPRegisters]; // Processor state // bits[31, 27]: Condition flags N, Z, C, and V. // (Negative, Zero, Carry, Overflow) SimSystemRegister nzcv_; // Floating-Point Control Register SimSystemRegister fpcr_; // Only a subset of FPCR features are supported by the simulator. This helper // checks that the FPCR settings are supported. // // This is checked when floating-point instructions are executed, not when // FPCR is set. This allows generated code to modify FPCR for external // functions, or to save and restore it when entering and leaving generated // code. void AssertSupportedFPCR() { DCHECK(fpcr().FZ() == 0); // No flush-to-zero support. DCHECK(fpcr().RMode() == FPTieEven); // Ties-to-even rounding only. // The simulator does not support half-precision operations so fpcr().AHP() // is irrelevant, and is not checked here. } template <typename T> static int CalcNFlag(T result) { return (result >> (sizeof(T) * 8 - 1)) & 1; } static int CalcZFlag(uint64_t result) { return result == 0; } static const uint32_t kConditionFlagsMask = 0xf0000000; // Stack uintptr_t stack_; static const size_t stack_protection_size_ = KB; size_t stack_size_; uintptr_t stack_limit_; Decoder<DispatchingDecoderVisitor>* decoder_; Decoder<DispatchingDecoderVisitor>* disassembler_decoder_; // Indicates if the pc has been modified by the instruction and should not be // automatically incremented. bool pc_modified_; Instruction* pc_; static const char* xreg_names[]; static const char* wreg_names[]; static const char* sreg_names[]; static const char* dreg_names[]; static const char* vreg_names[]; // Debugger input. void set_last_debugger_input(char* input) { DeleteArray(last_debugger_input_); last_debugger_input_ = input; } char* last_debugger_input() { return last_debugger_input_; } char* last_debugger_input_; private: void Init(FILE* stream); int log_parameters_; Isolate* isolate_; }; // When running with the simulator transition into simulated execution at this // point. #define CALL_GENERATED_CODE(isolate, entry, p0, p1, p2, p3, p4) \ reinterpret_cast<Object*>(Simulator::current(isolate)->CallJS( \ FUNCTION_ADDR(entry), p0, p1, p2, p3, p4)) #define CALL_GENERATED_REGEXP_CODE(isolate, entry, p0, p1, p2, p3, p4, p5, p6, \ p7, p8) \ static_cast<int>(Simulator::current(isolate)->CallRegExp( \ entry, p0, p1, p2, p3, p4, p5, p6, p7, NULL, p8)) // The simulator has its own stack. Thus it has a different stack limit from // the C-based native code. The JS-based limit normally points near the end of // the simulator stack. When the C-based limit is exhausted we reflect that by // lowering the JS-based limit as well, to make stack checks trigger. class SimulatorStack : public v8::internal::AllStatic { public: static uintptr_t JsLimitFromCLimit(v8::internal::Isolate* isolate, uintptr_t c_limit) { return Simulator::current(isolate)->StackLimit(c_limit); } static uintptr_t RegisterCTryCatch(v8::internal::Isolate* isolate, uintptr_t try_catch_address) { Simulator* sim = Simulator::current(isolate); return sim->PushAddress(try_catch_address); } static void UnregisterCTryCatch(v8::internal::Isolate* isolate) { Simulator::current(isolate)->PopAddress(); } }; #endif // !defined(USE_SIMULATOR) } // namespace internal } // namespace v8 #endif // V8_ARM64_SIMULATOR_ARM64_H_