| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
| // Copyright 2011 the V8 project authors. All rights reserved. |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: |
| // |
| // * Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above |
| // copyright notice, this list of conditions and the following |
| // disclaimer in the documentation and/or other materials provided |
| // with the distribution. |
| // * Neither the name of Google Inc. nor the names of its |
| // contributors may be used to endorse or promote products derived |
| // from this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| #ifndef jit_mips32_Simulator_mips32_h |
| #define jit_mips32_Simulator_mips32_h |
| |
| #ifdef JS_SIMULATOR_MIPS32 |
| |
| #include "jslock.h" |
| |
| #include "jit/IonTypes.h" |
| |
| namespace js { |
| namespace jit { |
| |
| class Simulator; |
| class Redirection; |
| class CachePage; |
| class AutoLockSimulator; |
| |
| const intptr_t kPointerAlignment = 4; |
| const intptr_t kPointerAlignmentMask = kPointerAlignment - 1; |
| |
| const intptr_t kDoubleAlignment = 8; |
| const intptr_t kDoubleAlignmentMask = kDoubleAlignment - 1; |
| |
| |
| // Number of general purpose registers. |
| const int kNumRegisters = 32; |
| |
| // In the simulator, the PC register is simulated as the 34th register. |
| const int kPCRegister = 34; |
| |
| // Number coprocessor registers. |
| const int kNumFPURegisters = 32; |
| |
| // FPU (coprocessor 1) control registers. Currently only FCSR is implemented. |
| const int kFCSRRegister = 31; |
| const int kInvalidFPUControlRegister = -1; |
| const uint32_t kFPUInvalidResult = static_cast<uint32_t>(1 << 31) - 1; |
| |
| // FCSR constants. |
| const uint32_t kFCSRInexactFlagBit = 2; |
| const uint32_t kFCSRUnderflowFlagBit = 3; |
| const uint32_t kFCSROverflowFlagBit = 4; |
| const uint32_t kFCSRDivideByZeroFlagBit = 5; |
| const uint32_t kFCSRInvalidOpFlagBit = 6; |
| |
| const uint32_t kFCSRInexactFlagMask = 1 << kFCSRInexactFlagBit; |
| const uint32_t kFCSRUnderflowFlagMask = 1 << kFCSRUnderflowFlagBit; |
| const uint32_t kFCSROverflowFlagMask = 1 << kFCSROverflowFlagBit; |
| const uint32_t kFCSRDivideByZeroFlagMask = 1 << kFCSRDivideByZeroFlagBit; |
| const uint32_t kFCSRInvalidOpFlagMask = 1 << kFCSRInvalidOpFlagBit; |
| |
| const uint32_t kFCSRFlagMask = |
| kFCSRInexactFlagMask | |
| kFCSRUnderflowFlagMask | |
| kFCSROverflowFlagMask | |
| kFCSRDivideByZeroFlagMask | |
| kFCSRInvalidOpFlagMask; |
| |
| const uint32_t kFCSRExceptionFlagMask = kFCSRFlagMask ^ kFCSRInexactFlagMask; |
| |
| // On MIPS Simulator breakpoints can have different codes: |
| // - Breaks between 0 and kMaxWatchpointCode are treated as simple watchpoints, |
| // the simulator will run through them and print the registers. |
| // - Breaks between kMaxWatchpointCode and kMaxStopCode are treated as stop() |
| // instructions (see Assembler::stop()). |
| // - Breaks larger than kMaxStopCode are simple breaks, dropping you into the |
| // debugger. |
| const uint32_t kMaxWatchpointCode = 31; |
| const uint32_t kMaxStopCode = 127; |
| |
| // ----------------------------------------------------------------------------- |
| // Utility functions |
| |
| typedef uint32_t Instr; |
| class SimInstruction; |
| |
| class Simulator { |
| friend class Redirection; |
| friend class MipsDebugger; |
| friend class AutoLockSimulatorCache; |
| public: |
| |
| // Registers are declared in order. See "See MIPS Run Linux" chapter 2. |
| enum Register { |
| no_reg = -1, |
| zero_reg = 0, |
| at, |
| v0, v1, |
| a0, a1, a2, a3, |
| t0, t1, t2, t3, t4, t5, t6, t7, |
| s0, s1, s2, s3, s4, s5, s6, s7, |
| t8, t9, |
| k0, k1, |
| gp, |
| sp, |
| s8, |
| ra, |
| // LO, HI, and pc. |
| LO, |
| HI, |
| pc, // pc must be the last register. |
| kNumSimuRegisters, |
| // aliases |
| fp = s8 |
| }; |
| |
| // Coprocessor registers. |
| enum FPURegister { |
| f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, |
| f12, f13, f14, f15, // f12 and f14 are arguments FPURegisters. |
| f16, f17, f18, f19, f20, f21, f22, f23, f24, f25, |
| f26, f27, f28, f29, f30, f31, |
| kNumFPURegisters |
| }; |
| |
| // Returns nullptr on OOM. |
| static Simulator* Create(); |
| |
| static void Destroy(Simulator* simulator); |
| |
| // Constructor/destructor are for internal use only; use the static methods above. |
| Simulator(); |
| ~Simulator(); |
| |
| // The currently executing Simulator instance. Potentially there can be one |
| // for each native thread. |
| static Simulator* Current(); |
| |
| static inline uintptr_t StackLimit() { |
| return Simulator::Current()->stackLimit(); |
| } |
| |
| uintptr_t* addressOfStackLimit(); |
| |
| // Accessors for register state. Reading the pc value adheres to the MIPS |
| // architecture specification and is off by a 8 from the currently executing |
| // instruction. |
| void setRegister(int reg, int32_t value); |
| int32_t getRegister(int reg) const; |
| double getDoubleFromRegisterPair(int reg); |
| // Same for FPURegisters. |
| void setFpuRegister(int fpureg, int32_t value); |
| void setFpuRegisterFloat(int fpureg, float value); |
| void setFpuRegisterFloat(int fpureg, int64_t value); |
| void setFpuRegisterDouble(int fpureg, double value); |
| void setFpuRegisterDouble(int fpureg, int64_t value); |
| int32_t getFpuRegister(int fpureg) const; |
| int64_t getFpuRegisterLong(int fpureg) const; |
| float getFpuRegisterFloat(int fpureg) const; |
| double getFpuRegisterDouble(int fpureg) const; |
| void setFCSRBit(uint32_t cc, bool value); |
| bool testFCSRBit(uint32_t cc); |
| bool setFCSRRoundError(double original, double rounded); |
| |
| // Special case of set_register and get_register to access the raw PC value. |
| void set_pc(int32_t value); |
| int32_t get_pc() const; |
| |
| template <typename T> |
| T get_pc_as() const { return reinterpret_cast<T>(get_pc()); } |
| |
| void set_resume_pc(void* value) { |
| resume_pc_ = int32_t(value); |
| } |
| |
| // Accessor to the internal simulator stack area. |
| uintptr_t stackLimit() const; |
| bool overRecursed(uintptr_t newsp = 0) const; |
| bool overRecursedWithExtra(uint32_t extra) const; |
| |
| // Executes MIPS instructions until the PC reaches end_sim_pc. |
| template<bool enableStopSimAt> |
| void execute(); |
| |
| // Sets up the simulator state and grabs the result on return. |
| int32_t call(uint8_t* entry, int argument_count, ...); |
| |
| // Push an address onto the JS stack. |
| uintptr_t pushAddress(uintptr_t address); |
| |
| // Pop an address from the JS stack. |
| uintptr_t popAddress(); |
| |
| // Debugger input. |
| void setLastDebuggerInput(char* input); |
| char* lastDebuggerInput() { return lastDebuggerInput_; } |
| // ICache checking. |
| static void FlushICache(void* start, size_t size); |
| |
| // Returns true if pc register contains one of the 'SpecialValues' defined |
| // below (bad_ra, end_sim_pc). |
| bool has_bad_pc() const; |
| |
| private: |
| enum SpecialValues { |
| // Known bad pc value to ensure that the simulator does not execute |
| // without being properly setup. |
| bad_ra = -1, |
| // A pc value used to signal the simulator to stop execution. Generally |
| // the ra 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. |
| end_sim_pc = -2, |
| // Unpredictable value. |
| Unpredictable = 0xbadbeaf |
| }; |
| |
| bool init(); |
| |
| // Unsupported instructions use Format to print an error and stop execution. |
| void format(SimInstruction* instr, const char* format); |
| |
| // Read and write memory. |
| inline uint32_t readBU(uint32_t addr); |
| inline int32_t readB(uint32_t addr); |
| inline void writeB(uint32_t addr, uint8_t value); |
| inline void writeB(uint32_t addr, int8_t value); |
| |
| inline uint16_t readHU(uint32_t addr, SimInstruction* instr); |
| inline int16_t readH(uint32_t addr, SimInstruction* instr); |
| // Note: Overloaded on the sign of the value. |
| inline void writeH(uint32_t addr, uint16_t value, SimInstruction* instr); |
| inline void writeH(uint32_t addr, int16_t value, SimInstruction* instr); |
| |
| inline int readW(uint32_t addr, SimInstruction* instr); |
| inline void writeW(uint32_t addr, int value, SimInstruction* instr); |
| |
| inline double readD(uint32_t addr, SimInstruction* instr); |
| inline void writeD(uint32_t addr, double value, SimInstruction* instr); |
| |
| // Executing is handled based on the instruction type. |
| void decodeTypeRegister(SimInstruction* instr); |
| |
| // Helper function for decodeTypeRegister. |
| void configureTypeRegister(SimInstruction* instr, |
| int32_t& alu_out, |
| int64_t& i64hilo, |
| uint64_t& u64hilo, |
| int32_t& next_pc, |
| int32_t& return_addr_reg, |
| bool& do_interrupt); |
| |
| void decodeTypeImmediate(SimInstruction* instr); |
| void decodeTypeJump(SimInstruction* instr); |
| |
| // Used for breakpoints and traps. |
| void softwareInterrupt(SimInstruction* instr); |
| |
| // Stop helper functions. |
| bool isWatchpoint(uint32_t code); |
| void printWatchpoint(uint32_t code); |
| void handleStop(uint32_t code, SimInstruction* instr); |
| bool isStopInstruction(SimInstruction* instr); |
| bool isEnabledStop(uint32_t code); |
| void enableStop(uint32_t code); |
| void disableStop(uint32_t code); |
| void increaseStopCounter(uint32_t code); |
| void printStopInfo(uint32_t code); |
| |
| |
| // Executes one instruction. |
| void instructionDecode(SimInstruction* instr); |
| // Execute one instruction placed in a branch delay slot. |
| void branchDelayInstructionDecode(SimInstruction* instr); |
| |
| public: |
| static bool ICacheCheckingEnabled; |
| |
| static int StopSimAt; |
| |
| // Runtime call support. |
| static void* RedirectNativeFunction(void* nativeFunction, ABIFunctionType type); |
| |
| private: |
| enum Exception { |
| kNone, |
| kIntegerOverflow, |
| kIntegerUnderflow, |
| kDivideByZero, |
| kNumExceptions |
| }; |
| int16_t exceptions[kNumExceptions]; |
| |
| // Exceptions. |
| void signalExceptions(); |
| |
| // Handle arguments and return value for runtime FP functions. |
| void getFpArgs(double* x, double* y, int32_t* z); |
| void getFpFromStack(int32_t* stack, double* x); |
| |
| void setCallResultDouble(double result); |
| void setCallResultFloat(float result); |
| void setCallResult(int64_t res); |
| |
| void callInternal(uint8_t* entry); |
| |
| // Architecture state. |
| // Registers. |
| int32_t registers_[kNumSimuRegisters]; |
| // Coprocessor Registers. |
| int32_t FPUregisters_[kNumFPURegisters]; |
| // FPU control register. |
| uint32_t FCSR_; |
| |
| // Simulator support. |
| char* stack_; |
| uintptr_t stackLimit_; |
| bool pc_modified_; |
| int icount_; |
| int break_count_; |
| |
| int32_t resume_pc_; |
| |
| // Debugger input. |
| char* lastDebuggerInput_; |
| |
| // Registered breakpoints. |
| SimInstruction* break_pc_; |
| Instr break_instr_; |
| |
| // A stop is watched if its code is less than kNumOfWatchedStops. |
| // Only watched stops support enabling/disabling and the counter feature. |
| static const uint32_t kNumOfWatchedStops = 256; |
| |
| |
| // Stop is disabled if bit 31 is set. |
| static const uint32_t kStopDisabledBit = 1U << 31; |
| |
| // A stop is enabled, meaning the simulator will stop when meeting the |
| // instruction, if bit 31 of watchedStops_[code].count is unset. |
| // The value watchedStops_[code].count & ~(1 << 31) indicates how many times |
| // the breakpoint was hit or gone through. |
| struct StopCountAndDesc { |
| uint32_t count_; |
| char* desc_; |
| }; |
| StopCountAndDesc watchedStops_[kNumOfWatchedStops]; |
| |
| private: |
| // ICache checking. |
| struct ICacheHasher { |
| typedef void* Key; |
| typedef void* Lookup; |
| static HashNumber hash(const Lookup& l); |
| static bool match(const Key& k, const Lookup& l); |
| }; |
| |
| public: |
| typedef HashMap<void*, CachePage*, ICacheHasher, SystemAllocPolicy> ICacheMap; |
| |
| private: |
| // This lock creates a critical section around 'redirection_' and |
| // 'icache_', which are referenced both by the execution engine |
| // and by the off-thread compiler (see Redirection::Get in the cpp file). |
| PRLock* cacheLock_; |
| #ifdef DEBUG |
| PRThread* cacheLockHolder_; |
| #endif |
| |
| Redirection* redirection_; |
| ICacheMap icache_; |
| |
| public: |
| ICacheMap& icache() { |
| // Technically we need the lock to access the innards of the |
| // icache, not to take its address, but the latter condition |
| // serves as a useful complement to the former. |
| MOZ_ASSERT(cacheLockHolder_); |
| return icache_; |
| } |
| |
| Redirection* redirection() const { |
| MOZ_ASSERT(cacheLockHolder_); |
| return redirection_; |
| } |
| |
| void setRedirection(js::jit::Redirection* redirection) { |
| MOZ_ASSERT(cacheLockHolder_); |
| redirection_ = redirection; |
| } |
| }; |
| |
| #define JS_CHECK_SIMULATOR_RECURSION_WITH_EXTRA(cx, extra, onerror) \ |
| JS_BEGIN_MACRO \ |
| if (cx->mainThread().simulator()->overRecursedWithExtra(extra)) { \ |
| js::ReportOverRecursed(cx); \ |
| onerror; \ |
| } \ |
| JS_END_MACRO |
| |
| } // namespace jit |
| } // namespace js |
| |
| #endif /* JS_SIMULATOR_MIPS32 */ |
| |
| #endif /* jit_mips32_Simulator_mips32_h */ |