/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- * vim: set ts=8 sts=2 et sw=2 tw=80: */ // Copyright 2020 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. #include "jit/loong64/Simulator-loong64.h" #include #include #include "jit/AtomicOperations.h" #include "jit/loong64/Assembler-loong64.h" #include "js/Conversions.h" #include "threading/LockGuard.h" #include "vm/JSContext.h" #include "vm/Runtime.h" #include "wasm/WasmInstance.h" #include "wasm/WasmSignalHandlers.h" #define I8(v) static_cast(v) #define I16(v) static_cast(v) #define U16(v) static_cast(v) #define I32(v) static_cast(v) #define U32(v) static_cast(v) #define I64(v) static_cast(v) #define U64(v) static_cast(v) #define I128(v) static_cast<__int128_t>(v) #define U128(v) static_cast<__uint128_t>(v) #define I32_CHECK(v) \ ({ \ MOZ_ASSERT(I64(I32(v)) == I64(v)); \ I32((v)); \ }) namespace js { namespace jit { static int64_t MultiplyHighSigned(int64_t u, int64_t v) { uint64_t u0, v0, w0; int64_t u1, v1, w1, w2, t; u0 = u & 0xFFFFFFFFL; u1 = u >> 32; v0 = v & 0xFFFFFFFFL; v1 = v >> 32; w0 = u0 * v0; t = u1 * v0 + (w0 >> 32); w1 = t & 0xFFFFFFFFL; w2 = t >> 32; w1 = u0 * v1 + w1; return u1 * v1 + w2 + (w1 >> 32); } static uint64_t MultiplyHighUnsigned(uint64_t u, uint64_t v) { uint64_t u0, v0, w0; uint64_t u1, v1, w1, w2, t; u0 = u & 0xFFFFFFFFL; u1 = u >> 32; v0 = v & 0xFFFFFFFFL; v1 = v >> 32; w0 = u0 * v0; t = u1 * v0 + (w0 >> 32); w1 = t & 0xFFFFFFFFL; w2 = t >> 32; w1 = u0 * v1 + w1; return u1 * v1 + w2 + (w1 >> 32); } // Precondition: 0 <= shift < 32 inline constexpr uint32_t RotateRight32(uint32_t value, uint32_t shift) { return (value >> shift) | (value << ((32 - shift) & 31)); } // Precondition: 0 <= shift < 32 inline constexpr uint32_t RotateLeft32(uint32_t value, uint32_t shift) { return (value << shift) | (value >> ((32 - shift) & 31)); } // Precondition: 0 <= shift < 64 inline constexpr uint64_t RotateRight64(uint64_t value, uint64_t shift) { return (value >> shift) | (value << ((64 - shift) & 63)); } // Precondition: 0 <= shift < 64 inline constexpr uint64_t RotateLeft64(uint64_t value, uint64_t shift) { return (value << shift) | (value >> ((64 - shift) & 63)); } // break instr with MAX_BREAK_CODE. static const Instr kCallRedirInstr = op_break | CODEMask; // ----------------------------------------------------------------------------- // LoongArch64 assembly various constants. class SimInstruction { public: enum { kInstrSize = 4, // On LoongArch, PC cannot actually be directly accessed. We behave as if PC // was always the value of the current instruction being executed. kPCReadOffset = 0 }; // Get the raw instruction bits. inline Instr instructionBits() const { return *reinterpret_cast(this); } // Set the raw instruction bits to value. inline void setInstructionBits(Instr value) { *reinterpret_cast(this) = value; } // Read one particular bit out of the instruction bits. inline int bit(int nr) const { return (instructionBits() >> nr) & 1; } // Read a bit field out of the instruction bits. inline int bits(int hi, int lo) const { return (instructionBits() >> lo) & ((2 << (hi - lo)) - 1); } // Instruction type. enum Type { kUnsupported = -1, kOp6Type, kOp7Type, kOp8Type, kOp10Type, kOp11Type, kOp12Type, kOp14Type, kOp15Type, kOp16Type, kOp17Type, kOp22Type, kOp24Type }; // Get the encoding type of the instruction. Type instructionType() const; inline int rjValue() const { return bits(RJShift + RJBits - 1, RJShift); } inline int rkValue() const { return bits(RKShift + RKBits - 1, RKShift); } inline int rdValue() const { return bits(RDShift + RDBits - 1, RDShift); } inline int sa2Value() const { return bits(SAShift + SA2Bits - 1, SAShift); } inline int sa3Value() const { return bits(SAShift + SA3Bits - 1, SAShift); } inline int lsbwValue() const { return bits(LSBWShift + LSBWBits - 1, LSBWShift); } inline int msbwValue() const { return bits(MSBWShift + MSBWBits - 1, MSBWShift); } inline int lsbdValue() const { return bits(LSBDShift + LSBDBits - 1, LSBDShift); } inline int msbdValue() const { return bits(MSBDShift + MSBDBits - 1, MSBDShift); } inline int fdValue() const { return bits(FDShift + FDBits - 1, FDShift); } inline int fjValue() const { return bits(FJShift + FJBits - 1, FJShift); } inline int fkValue() const { return bits(FKShift + FKBits - 1, FKShift); } inline int faValue() const { return bits(FAShift + FABits - 1, FAShift); } inline int cdValue() const { return bits(CDShift + CDBits - 1, CDShift); } inline int cjValue() const { return bits(CJShift + CJBits - 1, CJShift); } inline int caValue() const { return bits(CAShift + CABits - 1, CAShift); } inline int condValue() const { return bits(CONDShift + CONDBits - 1, CONDShift); } inline int imm5Value() const { return bits(Imm5Shift + Imm5Bits - 1, Imm5Shift); } inline int imm6Value() const { return bits(Imm6Shift + Imm6Bits - 1, Imm6Shift); } inline int imm12Value() const { return bits(Imm12Shift + Imm12Bits - 1, Imm12Shift); } inline int imm14Value() const { return bits(Imm14Shift + Imm14Bits - 1, Imm14Shift); } inline int imm16Value() const { return bits(Imm16Shift + Imm16Bits - 1, Imm16Shift); } inline int imm20Value() const { return bits(Imm20Shift + Imm20Bits - 1, Imm20Shift); } inline int32_t imm26Value() const { return bits(Imm26Shift + Imm26Bits - 1, Imm26Shift); } // Say if the instruction is a debugger break/trap. bool isTrap() const; private: SimInstruction() = delete; SimInstruction(const SimInstruction& other) = delete; void operator=(const SimInstruction& other) = delete; }; bool SimInstruction::isTrap() const { // is break?? switch (bits(31, 15) << 15) { case op_break: return (instructionBits() != kCallRedirInstr) && (bits(15, 0) != 6); default: return false; }; } SimInstruction::Type SimInstruction::instructionType() const { SimInstruction::Type kType = kUnsupported; // Check for kOp6Type switch (bits(31, 26) << 26) { case op_beqz: case op_bnez: case op_bcz: case op_jirl: case op_b: case op_bl: case op_beq: case op_bne: case op_blt: case op_bge: case op_bltu: case op_bgeu: case op_addu16i_d: kType = kOp6Type; break; default: kType = kUnsupported; } if (kType == kUnsupported) { // Check for kOp7Type switch (bits(31, 25) << 25) { case op_lu12i_w: case op_lu32i_d: case op_pcaddi: case op_pcalau12i: case op_pcaddu12i: case op_pcaddu18i: kType = kOp7Type; break; default: kType = kUnsupported; } } if (kType == kUnsupported) { // Check for kOp8Type switch (bits(31, 24) << 24) { case op_ll_w: case op_sc_w: case op_ll_d: case op_sc_d: case op_ldptr_w: case op_stptr_w: case op_ldptr_d: case op_stptr_d: kType = kOp8Type; break; default: kType = kUnsupported; } } if (kType == kUnsupported) { // Check for kOp10Type switch (bits(31, 22) << 22) { case op_bstrins_d: case op_bstrpick_d: case op_slti: case op_sltui: case op_addi_w: case op_addi_d: case op_lu52i_d: case op_andi: case op_ori: case op_xori: case op_ld_b: case op_ld_h: case op_ld_w: case op_ld_d: case op_st_b: case op_st_h: case op_st_w: case op_st_d: case op_ld_bu: case op_ld_hu: case op_ld_wu: case op_preld: case op_fld_s: case op_fst_s: case op_fld_d: case op_fst_d: case op_bstr_w: // BSTRINS_W & BSTRPICK_W kType = kOp10Type; break; default: kType = kUnsupported; } } if (kType == kUnsupported) { // Check for kOp11Type switch (bits(31, 21) << 21) { case op_bstr_w: kType = kOp11Type; break; default: kType = kUnsupported; } } if (kType == kUnsupported) { // Check for kOp12Type switch (bits(31, 20) << 20) { case op_fmadd_s: case op_fmadd_d: case op_fmsub_s: case op_fmsub_d: case op_fnmadd_s: case op_fnmadd_d: case op_fnmsub_s: case op_fnmsub_d: case op_fcmp_cond_s: case op_fcmp_cond_d: kType = kOp12Type; break; default: kType = kUnsupported; } } if (kType == kUnsupported) { // Check for kOp14Type switch (bits(31, 18) << 18) { case op_bytepick_d: case op_fsel: kType = kOp14Type; break; default: kType = kUnsupported; } } if (kType == kUnsupported) { // Check for kOp15Type switch (bits(31, 17) << 17) { case op_bytepick_w: case op_alsl_w: case op_alsl_wu: case op_alsl_d: kType = kOp15Type; break; default: kType = kUnsupported; } } if (kType == kUnsupported) { // Check for kOp16Type switch (bits(31, 16) << 16) { case op_slli_d: case op_srli_d: case op_srai_d: case op_rotri_d: kType = kOp16Type; break; default: kType = kUnsupported; } } if (kType == kUnsupported) { // Check for kOp17Type switch (bits(31, 15) << 15) { case op_slli_w: case op_srli_w: case op_srai_w: case op_rotri_w: case op_add_w: case op_add_d: case op_sub_w: case op_sub_d: case op_slt: case op_sltu: case op_maskeqz: case op_masknez: case op_nor: case op_and: case op_or: case op_xor: case op_orn: case op_andn: case op_sll_w: case op_srl_w: case op_sra_w: case op_sll_d: case op_srl_d: case op_sra_d: case op_rotr_w: case op_rotr_d: case op_mul_w: case op_mul_d: case op_mulh_d: case op_mulh_du: case op_mulh_w: case op_mulh_wu: case op_mulw_d_w: case op_mulw_d_wu: case op_div_w: case op_mod_w: case op_div_wu: case op_mod_wu: case op_div_d: case op_mod_d: case op_div_du: case op_mod_du: case op_break: case op_fadd_s: case op_fadd_d: case op_fsub_s: case op_fsub_d: case op_fmul_s: case op_fmul_d: case op_fdiv_s: case op_fdiv_d: case op_fmax_s: case op_fmax_d: case op_fmin_s: case op_fmin_d: case op_fmaxa_s: case op_fmaxa_d: case op_fmina_s: case op_fmina_d: case op_fcopysign_s: case op_fcopysign_d: case op_ldx_b: case op_ldx_h: case op_ldx_w: case op_ldx_d: case op_stx_b: case op_stx_h: case op_stx_w: case op_stx_d: case op_ldx_bu: case op_ldx_hu: case op_ldx_wu: case op_fldx_s: case op_fldx_d: case op_fstx_s: case op_fstx_d: case op_amswap_w: case op_amswap_d: case op_amadd_w: case op_amadd_d: case op_amand_w: case op_amand_d: case op_amor_w: case op_amor_d: case op_amxor_w: case op_amxor_d: case op_ammax_w: case op_ammax_d: case op_ammin_w: case op_ammin_d: case op_ammax_wu: case op_ammax_du: case op_ammin_wu: case op_ammin_du: case op_amswap_db_w: case op_amswap_db_d: case op_amadd_db_w: case op_amadd_db_d: case op_amand_db_w: case op_amand_db_d: case op_amor_db_w: case op_amor_db_d: case op_amxor_db_w: case op_amxor_db_d: case op_ammax_db_w: case op_ammax_db_d: case op_ammin_db_w: case op_ammin_db_d: case op_ammax_db_wu: case op_ammax_db_du: case op_ammin_db_wu: case op_ammin_db_du: case op_dbar: case op_ibar: kType = kOp17Type; break; default: kType = kUnsupported; } } if (kType == kUnsupported) { // Check for kOp22Type switch (bits(31, 10) << 10) { case op_clo_w: case op_clz_w: case op_cto_w: case op_ctz_w: case op_clo_d: case op_clz_d: case op_cto_d: case op_ctz_d: case op_revb_2h: case op_revb_4h: case op_revb_2w: case op_revb_d: case op_revh_2w: case op_revh_d: case op_bitrev_4b: case op_bitrev_8b: case op_bitrev_w: case op_bitrev_d: case op_ext_w_h: case op_ext_w_b: case op_fabs_s: case op_fabs_d: case op_fneg_s: case op_fneg_d: case op_fsqrt_s: case op_fsqrt_d: case op_fmov_s: case op_fmov_d: case op_movgr2fr_w: case op_movgr2fr_d: case op_movgr2frh_w: case op_movfr2gr_s: case op_movfr2gr_d: case op_movfrh2gr_s: case op_movfcsr2gr: case op_movfr2cf: case op_movgr2cf: case op_fcvt_s_d: case op_fcvt_d_s: case op_ftintrm_w_s: case op_ftintrm_w_d: case op_ftintrm_l_s: case op_ftintrm_l_d: case op_ftintrp_w_s: case op_ftintrp_w_d: case op_ftintrp_l_s: case op_ftintrp_l_d: case op_ftintrz_w_s: case op_ftintrz_w_d: case op_ftintrz_l_s: case op_ftintrz_l_d: case op_ftintrne_w_s: case op_ftintrne_w_d: case op_ftintrne_l_s: case op_ftintrne_l_d: case op_ftint_w_s: case op_ftint_w_d: case op_ftint_l_s: case op_ftint_l_d: case op_ffint_s_w: case op_ffint_s_l: case op_ffint_d_w: case op_ffint_d_l: case op_frint_s: case op_frint_d: kType = kOp22Type; break; default: kType = kUnsupported; } } if (kType == kUnsupported) { // Check for kOp24Type switch (bits(31, 8) << 8) { case op_movcf2fr: case op_movcf2gr: kType = kOp24Type; break; default: kType = kUnsupported; } } return kType; } // C/C++ argument slots size. const int kCArgSlotCount = 0; const int kCArgsSlotsSize = kCArgSlotCount * sizeof(uintptr_t); class CachePage { public: static const int LINE_VALID = 0; static const int LINE_INVALID = 1; static const int kPageShift = 12; static const int kPageSize = 1 << kPageShift; static const int kPageMask = kPageSize - 1; static const int kLineShift = 2; // The cache line is only 4 bytes right now. static const int kLineLength = 1 << kLineShift; static const int kLineMask = kLineLength - 1; CachePage() { memset(&validity_map_, LINE_INVALID, sizeof(validity_map_)); } char* validityByte(int offset) { return &validity_map_[offset >> kLineShift]; } char* cachedData(int offset) { return &data_[offset]; } private: char data_[kPageSize]; // The cached data. static const int kValidityMapSize = kPageSize >> kLineShift; char validity_map_[kValidityMapSize]; // One byte per line. }; // Protects the icache() and redirection() properties of the // Simulator. class AutoLockSimulatorCache : public LockGuard { using Base = LockGuard; public: explicit AutoLockSimulatorCache() : Base(SimulatorProcess::singleton_->cacheLock_) {} }; mozilla::Atomic SimulatorProcess::ICacheCheckingDisableCount( 1); // Checking is disabled by default. SimulatorProcess* SimulatorProcess::singleton_ = nullptr; int64_t Simulator::StopSimAt = -1; Simulator* Simulator::Create() { auto sim = MakeUnique(); if (!sim) { return nullptr; } if (!sim->init()) { return nullptr; } int64_t stopAt; char* stopAtStr = getenv("LOONG64_SIM_STOP_AT"); if (stopAtStr && sscanf(stopAtStr, "%" PRIi64, &stopAt) == 1) { fprintf(stderr, "\nStopping simulation at icount %" PRIi64 "\n", stopAt); Simulator::StopSimAt = stopAt; } return sim.release(); } void Simulator::Destroy(Simulator* sim) { js_delete(sim); } // The loong64Debugger class is used by the simulator while debugging simulated // code. class loong64Debugger { public: explicit loong64Debugger(Simulator* sim) : sim_(sim) {} void stop(SimInstruction* instr); void debug(); // Print all registers with a nice formatting. void printAllRegs(); void printAllRegsIncludingFPU(); private: // We set the breakpoint code to 0x7fff to easily recognize it. static const Instr kBreakpointInstr = op_break | (0x7fff & CODEMask); static const Instr kNopInstr = 0x0; Simulator* sim_; int64_t getRegisterValue(int regnum); int64_t getFPURegisterValueLong(int regnum); float getFPURegisterValueFloat(int regnum); double getFPURegisterValueDouble(int regnum); bool getValue(const char* desc, int64_t* value); // Set or delete a breakpoint. Returns true if successful. bool setBreakpoint(SimInstruction* breakpc); bool deleteBreakpoint(SimInstruction* breakpc); // Undo and redo all breakpoints. This is needed to bracket disassembly and // execution to skip past breakpoints when run from the debugger. void undoBreakpoints(); void redoBreakpoints(); }; static void UNIMPLEMENTED() { printf("UNIMPLEMENTED instruction.\n"); MOZ_CRASH(); } static void UNREACHABLE() { printf("UNREACHABLE instruction.\n"); MOZ_CRASH(); } static void UNSUPPORTED() { printf("Unsupported instruction.\n"); MOZ_CRASH(); } void loong64Debugger::stop(SimInstruction* instr) { // Get the stop code. uint32_t code = instr->bits(25, 6); // Retrieve the encoded address, which comes just after this stop. char* msg = *reinterpret_cast(sim_->get_pc() + SimInstruction::kInstrSize); // Update this stop description. if (!sim_->watchedStops_[code].desc_) { sim_->watchedStops_[code].desc_ = msg; } // Print the stop message and code if it is not the default code. if (code != kMaxStopCode) { printf("Simulator hit stop %u: %s\n", code, msg); } else { printf("Simulator hit %s\n", msg); } sim_->set_pc(sim_->get_pc() + 2 * SimInstruction::kInstrSize); debug(); } int64_t loong64Debugger::getRegisterValue(int regnum) { if (regnum == kPCRegister) { return sim_->get_pc(); } return sim_->getRegister(regnum); } int64_t loong64Debugger::getFPURegisterValueLong(int regnum) { return sim_->getFpuRegister(regnum); } float loong64Debugger::getFPURegisterValueFloat(int regnum) { return sim_->getFpuRegisterFloat(regnum); } double loong64Debugger::getFPURegisterValueDouble(int regnum) { return sim_->getFpuRegisterDouble(regnum); } bool loong64Debugger::getValue(const char* desc, int64_t* value) { Register reg = Register::FromName(desc); if (reg != InvalidReg) { *value = getRegisterValue(reg.code()); return true; } if (strncmp(desc, "0x", 2) == 0) { return sscanf(desc + 2, "%lx", reinterpret_cast(value)) == 1; } return sscanf(desc, "%lu", reinterpret_cast(value)) == 1; } bool loong64Debugger::setBreakpoint(SimInstruction* breakpc) { // Check if a breakpoint can be set. If not return without any side-effects. if (sim_->break_pc_ != nullptr) { return false; } // Set the breakpoint. sim_->break_pc_ = breakpc; sim_->break_instr_ = breakpc->instructionBits(); // Not setting the breakpoint instruction in the code itself. It will be set // when the debugger shell continues. return true; } bool loong64Debugger::deleteBreakpoint(SimInstruction* breakpc) { if (sim_->break_pc_ != nullptr) { sim_->break_pc_->setInstructionBits(sim_->break_instr_); } sim_->break_pc_ = nullptr; sim_->break_instr_ = 0; return true; } void loong64Debugger::undoBreakpoints() { if (sim_->break_pc_) { sim_->break_pc_->setInstructionBits(sim_->break_instr_); } } void loong64Debugger::redoBreakpoints() { if (sim_->break_pc_) { sim_->break_pc_->setInstructionBits(kBreakpointInstr); } } void loong64Debugger::printAllRegs() { int64_t value; for (uint32_t i = 0; i < Registers::Total; i++) { value = getRegisterValue(i); printf("%3s: 0x%016" PRIx64 " %20" PRIi64 " ", Registers::GetName(i), value, value); if (i % 2) { printf("\n"); } } printf("\n"); value = getRegisterValue(Simulator::pc); printf(" pc: 0x%016" PRIx64 "\n", value); } void loong64Debugger::printAllRegsIncludingFPU() { printAllRegs(); printf("\n\n"); // f0, f1, f2, ... f31. for (uint32_t i = 0; i < FloatRegisters::TotalPhys; i++) { printf("%3s: 0x%016" PRIi64 "\tflt: %-8.4g\tdbl: %-16.4g\n", FloatRegisters::GetName(i), getFPURegisterValueLong(i), getFPURegisterValueFloat(i), getFPURegisterValueDouble(i)); } } static char* ReadLine(const char* prompt) { UniqueChars result; char lineBuf[256]; int offset = 0; bool keepGoing = true; fprintf(stdout, "%s", prompt); fflush(stdout); while (keepGoing) { if (fgets(lineBuf, sizeof(lineBuf), stdin) == nullptr) { // fgets got an error. Just give up. return nullptr; } int len = strlen(lineBuf); if (len > 0 && lineBuf[len - 1] == '\n') { // Since we read a new line we are done reading the line. This // will exit the loop after copying this buffer into the result. keepGoing = false; } if (!result) { // Allocate the initial result and make room for the terminating '\0' result.reset(js_pod_malloc(len + 1)); if (!result) { return nullptr; } } else { // Allocate a new result with enough room for the new addition. int new_len = offset + len + 1; char* new_result = js_pod_malloc(new_len); if (!new_result) { return nullptr; } // Copy the existing input into the new array and set the new // array as the result. memcpy(new_result, result.get(), offset * sizeof(char)); result.reset(new_result); } // Copy the newly read line into the result. memcpy(result.get() + offset, lineBuf, len * sizeof(char)); offset += len; } MOZ_ASSERT(result); result[offset] = '\0'; return result.release(); } static void DisassembleInstruction(uint64_t pc) { printf("Not supported on loongarch64 yet\n"); } void loong64Debugger::debug() { intptr_t lastPC = -1; bool done = false; #define COMMAND_SIZE 63 #define ARG_SIZE 255 #define STR(a) #a #define XSTR(a) STR(a) char cmd[COMMAND_SIZE + 1]; char arg1[ARG_SIZE + 1]; char arg2[ARG_SIZE + 1]; char* argv[3] = {cmd, arg1, arg2}; // Make sure to have a proper terminating character if reaching the limit. cmd[COMMAND_SIZE] = 0; arg1[ARG_SIZE] = 0; arg2[ARG_SIZE] = 0; // Undo all set breakpoints while running in the debugger shell. This will // make them invisible to all commands. undoBreakpoints(); while (!done && (sim_->get_pc() != Simulator::end_sim_pc)) { if (lastPC != sim_->get_pc()) { DisassembleInstruction(sim_->get_pc()); printf(" 0x%016" PRIi64 " \n", sim_->get_pc()); lastPC = sim_->get_pc(); } char* line = ReadLine("sim> "); if (line == nullptr) { break; } else { char* last_input = sim_->lastDebuggerInput(); if (strcmp(line, "\n") == 0 && last_input != nullptr) { line = last_input; } else { // Ownership is transferred to sim_; sim_->setLastDebuggerInput(line); } // Use sscanf to parse the individual parts of the command line. At the // moment no command expects more than two parameters. int argc = sscanf(line, "%" XSTR(COMMAND_SIZE) "s " "%" XSTR(ARG_SIZE) "s " "%" XSTR(ARG_SIZE) "s", cmd, arg1, arg2); if ((strcmp(cmd, "si") == 0) || (strcmp(cmd, "stepi") == 0)) { SimInstruction* instr = reinterpret_cast(sim_->get_pc()); if (!instr->isTrap()) { sim_->instructionDecode( reinterpret_cast(sim_->get_pc())); } else { // Allow si to jump over generated breakpoints. printf("/!\\ Jumping over generated breakpoint.\n"); sim_->set_pc(sim_->get_pc() + SimInstruction::kInstrSize); } sim_->icount_++; } else if ((strcmp(cmd, "c") == 0) || (strcmp(cmd, "cont") == 0)) { // Execute the one instruction we broke at with breakpoints disabled. sim_->instructionDecode( reinterpret_cast(sim_->get_pc())); sim_->icount_++; // Leave the debugger shell. done = true; } else if ((strcmp(cmd, "p") == 0) || (strcmp(cmd, "print") == 0)) { if (argc == 2) { int64_t value; if (strcmp(arg1, "all") == 0) { printAllRegs(); } else if (strcmp(arg1, "allf") == 0) { printAllRegsIncludingFPU(); } else { Register reg = Register::FromName(arg1); FloatRegisters::Code fReg = FloatRegisters::FromName(arg1); if (reg != InvalidReg) { value = getRegisterValue(reg.code()); printf("%s: 0x%016" PRIi64 " %20" PRIi64 " \n", arg1, value, value); } else if (fReg != FloatRegisters::Invalid) { printf("%3s: 0x%016" PRIi64 "\tflt: %-8.4g\tdbl: %-16.4g\n", FloatRegisters::GetName(fReg), getFPURegisterValueLong(fReg), getFPURegisterValueFloat(fReg), getFPURegisterValueDouble(fReg)); } else { printf("%s unrecognized\n", arg1); } } } else { printf("print or print single\n"); } } else if (strcmp(cmd, "stack") == 0 || strcmp(cmd, "mem") == 0) { int64_t* cur = nullptr; int64_t* end = nullptr; int next_arg = 1; if (strcmp(cmd, "stack") == 0) { cur = reinterpret_cast(sim_->getRegister(Simulator::sp)); } else { // Command "mem". int64_t value; if (!getValue(arg1, &value)) { printf("%s unrecognized\n", arg1); continue; } cur = reinterpret_cast(value); next_arg++; } int64_t words; if (argc == next_arg) { words = 10; } else { if (!getValue(argv[next_arg], &words)) { words = 10; } } end = cur + words; while (cur < end) { printf(" %p: 0x%016" PRIx64 " %20" PRIi64, cur, *cur, *cur); printf("\n"); cur++; } } else if ((strcmp(cmd, "disasm") == 0) || (strcmp(cmd, "dpc") == 0) || (strcmp(cmd, "di") == 0)) { uint8_t* cur = nullptr; uint8_t* end = nullptr; if (argc == 1) { cur = reinterpret_cast(sim_->get_pc()); end = cur + (10 * SimInstruction::kInstrSize); } else if (argc == 2) { Register reg = Register::FromName(arg1); if (reg != InvalidReg || strncmp(arg1, "0x", 2) == 0) { // The argument is an address or a register name. int64_t value; if (getValue(arg1, &value)) { cur = reinterpret_cast(value); // Disassemble 10 instructions at . end = cur + (10 * SimInstruction::kInstrSize); } } else { // The argument is the number of instructions. int64_t value; if (getValue(arg1, &value)) { cur = reinterpret_cast(sim_->get_pc()); // Disassemble instructions. end = cur + (value * SimInstruction::kInstrSize); } } } else { int64_t value1; int64_t value2; if (getValue(arg1, &value1) && getValue(arg2, &value2)) { cur = reinterpret_cast(value1); end = cur + (value2 * SimInstruction::kInstrSize); } } while (cur < end) { DisassembleInstruction(uint64_t(cur)); cur += SimInstruction::kInstrSize; } } else if (strcmp(cmd, "gdb") == 0) { printf("relinquishing control to gdb\n"); asm("int $3"); printf("regaining control from gdb\n"); } else if (strcmp(cmd, "break") == 0) { if (argc == 2) { int64_t value; if (getValue(arg1, &value)) { if (!setBreakpoint(reinterpret_cast(value))) { printf("setting breakpoint failed\n"); } } else { printf("%s unrecognized\n", arg1); } } else { printf("break
\n"); } } else if (strcmp(cmd, "del") == 0) { if (!deleteBreakpoint(nullptr)) { printf("deleting breakpoint failed\n"); } } else if (strcmp(cmd, "flags") == 0) { printf("No flags on LOONG64 !\n"); } else if (strcmp(cmd, "stop") == 0) { int64_t value; intptr_t stop_pc = sim_->get_pc() - 2 * SimInstruction::kInstrSize; SimInstruction* stop_instr = reinterpret_cast(stop_pc); SimInstruction* msg_address = reinterpret_cast( stop_pc + SimInstruction::kInstrSize); if ((argc == 2) && (strcmp(arg1, "unstop") == 0)) { // Remove the current stop. if (sim_->isStopInstruction(stop_instr)) { stop_instr->setInstructionBits(kNopInstr); msg_address->setInstructionBits(kNopInstr); } else { printf("Not at debugger stop.\n"); } } else if (argc == 3) { // Print information about all/the specified breakpoint(s). if (strcmp(arg1, "info") == 0) { if (strcmp(arg2, "all") == 0) { printf("Stop information:\n"); for (uint32_t i = kMaxWatchpointCode + 1; i <= kMaxStopCode; i++) { sim_->printStopInfo(i); } } else if (getValue(arg2, &value)) { sim_->printStopInfo(value); } else { printf("Unrecognized argument.\n"); } } else if (strcmp(arg1, "enable") == 0) { // Enable all/the specified breakpoint(s). if (strcmp(arg2, "all") == 0) { for (uint32_t i = kMaxWatchpointCode + 1; i <= kMaxStopCode; i++) { sim_->enableStop(i); } } else if (getValue(arg2, &value)) { sim_->enableStop(value); } else { printf("Unrecognized argument.\n"); } } else if (strcmp(arg1, "disable") == 0) { // Disable all/the specified breakpoint(s). if (strcmp(arg2, "all") == 0) { for (uint32_t i = kMaxWatchpointCode + 1; i <= kMaxStopCode; i++) { sim_->disableStop(i); } } else if (getValue(arg2, &value)) { sim_->disableStop(value); } else { printf("Unrecognized argument.\n"); } } } else { printf("Wrong usage. Use help command for more information.\n"); } } else if ((strcmp(cmd, "h") == 0) || (strcmp(cmd, "help") == 0)) { printf("cont\n"); printf(" continue execution (alias 'c')\n"); printf("stepi\n"); printf(" step one instruction (alias 'si')\n"); printf("print \n"); printf(" print register content (alias 'p')\n"); printf(" use register name 'all' to print all registers\n"); printf("printobject \n"); printf(" print an object from a register (alias 'po')\n"); printf("stack []\n"); printf(" dump stack content, default dump 10 words)\n"); printf("mem
[]\n"); printf(" dump memory content, default dump 10 words)\n"); printf("flags\n"); printf(" print flags\n"); printf("disasm []\n"); printf("disasm [
]\n"); printf("disasm [[
] ]\n"); printf(" disassemble code, default is 10 instructions\n"); printf(" from pc (alias 'di')\n"); printf("gdb\n"); printf(" enter gdb\n"); printf("break
\n"); printf(" set a break point on the address\n"); printf("del\n"); printf(" delete the breakpoint\n"); printf("stop feature:\n"); printf(" Description:\n"); printf(" Stops are debug instructions inserted by\n"); printf(" the Assembler::stop() function.\n"); printf(" When hitting a stop, the Simulator will\n"); printf(" stop and and give control to the Debugger.\n"); printf(" All stop codes are watched:\n"); printf(" - They can be enabled / disabled: the Simulator\n"); printf(" will / won't stop when hitting them.\n"); printf(" - The Simulator keeps track of how many times they \n"); printf(" are met. (See the info command.) Going over a\n"); printf(" disabled stop still increases its counter. \n"); printf(" Commands:\n"); printf(" stop info all/ : print infos about number \n"); printf(" or all stop(s).\n"); printf(" stop enable/disable all/ : enables / disables\n"); printf(" all or number stop(s)\n"); printf(" stop unstop\n"); printf(" ignore the stop instruction at the current location\n"); printf(" from now on\n"); } else { printf("Unknown command: %s\n", cmd); } } } // Add all the breakpoints back to stop execution and enter the debugger // shell when hit. redoBreakpoints(); #undef COMMAND_SIZE #undef ARG_SIZE #undef STR #undef XSTR } static bool AllOnOnePage(uintptr_t start, int size) { intptr_t start_page = (start & ~CachePage::kPageMask); intptr_t end_page = ((start + size) & ~CachePage::kPageMask); return start_page == end_page; } void Simulator::setLastDebuggerInput(char* input) { js_free(lastDebuggerInput_); lastDebuggerInput_ = input; } static CachePage* GetCachePageLocked(SimulatorProcess::ICacheMap& i_cache, void* page) { SimulatorProcess::ICacheMap::AddPtr p = i_cache.lookupForAdd(page); if (p) { return p->value(); } AutoEnterOOMUnsafeRegion oomUnsafe; CachePage* new_page = js_new(); if (!new_page || !i_cache.add(p, page, new_page)) { oomUnsafe.crash("Simulator CachePage"); } return new_page; } // Flush from start up to and not including start + size. static void FlushOnePageLocked(SimulatorProcess::ICacheMap& i_cache, intptr_t start, int size) { MOZ_ASSERT(size <= CachePage::kPageSize); MOZ_ASSERT(AllOnOnePage(start, size - 1)); MOZ_ASSERT((start & CachePage::kLineMask) == 0); MOZ_ASSERT((size & CachePage::kLineMask) == 0); void* page = reinterpret_cast(start & (~CachePage::kPageMask)); int offset = (start & CachePage::kPageMask); CachePage* cache_page = GetCachePageLocked(i_cache, page); char* valid_bytemap = cache_page->validityByte(offset); memset(valid_bytemap, CachePage::LINE_INVALID, size >> CachePage::kLineShift); } static void FlushICacheLocked(SimulatorProcess::ICacheMap& i_cache, void* start_addr, size_t size) { intptr_t start = reinterpret_cast(start_addr); int intra_line = (start & CachePage::kLineMask); start -= intra_line; size += intra_line; size = ((size - 1) | CachePage::kLineMask) + 1; int offset = (start & CachePage::kPageMask); while (!AllOnOnePage(start, size - 1)) { int bytes_to_flush = CachePage::kPageSize - offset; FlushOnePageLocked(i_cache, start, bytes_to_flush); start += bytes_to_flush; size -= bytes_to_flush; MOZ_ASSERT((start & CachePage::kPageMask) == 0); offset = 0; } if (size != 0) { FlushOnePageLocked(i_cache, start, size); } } /* static */ void SimulatorProcess::checkICacheLocked(SimInstruction* instr) { intptr_t address = reinterpret_cast(instr); void* page = reinterpret_cast(address & (~CachePage::kPageMask)); void* line = reinterpret_cast(address & (~CachePage::kLineMask)); int offset = (address & CachePage::kPageMask); CachePage* cache_page = GetCachePageLocked(icache(), page); char* cache_valid_byte = cache_page->validityByte(offset); bool cache_hit = (*cache_valid_byte == CachePage::LINE_VALID); char* cached_line = cache_page->cachedData(offset & ~CachePage::kLineMask); if (cache_hit) { // Check that the data in memory matches the contents of the I-cache. mozilla::DebugOnly cmpret = memcmp(reinterpret_cast(instr), cache_page->cachedData(offset), SimInstruction::kInstrSize); MOZ_ASSERT(cmpret == 0); } else { // Cache miss. Load memory into the cache. memcpy(cached_line, line, CachePage::kLineLength); *cache_valid_byte = CachePage::LINE_VALID; } } HashNumber SimulatorProcess::ICacheHasher::hash(const Lookup& l) { return U32(reinterpret_cast(l)) >> 2; } bool SimulatorProcess::ICacheHasher::match(const Key& k, const Lookup& l) { MOZ_ASSERT((reinterpret_cast(k) & CachePage::kPageMask) == 0); MOZ_ASSERT((reinterpret_cast(l) & CachePage::kPageMask) == 0); return k == l; } /* static */ void SimulatorProcess::FlushICache(void* start_addr, size_t size) { if (!ICacheCheckingDisableCount) { AutoLockSimulatorCache als; js::jit::FlushICacheLocked(icache(), start_addr, size); } } Simulator::Simulator() { // Set up simulator support first. Some of this information is needed to // setup the architecture state. // Note, allocation and anything that depends on allocated memory is // deferred until init(), in order to handle OOM properly. stack_ = nullptr; stackLimit_ = 0; pc_modified_ = false; icount_ = 0; break_count_ = 0; break_pc_ = nullptr; break_instr_ = 0; single_stepping_ = false; single_step_callback_ = nullptr; single_step_callback_arg_ = nullptr; // Set up architecture state. // All registers are initialized to zero to start with. for (int i = 0; i < Register::kNumSimuRegisters; i++) { registers_[i] = 0; } for (int i = 0; i < Simulator::FPURegister::kNumFPURegisters; i++) { FPUregisters_[i] = 0; } for (int i = 0; i < kNumCFRegisters; i++) { CFregisters_[i] = 0; } FCSR_ = 0; LLBit_ = false; LLAddr_ = 0; lastLLValue_ = 0; // The ra and pc are initialized to a known bad value that will cause an // access violation if the simulator ever tries to execute it. registers_[pc] = bad_ra; registers_[ra] = bad_ra; for (int i = 0; i < kNumExceptions; i++) { exceptions[i] = 0; } lastDebuggerInput_ = nullptr; } bool Simulator::init() { // Allocate 2MB for the stack. Note that we will only use 1MB, see below. static const size_t stackSize = 2 * 1024 * 1024; stack_ = js_pod_malloc(stackSize); if (!stack_) { return false; } // Leave a safety margin of 1MB to prevent overrunning the stack when // pushing values (total stack size is 2MB). stackLimit_ = reinterpret_cast(stack_) + 1024 * 1024; // The sp is initialized to point to the bottom (high address) of the // allocated stack area. To be safe in potential stack underflows we leave // some buffer below. registers_[sp] = reinterpret_cast(stack_) + stackSize - 64; return true; } // When the generated code calls an external reference we need to catch that in // the simulator. The external reference will be a function compiled for the // host architecture. We need to call that function instead of trying to // execute it with the simulator. We do that by redirecting the external // reference to a swi (software-interrupt) instruction that is handled by // the simulator. We write the original destination of the jump just at a known // offset from the swi instruction so the simulator knows what to call. class Redirection { friend class SimulatorProcess; // sim's lock must already be held. Redirection(void* nativeFunction, ABIFunctionType type) : nativeFunction_(nativeFunction), swiInstruction_(kCallRedirInstr), type_(type), next_(nullptr) { next_ = SimulatorProcess::redirection(); if (!SimulatorProcess::ICacheCheckingDisableCount) { FlushICacheLocked(SimulatorProcess::icache(), addressOfSwiInstruction(), SimInstruction::kInstrSize); } SimulatorProcess::setRedirection(this); } public: void* addressOfSwiInstruction() { return &swiInstruction_; } void* nativeFunction() const { return nativeFunction_; } ABIFunctionType type() const { return type_; } static Redirection* Get(void* nativeFunction, ABIFunctionType type) { AutoLockSimulatorCache als; Redirection* current = SimulatorProcess::redirection(); for (; current != nullptr; current = current->next_) { if (current->nativeFunction_ == nativeFunction) { MOZ_ASSERT(current->type() == type); return current; } } // Note: we can't use js_new here because the constructor is private. AutoEnterOOMUnsafeRegion oomUnsafe; Redirection* redir = js_pod_malloc(1); if (!redir) { oomUnsafe.crash("Simulator redirection"); } new (redir) Redirection(nativeFunction, type); return redir; } static Redirection* FromSwiInstruction(SimInstruction* swiInstruction) { uint8_t* addrOfSwi = reinterpret_cast(swiInstruction); uint8_t* addrOfRedirection = addrOfSwi - offsetof(Redirection, swiInstruction_); return reinterpret_cast(addrOfRedirection); } private: void* nativeFunction_; uint32_t swiInstruction_; ABIFunctionType type_; Redirection* next_; }; Simulator::~Simulator() { js_free(stack_); } SimulatorProcess::SimulatorProcess() : cacheLock_(mutexid::SimulatorCacheLock), redirection_(nullptr) { if (getenv("LOONG64_SIM_ICACHE_CHECKS")) { ICacheCheckingDisableCount = 0; } } SimulatorProcess::~SimulatorProcess() { Redirection* r = redirection_; while (r) { Redirection* next = r->next_; js_delete(r); r = next; } } /* static */ void* Simulator::RedirectNativeFunction(void* nativeFunction, ABIFunctionType type) { Redirection* redirection = Redirection::Get(nativeFunction, type); return redirection->addressOfSwiInstruction(); } // Get the active Simulator for the current thread. Simulator* Simulator::Current() { JSContext* cx = TlsContext.get(); MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime())); return cx->simulator(); } // Sets the register in the architecture state. It will also deal with updating // Simulator internal state for special registers such as PC. void Simulator::setRegister(int reg, int64_t value) { MOZ_ASSERT((reg >= 0) && (reg < Register::kNumSimuRegisters)); if (reg == pc) { pc_modified_ = true; } // Zero register always holds 0. registers_[reg] = (reg == 0) ? 0 : value; } void Simulator::setFpuRegister(int fpureg, int64_t value) { MOZ_ASSERT((fpureg >= 0) && (fpureg < Simulator::FPURegister::kNumFPURegisters)); FPUregisters_[fpureg] = value; } void Simulator::setFpuRegisterHiWord(int fpureg, int32_t value) { // Set ONLY upper 32-bits, leaving lower bits untouched. MOZ_ASSERT((fpureg >= 0) && (fpureg < Simulator::FPURegister::kNumFPURegisters)); int32_t* phiword; phiword = (reinterpret_cast(&FPUregisters_[fpureg])) + 1; *phiword = value; } void Simulator::setFpuRegisterWord(int fpureg, int32_t value) { // Set ONLY lower 32-bits, leaving upper bits untouched. MOZ_ASSERT((fpureg >= 0) && (fpureg < Simulator::FPURegister::kNumFPURegisters)); int32_t* pword; pword = reinterpret_cast(&FPUregisters_[fpureg]); *pword = value; } void Simulator::setFpuRegisterWordInvalidResult(float original, float rounded, int fpureg) { double max_int32 = static_cast(INT32_MAX); double min_int32 = static_cast(INT32_MIN); if (std::isnan(original)) { setFpuRegisterWord(fpureg, 0); } else if (rounded > max_int32) { setFpuRegister(fpureg, kFPUInvalidResult); } else if (rounded < min_int32) { setFpuRegister(fpureg, kFPUInvalidResultNegative); } else { UNREACHABLE(); } } void Simulator::setFpuRegisterWordInvalidResult(double original, double rounded, int fpureg) { double max_int32 = static_cast(INT32_MAX); double min_int32 = static_cast(INT32_MIN); if (std::isnan(original)) { setFpuRegisterWord(fpureg, 0); } else if (rounded > max_int32) { setFpuRegisterWord(fpureg, kFPUInvalidResult); } else if (rounded < min_int32) { setFpuRegisterWord(fpureg, kFPUInvalidResultNegative); } else { UNREACHABLE(); } } void Simulator::setFpuRegisterInvalidResult(float original, float rounded, int fpureg) { double max_int32 = static_cast(INT32_MAX); double min_int32 = static_cast(INT32_MIN); if (std::isnan(original)) { setFpuRegister(fpureg, 0); } else if (rounded > max_int32) { setFpuRegister(fpureg, kFPUInvalidResult); } else if (rounded < min_int32) { setFpuRegister(fpureg, kFPUInvalidResultNegative); } else { UNREACHABLE(); } } void Simulator::setFpuRegisterInvalidResult(double original, double rounded, int fpureg) { double max_int32 = static_cast(INT32_MAX); double min_int32 = static_cast(INT32_MIN); if (std::isnan(original)) { setFpuRegister(fpureg, 0); } else if (rounded > max_int32) { setFpuRegister(fpureg, kFPUInvalidResult); } else if (rounded < min_int32) { setFpuRegister(fpureg, kFPUInvalidResultNegative); } else { UNREACHABLE(); } } void Simulator::setFpuRegisterInvalidResult64(float original, float rounded, int fpureg) { // The value of INT64_MAX (2^63-1) can't be represented as double exactly, // loading the most accurate representation into max_int64, which is 2^63. double max_int64 = static_cast(INT64_MAX); double min_int64 = static_cast(INT64_MIN); if (std::isnan(original)) { setFpuRegister(fpureg, 0); } else if (rounded >= max_int64) { setFpuRegister(fpureg, kFPU64InvalidResult); } else if (rounded < min_int64) { setFpuRegister(fpureg, kFPU64InvalidResultNegative); } else { UNREACHABLE(); } } void Simulator::setFpuRegisterInvalidResult64(double original, double rounded, int fpureg) { // The value of INT64_MAX (2^63-1) can't be represented as double exactly, // loading the most accurate representation into max_int64, which is 2^63. double max_int64 = static_cast(INT64_MAX); double min_int64 = static_cast(INT64_MIN); if (std::isnan(original)) { setFpuRegister(fpureg, 0); } else if (rounded >= max_int64) { setFpuRegister(fpureg, kFPU64InvalidResult); } else if (rounded < min_int64) { setFpuRegister(fpureg, kFPU64InvalidResultNegative); } else { UNREACHABLE(); } } void Simulator::setFpuRegisterFloat(int fpureg, float value) { MOZ_ASSERT((fpureg >= 0) && (fpureg < Simulator::FPURegister::kNumFPURegisters)); *mozilla::BitwiseCast(&FPUregisters_[fpureg]) = value; } void Simulator::setFpuRegisterDouble(int fpureg, double value) { MOZ_ASSERT((fpureg >= 0) && (fpureg < Simulator::FPURegister::kNumFPURegisters)); *mozilla::BitwiseCast(&FPUregisters_[fpureg]) = value; } void Simulator::setCFRegister(int cfreg, bool value) { MOZ_ASSERT((cfreg >= 0) && (cfreg < kNumCFRegisters)); CFregisters_[cfreg] = value; } bool Simulator::getCFRegister(int cfreg) const { MOZ_ASSERT((cfreg >= 0) && (cfreg < kNumCFRegisters)); return CFregisters_[cfreg]; } // Get the register from the architecture state. This function does handle // the special case of accessing the PC register. int64_t Simulator::getRegister(int reg) const { MOZ_ASSERT((reg >= 0) && (reg < Register::kNumSimuRegisters)); if (reg == 0) { return 0; } return registers_[reg] + ((reg == pc) ? SimInstruction::kPCReadOffset : 0); } int64_t Simulator::getFpuRegister(int fpureg) const { MOZ_ASSERT((fpureg >= 0) && (fpureg < Simulator::FPURegister::kNumFPURegisters)); return FPUregisters_[fpureg]; } int32_t Simulator::getFpuRegisterWord(int fpureg) const { MOZ_ASSERT((fpureg >= 0) && (fpureg < Simulator::FPURegister::kNumFPURegisters)); return *mozilla::BitwiseCast(&FPUregisters_[fpureg]); } int32_t Simulator::getFpuRegisterSignedWord(int fpureg) const { MOZ_ASSERT((fpureg >= 0) && (fpureg < Simulator::FPURegister::kNumFPURegisters)); return *mozilla::BitwiseCast(&FPUregisters_[fpureg]); } int32_t Simulator::getFpuRegisterHiWord(int fpureg) const { MOZ_ASSERT((fpureg >= 0) && (fpureg < Simulator::FPURegister::kNumFPURegisters)); return *((mozilla::BitwiseCast(&FPUregisters_[fpureg])) + 1); } float Simulator::getFpuRegisterFloat(int fpureg) const { MOZ_ASSERT((fpureg >= 0) && (fpureg < Simulator::FPURegister::kNumFPURegisters)); return *mozilla::BitwiseCast(&FPUregisters_[fpureg]); } double Simulator::getFpuRegisterDouble(int fpureg) const { MOZ_ASSERT((fpureg >= 0) && (fpureg < Simulator::FPURegister::kNumFPURegisters)); return *mozilla::BitwiseCast(&FPUregisters_[fpureg]); } void Simulator::setCallResultDouble(double result) { setFpuRegisterDouble(f0, result); } void Simulator::setCallResultFloat(float result) { setFpuRegisterFloat(f0, result); } void Simulator::setCallResult(int64_t res) { setRegister(a0, res); } void Simulator::setCallResult(__int128_t res) { setRegister(a0, I64(res)); setRegister(a1, I64(res >> 64)); } // Helper functions for setting and testing the FCSR register's bits. void Simulator::setFCSRBit(uint32_t cc, bool value) { if (value) { FCSR_ |= (1 << cc); } else { FCSR_ &= ~(1 << cc); } } bool Simulator::testFCSRBit(uint32_t cc) { return FCSR_ & (1 << cc); } unsigned int Simulator::getFCSRRoundingMode() { return FCSR_ & kFPURoundingModeMask; } // Sets the rounding error codes in FCSR based on the result of the rounding. // Returns true if the operation was invalid. template bool Simulator::setFCSRRoundError(double original, double rounded) { bool ret = false; setFCSRBit(kFCSRInexactCauseBit, false); setFCSRBit(kFCSRUnderflowCauseBit, false); setFCSRBit(kFCSROverflowCauseBit, false); setFCSRBit(kFCSRInvalidOpCauseBit, false); if (!std::isfinite(original) || !std::isfinite(rounded)) { setFCSRBit(kFCSRInvalidOpFlagBit, true); setFCSRBit(kFCSRInvalidOpCauseBit, true); ret = true; } if (original != rounded) { setFCSRBit(kFCSRInexactFlagBit, true); setFCSRBit(kFCSRInexactCauseBit, true); } if (rounded < DBL_MIN && rounded > -DBL_MIN && rounded != 0) { setFCSRBit(kFCSRUnderflowFlagBit, true); setFCSRBit(kFCSRUnderflowCauseBit, true); ret = true; } if ((long double)rounded > (long double)std::numeric_limits::max() || (long double)rounded < (long double)std::numeric_limits::min()) { setFCSRBit(kFCSROverflowFlagBit, true); setFCSRBit(kFCSROverflowCauseBit, true); // The reference is not really clear but it seems this is required: setFCSRBit(kFCSRInvalidOpFlagBit, true); setFCSRBit(kFCSRInvalidOpCauseBit, true); ret = true; } return ret; } // For cvt instructions only template void Simulator::roundAccordingToFCSR(T toRound, T* rounded, int32_t* rounded_int) { switch ((FCSR_ >> 8) & 3) { case kRoundToNearest: *rounded = std::floor(toRound + 0.5); *rounded_int = static_cast(*rounded); if ((*rounded_int & 1) != 0 && *rounded_int - toRound == 0.5) { // If the number is halfway between two integers, // round to the even one. *rounded_int -= 1; *rounded -= 1.; } break; case kRoundToZero: *rounded = trunc(toRound); *rounded_int = static_cast(*rounded); break; case kRoundToPlusInf: *rounded = std::ceil(toRound); *rounded_int = static_cast(*rounded); break; case kRoundToMinusInf: *rounded = std::floor(toRound); *rounded_int = static_cast(*rounded); break; } } template void Simulator::round64AccordingToFCSR(T toRound, T* rounded, int64_t* rounded_int) { switch ((FCSR_ >> 8) & 3) { case kRoundToNearest: *rounded = std::floor(toRound + 0.5); *rounded_int = static_cast(*rounded); if ((*rounded_int & 1) != 0 && *rounded_int - toRound == 0.5) { // If the number is halfway between two integers, // round to the even one. *rounded_int -= 1; *rounded -= 1.; } break; case kRoundToZero: *rounded = trunc(toRound); *rounded_int = static_cast(*rounded); break; case kRoundToPlusInf: *rounded = std::ceil(toRound); *rounded_int = static_cast(*rounded); break; case kRoundToMinusInf: *rounded = std::floor(toRound); *rounded_int = static_cast(*rounded); break; } } // Raw access to the PC register. void Simulator::set_pc(int64_t value) { pc_modified_ = true; registers_[pc] = value; } bool Simulator::has_bad_pc() const { return ((registers_[pc] == bad_ra) || (registers_[pc] == end_sim_pc)); } // Raw access to the PC register without the special adjustment when reading. int64_t Simulator::get_pc() const { return registers_[pc]; } JS::ProfilingFrameIterator::RegisterState Simulator::registerState() { wasm::RegisterState state; state.pc = (void*)get_pc(); state.fp = (void*)getRegister(fp); state.sp = (void*)getRegister(sp); state.lr = (void*)getRegister(ra); return state; } uint8_t Simulator::readBU(uint64_t addr) { if (handleWasmSegFault(addr, 1)) { return 0xff; } uint8_t* ptr = reinterpret_cast(addr); return *ptr; } int8_t Simulator::readB(uint64_t addr) { if (handleWasmSegFault(addr, 1)) { return -1; } int8_t* ptr = reinterpret_cast(addr); return *ptr; } void Simulator::writeB(uint64_t addr, uint8_t value) { if (handleWasmSegFault(addr, 1)) { return; } uint8_t* ptr = reinterpret_cast(addr); *ptr = value; } void Simulator::writeB(uint64_t addr, int8_t value) { if (handleWasmSegFault(addr, 1)) { return; } int8_t* ptr = reinterpret_cast(addr); *ptr = value; } uint16_t Simulator::readHU(uint64_t addr, SimInstruction* instr) { if (handleWasmSegFault(addr, 2)) { return 0xffff; } uint16_t* ptr = reinterpret_cast(addr); return *ptr; } int16_t Simulator::readH(uint64_t addr, SimInstruction* instr) { if (handleWasmSegFault(addr, 2)) { return -1; } int16_t* ptr = reinterpret_cast(addr); return *ptr; } void Simulator::writeH(uint64_t addr, uint16_t value, SimInstruction* instr) { if (handleWasmSegFault(addr, 2)) { return; } uint16_t* ptr = reinterpret_cast(addr); LLBit_ = false; *ptr = value; return; } void Simulator::writeH(uint64_t addr, int16_t value, SimInstruction* instr) { if (handleWasmSegFault(addr, 2)) { return; } int16_t* ptr = reinterpret_cast(addr); LLBit_ = false; *ptr = value; return; } uint32_t Simulator::readWU(uint64_t addr, SimInstruction* instr) { if (handleWasmSegFault(addr, 4)) { return -1; } uint32_t* ptr = reinterpret_cast(addr); return *ptr; } int32_t Simulator::readW(uint64_t addr, SimInstruction* instr) { if (handleWasmSegFault(addr, 4)) { return -1; } int32_t* ptr = reinterpret_cast(addr); return *ptr; } void Simulator::writeW(uint64_t addr, uint32_t value, SimInstruction* instr) { if (handleWasmSegFault(addr, 4)) { return; } uint32_t* ptr = reinterpret_cast(addr); LLBit_ = false; *ptr = value; return; } void Simulator::writeW(uint64_t addr, int32_t value, SimInstruction* instr) { if (handleWasmSegFault(addr, 4)) { return; } int32_t* ptr = reinterpret_cast(addr); LLBit_ = false; *ptr = value; return; } int64_t Simulator::readDW(uint64_t addr, SimInstruction* instr) { if (handleWasmSegFault(addr, 8)) { return -1; } intptr_t* ptr = reinterpret_cast(addr); return *ptr; } void Simulator::writeDW(uint64_t addr, int64_t value, SimInstruction* instr) { if (handleWasmSegFault(addr, 8)) { return; } int64_t* ptr = reinterpret_cast(addr); LLBit_ = false; *ptr = value; return; } double Simulator::readD(uint64_t addr, SimInstruction* instr) { if (handleWasmSegFault(addr, 8)) { return NAN; } double* ptr = reinterpret_cast(addr); return *ptr; } void Simulator::writeD(uint64_t addr, double value, SimInstruction* instr) { if (handleWasmSegFault(addr, 8)) { return; } double* ptr = reinterpret_cast(addr); LLBit_ = false; *ptr = value; return; } int Simulator::loadLinkedW(uint64_t addr, SimInstruction* instr) { if ((addr & 3) == 0) { if (handleWasmSegFault(addr, 4)) { return -1; } volatile int32_t* ptr = reinterpret_cast(addr); int32_t value = *ptr; lastLLValue_ = value; LLAddr_ = addr; // Note that any memory write or "external" interrupt should reset this // value to false. LLBit_ = true; return value; } printf("Unaligned write at 0x%016" PRIx64 ", pc=0x%016" PRIxPTR "\n", addr, reinterpret_cast(instr)); MOZ_CRASH(); return 0; } int Simulator::storeConditionalW(uint64_t addr, int value, SimInstruction* instr) { // Correct behavior in this case, as defined by architecture, is to just // return 0, but there is no point at allowing that. It is certainly an // indicator of a bug. if (addr != LLAddr_) { printf("SC to bad address: 0x%016" PRIx64 ", pc=0x%016" PRIx64 ", expected: 0x%016" PRIx64 "\n", addr, reinterpret_cast(instr), LLAddr_); MOZ_CRASH(); } if ((addr & 3) == 0) { SharedMem ptr = SharedMem::shared(reinterpret_cast(addr)); if (!LLBit_) { return 0; } LLBit_ = false; LLAddr_ = 0; int32_t expected = int32_t(lastLLValue_); int32_t old = AtomicOperations::compareExchangeSeqCst(ptr, expected, int32_t(value)); return (old == expected) ? 1 : 0; } printf("Unaligned SC at 0x%016" PRIx64 ", pc=0x%016" PRIxPTR "\n", addr, reinterpret_cast(instr)); MOZ_CRASH(); return 0; } int64_t Simulator::loadLinkedD(uint64_t addr, SimInstruction* instr) { if ((addr & kPointerAlignmentMask) == 0) { if (handleWasmSegFault(addr, 8)) { return -1; } volatile int64_t* ptr = reinterpret_cast(addr); int64_t value = *ptr; lastLLValue_ = value; LLAddr_ = addr; // Note that any memory write or "external" interrupt should reset this // value to false. LLBit_ = true; return value; } printf("Unaligned write at 0x%016" PRIx64 ", pc=0x%016" PRIxPTR "\n", addr, reinterpret_cast(instr)); MOZ_CRASH(); return 0; } int Simulator::storeConditionalD(uint64_t addr, int64_t value, SimInstruction* instr) { // Correct behavior in this case, as defined by architecture, is to just // return 0, but there is no point at allowing that. It is certainly an // indicator of a bug. if (addr != LLAddr_) { printf("SC to bad address: 0x%016" PRIx64 ", pc=0x%016" PRIx64 ", expected: 0x%016" PRIx64 "\n", addr, reinterpret_cast(instr), LLAddr_); MOZ_CRASH(); } if ((addr & kPointerAlignmentMask) == 0) { SharedMem ptr = SharedMem::shared(reinterpret_cast(addr)); if (!LLBit_) { return 0; } LLBit_ = false; LLAddr_ = 0; int64_t expected = lastLLValue_; int64_t old = AtomicOperations::compareExchangeSeqCst(ptr, expected, int64_t(value)); return (old == expected) ? 1 : 0; } printf("Unaligned SC at 0x%016" PRIx64 ", pc=0x%016" PRIxPTR "\n", addr, reinterpret_cast(instr)); MOZ_CRASH(); return 0; } uintptr_t Simulator::stackLimit() const { return stackLimit_; } uintptr_t* Simulator::addressOfStackLimit() { return &stackLimit_; } bool Simulator::overRecursed(uintptr_t newsp) const { if (newsp == 0) { newsp = getRegister(sp); } return newsp <= stackLimit(); } bool Simulator::overRecursedWithExtra(uint32_t extra) const { uintptr_t newsp = getRegister(sp) - extra; return newsp <= stackLimit(); } // Unsupported instructions use format to print an error and stop execution. void Simulator::format(SimInstruction* instr, const char* format) { printf("Simulator found unsupported instruction:\n 0x%016lx: %s\n", reinterpret_cast(instr), format); MOZ_CRASH(); } // Note: With the code below we assume that all runtime calls return a 64 bits // result. If they don't, the a1 result register contains a bogus value, which // is fine because it is caller-saved. typedef int64_t (*Prototype_General0)(); typedef int64_t (*Prototype_General1)(int64_t arg0); typedef int64_t (*Prototype_General2)(int64_t arg0, int64_t arg1); typedef int64_t (*Prototype_General3)(int64_t arg0, int64_t arg1, int64_t arg2); typedef int64_t (*Prototype_General4)(int64_t arg0, int64_t arg1, int64_t arg2, int64_t arg3); typedef int64_t (*Prototype_General5)(int64_t arg0, int64_t arg1, int64_t arg2, int64_t arg3, int64_t arg4); typedef int64_t (*Prototype_General6)(int64_t arg0, int64_t arg1, int64_t arg2, int64_t arg3, int64_t arg4, int64_t arg5); typedef int64_t (*Prototype_General7)(int64_t arg0, int64_t arg1, int64_t arg2, int64_t arg3, int64_t arg4, int64_t arg5, int64_t arg6); typedef int64_t (*Prototype_General8)(int64_t arg0, int64_t arg1, int64_t arg2, int64_t arg3, int64_t arg4, int64_t arg5, int64_t arg6, int64_t arg7); typedef int64_t (*Prototype_GeneralGeneralGeneralInt64)(int64_t arg0, int64_t arg1, int64_t arg2, int64_t arg3); typedef int64_t (*Prototype_GeneralGeneralInt64Int64)(int64_t arg0, int64_t arg1, int64_t arg2, int64_t arg3); typedef int64_t (*Prototype_Int_Float32)(float arg0); typedef int64_t (*Prototype_Int_Double)(double arg0); typedef int64_t (*Prototype_Int_IntDouble)(int64_t arg0, double arg1); typedef int64_t (*Prototype_Int_DoubleInt)(double arg0, int64_t arg1); typedef int64_t (*Prototype_Int_DoubleIntInt)(double arg0, int64_t arg1, int64_t arg2); typedef int64_t (*Prototype_Int_IntDoubleIntInt)(int64_t arg0, double arg1, int64_t arg2, int64_t arg3); typedef float (*Prototype_Float32_Float32)(float arg0); typedef float (*Prototype_Float32_Float32Float32)(float arg0, float arg1); typedef double (*Prototype_Double_None)(); typedef double (*Prototype_Double_Double)(double arg0); typedef double (*Prototype_Double_Int)(int64_t arg0); typedef double (*Prototype_Double_DoubleInt)(double arg0, int64_t arg1); typedef double (*Prototype_Double_IntDouble)(int64_t arg0, double arg1); typedef double (*Prototype_Double_DoubleDouble)(double arg0, double arg1); typedef double (*Prototype_Double_DoubleDoubleDouble)(double arg0, double arg1, double arg2); typedef double (*Prototype_Double_DoubleDoubleDoubleDouble)(double arg0, double arg1, double arg2, double arg3); typedef int32_t (*Prototype_Int32_General)(int64_t); typedef int32_t (*Prototype_Int32_GeneralInt32)(int64_t, int32_t); typedef int32_t (*Prototype_Int32_GeneralInt32Int32)(int64_t, int32_t, int32_t); typedef int32_t (*Prototype_Int32_GeneralInt32Int32Int32Int32)(int64_t, int32_t, int32_t, int32_t, int32_t); typedef int32_t (*Prototype_Int32_GeneralInt32Int32Int32Int32Int32)( int64_t, int32_t, int32_t, int32_t, int32_t, int32_t); typedef int32_t (*Prototype_Int32_GeneralInt32Int32Int32Int32General)( int64_t, int32_t, int32_t, int32_t, int32_t, int64_t); typedef int32_t (*Prototype_Int32_GeneralInt32Int32Int32General)( int64_t, int32_t, int32_t, int32_t, int64_t); typedef int32_t (*Prototype_Int32_GeneralInt32Int32Int64)(int64_t, int32_t, int32_t, int64_t); typedef int32_t (*Prototype_Int32_GeneralInt32Int32General)(int64_t, int32_t, int32_t, int64_t); typedef int32_t (*Prototype_Int32_GeneralInt32Int64Int64)(int64_t, int32_t, int64_t, int64_t); typedef int32_t (*Prototype_Int32_GeneralInt32GeneralInt32)(int64_t, int32_t, int64_t, int32_t); typedef int32_t (*Prototype_Int32_GeneralInt32GeneralInt32Int32)( int64_t, int32_t, int64_t, int32_t, int32_t); typedef int32_t (*Prototype_Int32_GeneralGeneral)(int64_t, int64_t); typedef int32_t (*Prototype_Int32_GeneralGeneralGeneral)(int64_t, int64_t, int64_t); typedef int32_t (*Prototype_Int32_GeneralGeneralInt32Int32)(int64_t, int64_t, int32_t, int32_t); typedef int32_t (*Prototype_Int32_GeneralInt64Int32Int32Int32)(int64_t, int64_t, int32_t, int32_t, int32_t); typedef int32_t (*Prototype_Int32_GeneralInt64Int32)(int64_t, int64_t, int32_t); typedef int32_t (*Prototype_Int32_GeneralInt64Int32Int64)(int64_t, int64_t, int32_t, int64_t); typedef int32_t (*Prototype_Int32_GeneralInt64Int32Int64General)( int64_t, int64_t, int32_t, int64_t, int64_t); typedef int32_t (*Prototype_Int32_GeneralInt64Int64Int64)(int64_t, int64_t, int64_t, int64_t); typedef int32_t (*Prototype_Int32_GeneralInt64Int64General)(int64_t, int64_t, int64_t, int64_t); typedef int32_t (*Prototype_Int32_GeneralInt64Int64Int64General)( int64_t, int64_t, int64_t, int64_t, int64_t); typedef int64_t (*Prototype_General_GeneralInt32)(int64_t, int32_t); typedef int64_t (*Prototype_General_GeneralInt32Int32)(int64_t, int32_t, int32_t); typedef int64_t (*Prototype_General_GeneralInt32General)(int64_t, int32_t, int64_t); typedef int64_t (*Prototype_General_GeneralInt32Int32GeneralInt32)( int64_t, int32_t, int32_t, int64_t, int32_t); typedef int32_t (*Prototype_Int32_GeneralGeneralInt32GeneralInt32Int32Int32)( int64_t, int64_t, int32_t, int64_t, int32_t, int32_t, int32_t); typedef int32_t (*Prototype_Int32_GeneralGeneralInt32General)(int64_t, int64_t, int32_t, int64_t); typedef int64_t (*Prototype_Int64_General)(int64_t); typedef int64_t (*Prototype_Int64_GeneralInt64)(int64_t, int64_t); inline int32_t Simulator::rj_reg(SimInstruction* instr) const { return instr->rjValue(); } inline int64_t Simulator::rj(SimInstruction* instr) const { return getRegister(rj_reg(instr)); } inline uint64_t Simulator::rj_u(SimInstruction* instr) const { return static_cast(getRegister(rj_reg(instr))); } inline int32_t Simulator::rk_reg(SimInstruction* instr) const { return instr->rkValue(); } inline int64_t Simulator::rk(SimInstruction* instr) const { return getRegister(rk_reg(instr)); } inline uint64_t Simulator::rk_u(SimInstruction* instr) const { return static_cast(getRegister(rk_reg(instr))); } inline int32_t Simulator::rd_reg(SimInstruction* instr) const { return instr->rdValue(); } inline int64_t Simulator::rd(SimInstruction* instr) const { return getRegister(rd_reg(instr)); } inline uint64_t Simulator::rd_u(SimInstruction* instr) const { return static_cast(getRegister(rd_reg(instr))); } inline int32_t Simulator::fa_reg(SimInstruction* instr) const { return instr->faValue(); } inline float Simulator::fa_float(SimInstruction* instr) const { return getFpuRegisterFloat(fa_reg(instr)); } inline double Simulator::fa_double(SimInstruction* instr) const { return getFpuRegisterDouble(fa_reg(instr)); } inline int32_t Simulator::fj_reg(SimInstruction* instr) const { return instr->fjValue(); } inline float Simulator::fj_float(SimInstruction* instr) const { return getFpuRegisterFloat(fj_reg(instr)); } inline double Simulator::fj_double(SimInstruction* instr) const { return getFpuRegisterDouble(fj_reg(instr)); } inline int32_t Simulator::fk_reg(SimInstruction* instr) const { return instr->fkValue(); } inline float Simulator::fk_float(SimInstruction* instr) const { return getFpuRegisterFloat(fk_reg(instr)); } inline double Simulator::fk_double(SimInstruction* instr) const { return getFpuRegisterDouble(fk_reg(instr)); } inline int32_t Simulator::fd_reg(SimInstruction* instr) const { return instr->fdValue(); } inline float Simulator::fd_float(SimInstruction* instr) const { return getFpuRegisterFloat(fd_reg(instr)); } inline double Simulator::fd_double(SimInstruction* instr) const { return getFpuRegisterDouble(fd_reg(instr)); } inline int32_t Simulator::cj_reg(SimInstruction* instr) const { return instr->cjValue(); } inline bool Simulator::cj(SimInstruction* instr) const { return getCFRegister(cj_reg(instr)); } inline int32_t Simulator::cd_reg(SimInstruction* instr) const { return instr->cdValue(); } inline bool Simulator::cd(SimInstruction* instr) const { return getCFRegister(cd_reg(instr)); } inline int32_t Simulator::ca_reg(SimInstruction* instr) const { return instr->caValue(); } inline bool Simulator::ca(SimInstruction* instr) const { return getCFRegister(ca_reg(instr)); } inline uint32_t Simulator::sa2(SimInstruction* instr) const { return instr->sa2Value(); } inline uint32_t Simulator::sa3(SimInstruction* instr) const { return instr->sa3Value(); } inline uint32_t Simulator::ui5(SimInstruction* instr) const { return instr->imm5Value(); } inline uint32_t Simulator::ui6(SimInstruction* instr) const { return instr->imm6Value(); } inline uint32_t Simulator::lsbw(SimInstruction* instr) const { return instr->lsbwValue(); } inline uint32_t Simulator::msbw(SimInstruction* instr) const { return instr->msbwValue(); } inline uint32_t Simulator::lsbd(SimInstruction* instr) const { return instr->lsbdValue(); } inline uint32_t Simulator::msbd(SimInstruction* instr) const { return instr->msbdValue(); } inline uint32_t Simulator::cond(SimInstruction* instr) const { return instr->condValue(); } inline int32_t Simulator::si12(SimInstruction* instr) const { return (instr->imm12Value() << 20) >> 20; } inline uint32_t Simulator::ui12(SimInstruction* instr) const { return instr->imm12Value(); } inline int32_t Simulator::si14(SimInstruction* instr) const { return (instr->imm14Value() << 18) >> 18; } inline int32_t Simulator::si16(SimInstruction* instr) const { return (instr->imm16Value() << 16) >> 16; } inline int32_t Simulator::si20(SimInstruction* instr) const { return (instr->imm20Value() << 12) >> 12; } // Software interrupt instructions are used by the simulator to call into C++. void Simulator::softwareInterrupt(SimInstruction* instr) { // the break_ instruction could get us here. mozilla::DebugOnly opcode_hi15 = instr->bits(31, 17); MOZ_ASSERT(opcode_hi15 == 0x15); uint32_t code = instr->bits(14, 0); if (instr->instructionBits() == kCallRedirInstr) { Redirection* redirection = Redirection::FromSwiInstruction(instr); uintptr_t nativeFn = reinterpret_cast(redirection->nativeFunction()); int64_t arg0 = getRegister(a0); int64_t arg1 = getRegister(a1); int64_t arg2 = getRegister(a2); int64_t arg3 = getRegister(a3); int64_t arg4 = getRegister(a4); int64_t arg5 = getRegister(a5); // This is dodgy but it works because the C entry stubs are never moved. // See comment in codegen-arm.cc and bug 1242173. int64_t saved_ra = getRegister(ra); intptr_t external = reinterpret_cast(redirection->nativeFunction()); bool stack_aligned = (getRegister(sp) & (ABIStackAlignment - 1)) == 0; if (!stack_aligned) { fprintf(stderr, "Runtime call with unaligned stack!\n"); MOZ_CRASH(); } if (single_stepping_) { single_step_callback_(single_step_callback_arg_, this, nullptr); } switch (redirection->type()) { case Args_General0: { Prototype_General0 target = reinterpret_cast(external); int64_t result = target(); setCallResult(result); break; } case Args_General1: { Prototype_General1 target = reinterpret_cast(external); int64_t result = target(arg0); setCallResult(result); break; } case Args_General2: { Prototype_General2 target = reinterpret_cast(external); int64_t result = target(arg0, arg1); setCallResult(result); break; } case Args_General3: { Prototype_General3 target = reinterpret_cast(external); int64_t result = target(arg0, arg1, arg2); if (external == intptr_t(&js::wasm::Instance::wake_m32)) { result = int32_t(result); } setCallResult(result); break; } case Args_General4: { Prototype_General4 target = reinterpret_cast(external); int64_t result = target(arg0, arg1, arg2, arg3); setCallResult(result); break; } case Args_General5: { Prototype_General5 target = reinterpret_cast(external); int64_t result = target(arg0, arg1, arg2, arg3, arg4); setCallResult(result); break; } case Args_General6: { Prototype_General6 target = reinterpret_cast(external); int64_t result = target(arg0, arg1, arg2, arg3, arg4, arg5); setCallResult(result); break; } case Args_General7: { Prototype_General7 target = reinterpret_cast(external); int64_t arg6 = getRegister(a6); int64_t result = target(arg0, arg1, arg2, arg3, arg4, arg5, arg6); setCallResult(result); break; } case Args_General8: { Prototype_General8 target = reinterpret_cast(external); int64_t arg6 = getRegister(a6); int64_t arg7 = getRegister(a7); int64_t result = target(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7); setCallResult(result); break; } case Args_Double_None: { Prototype_Double_None target = reinterpret_cast(external); double dresult = target(); setCallResultDouble(dresult); break; } case Args_Int_Float32: { float fval0; fval0 = getFpuRegisterFloat(0); Prototype_Int_Float32 target = reinterpret_cast(external); int64_t result = target(fval0); setRegister(a0, result); break; } case Args_Int_Double: { double dval0 = getFpuRegisterDouble(0); Prototype_Int_Double target = reinterpret_cast(external); int64_t result = target(dval0); if (external == intptr_t((int32_t(*)(double))JS::ToInt32)) { result = int32_t(result); } setRegister(a0, result); break; } case Args_Int_GeneralGeneralGeneralInt64: { Prototype_GeneralGeneralGeneralInt64 target = reinterpret_cast(external); int64_t result = target(arg0, arg1, arg2, arg3); if (external == intptr_t(&js::wasm::Instance::wait_i32_m32)) { result = int32_t(result); } setRegister(a0, result); break; } case Args_Int_GeneralGeneralInt64Int64: { Prototype_GeneralGeneralInt64Int64 target = reinterpret_cast(external); int64_t result = target(arg0, arg1, arg2, arg3); if (external == intptr_t(&js::wasm::Instance::wait_i64_m32)) { result = int32_t(result); } setRegister(a0, result); break; } case Args_Int_DoubleInt: { double dval = getFpuRegisterDouble(0); Prototype_Int_DoubleInt target = reinterpret_cast(external); int64_t result = target(dval, arg0); setRegister(a0, result); break; } case Args_Int_DoubleIntInt: { double dval = getFpuRegisterDouble(0); Prototype_Int_DoubleIntInt target = reinterpret_cast(external); int64_t result = target(dval, arg0, arg1); setRegister(a0, result); break; } case Args_Int_IntDoubleIntInt: { double dval = getFpuRegisterDouble(0); Prototype_Int_IntDoubleIntInt target = reinterpret_cast(external); int64_t result = target(arg0, dval, arg1, arg2); setRegister(a0, result); break; } case Args_Double_Double: { double dval0 = getFpuRegisterDouble(0); Prototype_Double_Double target = reinterpret_cast(external); double dresult = target(dval0); setCallResultDouble(dresult); break; } case Args_Float32_Float32: { float fval0; fval0 = getFpuRegisterFloat(0); Prototype_Float32_Float32 target = reinterpret_cast(external); float fresult = target(fval0); setCallResultFloat(fresult); break; } case Args_Float32_Float32Float32: { float fval0; float fval1; fval0 = getFpuRegisterFloat(0); fval1 = getFpuRegisterFloat(1); Prototype_Float32_Float32Float32 target = reinterpret_cast(external); float fresult = target(fval0, fval1); setCallResultFloat(fresult); break; } case Args_Double_Int: { Prototype_Double_Int target = reinterpret_cast(external); double dresult = target(arg0); setCallResultDouble(dresult); break; } case Args_Double_DoubleInt: { double dval0 = getFpuRegisterDouble(0); Prototype_Double_DoubleInt target = reinterpret_cast(external); double dresult = target(dval0, arg0); setCallResultDouble(dresult); break; } case Args_Double_DoubleDouble: { double dval0 = getFpuRegisterDouble(0); double dval1 = getFpuRegisterDouble(1); Prototype_Double_DoubleDouble target = reinterpret_cast(external); double dresult = target(dval0, dval1); setCallResultDouble(dresult); break; } case Args_Double_IntDouble: { double dval0 = getFpuRegisterDouble(0); Prototype_Double_IntDouble target = reinterpret_cast(external); double dresult = target(arg0, dval0); setCallResultDouble(dresult); break; } case Args_Int_IntDouble: { double dval0 = getFpuRegisterDouble(0); Prototype_Int_IntDouble target = reinterpret_cast(external); int64_t result = target(arg0, dval0); setRegister(a0, result); break; } case Args_Double_DoubleDoubleDouble: { double dval0 = getFpuRegisterDouble(0); double dval1 = getFpuRegisterDouble(1); double dval2 = getFpuRegisterDouble(2); Prototype_Double_DoubleDoubleDouble target = reinterpret_cast(external); double dresult = target(dval0, dval1, dval2); setCallResultDouble(dresult); break; } case Args_Double_DoubleDoubleDoubleDouble: { double dval0 = getFpuRegisterDouble(0); double dval1 = getFpuRegisterDouble(1); double dval2 = getFpuRegisterDouble(2); double dval3 = getFpuRegisterDouble(3); Prototype_Double_DoubleDoubleDoubleDouble target = reinterpret_cast( external); double dresult = target(dval0, dval1, dval2, dval3); setCallResultDouble(dresult); break; } case Args_Int32_General: { int32_t ret = reinterpret_cast(nativeFn)(arg0); setRegister(a0, I64(ret)); break; } case Args_Int32_GeneralInt32: { int32_t ret = reinterpret_cast(nativeFn)( arg0, I32(arg1)); setRegister(a0, I64(ret)); break; } case Args_Int32_GeneralInt32Int32: { int32_t ret = reinterpret_cast( nativeFn)(arg0, I32(arg1), I32(arg2)); setRegister(a0, I64(ret)); break; } case Args_Int32_GeneralInt32Int32Int32Int32: { int32_t ret = reinterpret_cast( nativeFn)(arg0, I32(arg1), I32(arg2), I32(arg3), I32(arg4)); setRegister(a0, I64(ret)); break; } case Args_Int32_GeneralInt32Int32Int32Int32Int32: { int32_t ret = reinterpret_cast( nativeFn)(arg0, I32(arg1), I32(arg2), I32(arg3), I32(arg4), I32(arg5)); setRegister(a0, I64(ret)); break; } case Args_Int32_GeneralInt32Int32Int32Int32General: { int32_t ret = reinterpret_cast< Prototype_Int32_GeneralInt32Int32Int32Int32General>(nativeFn)( arg0, I32(arg1), I32(arg2), I32(arg3), I32(arg4), arg5); setRegister(a0, I64(ret)); break; } case Args_Int32_GeneralInt32Int32Int32General: { int32_t ret = reinterpret_cast( nativeFn)(arg0, I32(arg1), I32(arg2), I32(arg3), arg4); setRegister(a0, I64(ret)); break; } case Args_Int32_GeneralInt32Int32Int64: { int32_t ret = reinterpret_cast( nativeFn)(arg0, I32(arg1), I32(arg2), arg3); setRegister(a0, I64(ret)); break; } case Args_Int32_GeneralInt32Int32General: { int32_t ret = reinterpret_cast( nativeFn)(arg0, I32(arg1), I32(arg2), arg3); setRegister(a0, I64(ret)); break; } case Args_Int32_GeneralInt32Int64Int64: { int32_t ret = reinterpret_cast( nativeFn)(arg0, I32(arg1), arg2, arg3); setRegister(a0, I64(ret)); break; } case Args_Int32_GeneralInt32GeneralInt32: { int32_t ret = reinterpret_cast( nativeFn)(arg0, I32(arg1), arg2, I32(arg3)); setRegister(a0, I64(ret)); break; } case Args_Int32_GeneralInt32GeneralInt32Int32: { int32_t ret = reinterpret_cast( nativeFn)(arg0, I32(arg1), arg2, I32(arg3), I32(arg4)); setRegister(a0, I64(ret)); break; } case Args_Int32_GeneralGeneral: { int32_t ret = reinterpret_cast( nativeFn)(arg0, arg1); setRegister(a0, I64(ret)); break; } case Args_Int32_GeneralGeneralGeneral: { int32_t ret = reinterpret_cast( nativeFn)(arg0, arg1, arg2); setRegister(a0, I64(ret)); break; } case Args_Int32_GeneralGeneralInt32Int32: { int32_t ret = reinterpret_cast( nativeFn)(arg0, arg1, I32(arg2), I32(arg3)); setRegister(a0, I64(ret)); break; } case js::jit::Args_Int32_GeneralInt64Int32Int32Int32: { int32_t ret = reinterpret_cast( nativeFn)(arg0, arg1, I32(arg2), I32(arg3), I32(arg4)); setRegister(a0, I64(ret)); break; } case js::jit::Args_Int32_GeneralInt64Int32: { int32_t ret = reinterpret_cast( nativeFn)(arg0, arg1, I32(arg2)); setRegister(a0, I64(ret)); break; } case js::jit::Args_Int32_GeneralInt64Int32Int64: { int32_t ret = reinterpret_cast( nativeFn)(arg0, arg1, I32(arg2), arg3); setRegister(a0, I64(ret)); break; } case js::jit::Args_Int32_GeneralInt64Int32Int64General: { int32_t ret = reinterpret_cast( nativeFn)(arg0, arg1, I32(arg2), arg3, arg4); setRegister(a0, I64(ret)); break; } case js::jit::Args_Int32_GeneralInt64Int64Int64: { int32_t ret = reinterpret_cast( nativeFn)(arg0, arg1, arg2, arg3); setRegister(a0, I64(ret)); break; } case js::jit::Args_Int32_GeneralInt64Int64General: { int32_t ret = reinterpret_cast( nativeFn)(arg0, arg1, arg2, arg3); setRegister(a0, I64(ret)); break; } case js::jit::Args_Int32_GeneralInt64Int64Int64General: { int32_t ret = reinterpret_cast( nativeFn)(arg0, arg1, arg2, arg3, arg4); setRegister(a0, I64(ret)); break; } case Args_General_GeneralInt32: { int64_t ret = reinterpret_cast( nativeFn)(arg0, I32(arg1)); setRegister(a0, ret); break; } case Args_General_GeneralInt32Int32: { int64_t ret = reinterpret_cast( nativeFn)(arg0, I32(arg1), I32(arg2)); setRegister(a0, ret); break; } case Args_General_GeneralInt32General: { int64_t ret = reinterpret_cast( nativeFn)(arg0, I32(arg1), arg2); setRegister(a0, ret); break; } case js::jit::Args_General_GeneralInt32Int32GeneralInt32: { int64_t ret = reinterpret_cast( nativeFn)(arg0, I32(arg1), I32(arg2), arg3, I32(arg4)); setRegister(a0, ret); break; } case js::jit::Args_Int32_GeneralGeneralInt32GeneralInt32Int32Int32: { int64_t arg6 = getRegister(a6); int32_t ret = reinterpret_cast< Prototype_Int32_GeneralGeneralInt32GeneralInt32Int32Int32>( nativeFn)(arg0, arg1, I32(arg2), arg3, I32(arg4), I32(arg5), I32(arg6)); setRegister(a0, I64(ret)); break; } case js::jit::Args_Int32_GeneralGeneralInt32General: { int32_t ret = reinterpret_cast( nativeFn)(arg0, arg1, I32(arg2), arg3); setRegister(a0, I64(ret)); break; } case js::jit::Args_Int64_General: { int64_t ret = reinterpret_cast(nativeFn)(arg0); setRegister(a0, ret); break; } case js::jit::Args_Int64_GeneralInt64: { int64_t ret = reinterpret_cast(nativeFn)( arg0, arg1); setRegister(a0, ret); break; } default: MOZ_CRASH("Unknown function type."); } if (single_stepping_) { single_step_callback_(single_step_callback_arg_, this, nullptr); } setRegister(ra, saved_ra); set_pc(getRegister(ra)); } else if ((instr->bits(31, 15) << 15 == op_break) && code == kWasmTrapCode) { uint8_t* newPC; if (wasm::HandleIllegalInstruction(registerState(), &newPC)) { set_pc(int64_t(newPC)); return; } } else if ((instr->bits(31, 15) << 15 == op_break) && code <= kMaxStopCode && code != 6) { if (isWatchpoint(code)) { // printWatchpoint(code); } else { increaseStopCounter(code); handleStop(code, instr); } } else { // All remaining break_ codes, and all traps are handled here. loong64Debugger dbg(this); dbg.debug(); } } // Stop helper functions. bool Simulator::isWatchpoint(uint32_t code) { return (code <= kMaxWatchpointCode); } void Simulator::printWatchpoint(uint32_t code) { loong64Debugger dbg(this); ++break_count_; printf("\n---- break %d marker: %20" PRIi64 " (instr count: %20" PRIi64 ") ----\n", code, break_count_, icount_); dbg.printAllRegs(); // Print registers and continue running. } void Simulator::handleStop(uint32_t code, SimInstruction* instr) { // Stop if it is enabled, otherwise go on jumping over the stop // and the message address. if (isEnabledStop(code)) { loong64Debugger dbg(this); dbg.stop(instr); } else { set_pc(get_pc() + 1 * SimInstruction::kInstrSize); } } bool Simulator::isStopInstruction(SimInstruction* instr) { int32_t opcode_hi15 = instr->bits(31, 17); uint32_t code = static_cast(instr->bits(14, 0)); return (opcode_hi15 == 0x15) && code > kMaxWatchpointCode && code <= kMaxStopCode; } bool Simulator::isEnabledStop(uint32_t code) { MOZ_ASSERT(code <= kMaxStopCode); MOZ_ASSERT(code > kMaxWatchpointCode); return !(watchedStops_[code].count_ & kStopDisabledBit); } void Simulator::enableStop(uint32_t code) { if (!isEnabledStop(code)) { watchedStops_[code].count_ &= ~kStopDisabledBit; } } void Simulator::disableStop(uint32_t code) { if (isEnabledStop(code)) { watchedStops_[code].count_ |= kStopDisabledBit; } } void Simulator::increaseStopCounter(uint32_t code) { MOZ_ASSERT(code <= kMaxStopCode); if ((watchedStops_[code].count_ & ~(1 << 31)) == 0x7fffffff) { printf( "Stop counter for code %i has overflowed.\n" "Enabling this code and reseting the counter to 0.\n", code); watchedStops_[code].count_ = 0; enableStop(code); } else { watchedStops_[code].count_++; } } // Print a stop status. void Simulator::printStopInfo(uint32_t code) { if (code <= kMaxWatchpointCode) { printf("That is a watchpoint, not a stop.\n"); return; } else if (code > kMaxStopCode) { printf("Code too large, only %u stops can be used\n", kMaxStopCode + 1); return; } const char* state = isEnabledStop(code) ? "Enabled" : "Disabled"; int32_t count = watchedStops_[code].count_ & ~kStopDisabledBit; // Don't print the state of unused breakpoints. if (count != 0) { if (watchedStops_[code].desc_) { printf("stop %i - 0x%x: \t%s, \tcounter = %i, \t%s\n", code, code, state, count, watchedStops_[code].desc_); } else { printf("stop %i - 0x%x: \t%s, \tcounter = %i\n", code, code, state, count); } } } void Simulator::signalExceptions() { for (int i = 1; i < kNumExceptions; i++) { if (exceptions[i] != 0) { MOZ_CRASH("Error: Exception raised."); } } } // ReverseBits(value) returns |value| in reverse bit order. template T ReverseBits(T value) { MOZ_ASSERT((sizeof(value) == 1) || (sizeof(value) == 2) || (sizeof(value) == 4) || (sizeof(value) == 8)); T result = 0; for (unsigned i = 0; i < (sizeof(value) * 8); i++) { result = (result << 1) | (value & 1); value >>= 1; } return result; } // Min/Max template functions for Double and Single arguments. template static T FPAbs(T a); template <> double FPAbs(double a) { return fabs(a); } template <> float FPAbs(float a) { return fabsf(a); } enum class MaxMinKind : int { kMin = 0, kMax = 1 }; template static bool FPUProcessNaNsAndZeros(T a, T b, MaxMinKind kind, T* result) { if (std::isnan(a) && std::isnan(b)) { *result = a; } else if (std::isnan(a)) { *result = b; } else if (std::isnan(b)) { *result = a; } else if (b == a) { // Handle -0.0 == 0.0 case. // std::signbit() returns int 0 or 1 so subtracting MaxMinKind::kMax // negates the result. *result = std::signbit(b) - static_cast(kind) ? b : a; } else { return false; } return true; } template static T FPUMin(T a, T b) { T result; if (FPUProcessNaNsAndZeros(a, b, MaxMinKind::kMin, &result)) { return result; } else { return b < a ? b : a; } } template static T FPUMax(T a, T b) { T result; if (FPUProcessNaNsAndZeros(a, b, MaxMinKind::kMax, &result)) { return result; } else { return b > a ? b : a; } } template static T FPUMinA(T a, T b) { T result; if (!FPUProcessNaNsAndZeros(a, b, MaxMinKind::kMin, &result)) { if (FPAbs(a) < FPAbs(b)) { result = a; } else if (FPAbs(b) < FPAbs(a)) { result = b; } else { result = a < b ? a : b; } } return result; } template static T FPUMaxA(T a, T b) { T result; if (!FPUProcessNaNsAndZeros(a, b, MaxMinKind::kMin, &result)) { if (FPAbs(a) > FPAbs(b)) { result = a; } else if (FPAbs(b) > FPAbs(a)) { result = b; } else { result = a > b ? a : b; } } return result; } enum class KeepSign : bool { no = false, yes }; // Handle execution based on instruction types. // decodeTypeImmediate void Simulator::decodeTypeOp6(SimInstruction* instr) { // Next pc. int64_t next_pc = bad_ra; // Used for memory instructions. int64_t alu_out = 0; // Branch instructions common part. auto BranchAndLinkHelper = [this, &next_pc](SimInstruction* instr) { int64_t current_pc = get_pc(); setRegister(ra, current_pc + SimInstruction::kInstrSize); int32_t offs26_low16 = static_cast(instr->bits(25, 10) << 16) >> 16; int32_t offs26_high10 = static_cast(instr->bits(9, 0) << 22) >> 6; int32_t offs26 = offs26_low16 | offs26_high10; next_pc = current_pc + (offs26 << 2); set_pc(next_pc); }; auto BranchOff16Helper = [this, &next_pc](SimInstruction* instr, bool do_branch) { int64_t current_pc = get_pc(); int32_t offs16 = static_cast(instr->bits(25, 10) << 16) >> 16; int32_t offs = do_branch ? (offs16 << 2) : SimInstruction::kInstrSize; next_pc = current_pc + offs; set_pc(next_pc); }; auto BranchOff21Helper = [this, &next_pc](SimInstruction* instr, bool do_branch) { int64_t current_pc = get_pc(); int32_t offs21_low16 = static_cast(instr->bits(25, 10) << 16) >> 16; int32_t offs21_high5 = static_cast(instr->bits(4, 0) << 27) >> 11; int32_t offs = offs21_low16 | offs21_high5; offs = do_branch ? (offs << 2) : SimInstruction::kInstrSize; next_pc = current_pc + offs; set_pc(next_pc); }; auto BranchOff26Helper = [this, &next_pc](SimInstruction* instr) { int64_t current_pc = get_pc(); int32_t offs26_low16 = static_cast(instr->bits(25, 10) << 16) >> 16; int32_t offs26_high10 = static_cast(instr->bits(9, 0) << 22) >> 6; int32_t offs26 = offs26_low16 | offs26_high10; next_pc = current_pc + (offs26 << 2); set_pc(next_pc); }; auto JumpOff16Helper = [this, &next_pc](SimInstruction* instr) { int32_t offs16 = static_cast(instr->bits(25, 10) << 16) >> 16; setRegister(rd_reg(instr), get_pc() + SimInstruction::kInstrSize); next_pc = rj(instr) + (offs16 << 2); set_pc(next_pc); }; switch (instr->bits(31, 26) << 26) { case op_addu16i_d: { int32_t si16_upper = static_cast(si16(instr)) << 16; alu_out = static_cast(si16_upper) + rj(instr); setRegister(rd_reg(instr), alu_out); break; } case op_beqz: { BranchOff21Helper(instr, rj(instr) == 0); break; } case op_bnez: { BranchOff21Helper(instr, rj(instr) != 0); break; } case op_bcz: { if (instr->bits(9, 8) == 0b00) { // BCEQZ BranchOff21Helper(instr, cj(instr) == false); } else if (instr->bits(9, 8) == 0b01) { // BCNEZ BranchOff21Helper(instr, cj(instr) == true); } else { UNREACHABLE(); } break; } case op_jirl: { JumpOff16Helper(instr); break; } case op_b: { BranchOff26Helper(instr); break; } case op_bl: { BranchAndLinkHelper(instr); break; } case op_beq: { BranchOff16Helper(instr, rj(instr) == rd(instr)); break; } case op_bne: { BranchOff16Helper(instr, rj(instr) != rd(instr)); break; } case op_blt: { BranchOff16Helper(instr, rj(instr) < rd(instr)); break; } case op_bge: { BranchOff16Helper(instr, rj(instr) >= rd(instr)); break; } case op_bltu: { BranchOff16Helper(instr, rj_u(instr) < rd_u(instr)); break; } case op_bgeu: { BranchOff16Helper(instr, rj_u(instr) >= rd_u(instr)); break; } default: UNREACHABLE(); } } void Simulator::decodeTypeOp7(SimInstruction* instr) { int64_t alu_out; switch (instr->bits(31, 25) << 25) { case op_lu12i_w: { int32_t si20_upper = static_cast(si20(instr) << 12); setRegister(rd_reg(instr), static_cast(si20_upper)); break; } case op_lu32i_d: { int32_t si20_signExtend = static_cast(si20(instr) << 12) >> 12; int64_t lower_32bit_mask = 0xFFFFFFFF; alu_out = (static_cast(si20_signExtend) << 32) | (rd(instr) & lower_32bit_mask); setRegister(rd_reg(instr), alu_out); break; } case op_pcaddi: { int32_t si20_signExtend = static_cast(si20(instr) << 12) >> 10; int64_t current_pc = get_pc(); alu_out = static_cast(si20_signExtend) + current_pc; setRegister(rd_reg(instr), alu_out); break; } case op_pcalau12i: { int32_t si20_signExtend = static_cast(si20(instr) << 12); int64_t current_pc = get_pc(); int64_t clear_lower12bit_mask = 0xFFFFFFFFFFFFF000; alu_out = static_cast(si20_signExtend) + current_pc; setRegister(rd_reg(instr), alu_out & clear_lower12bit_mask); break; } case op_pcaddu12i: { int32_t si20_signExtend = static_cast(si20(instr) << 12); int64_t current_pc = get_pc(); alu_out = static_cast(si20_signExtend) + current_pc; setRegister(rd_reg(instr), alu_out); break; } case op_pcaddu18i: { int64_t si20_signExtend = (static_cast(si20(instr)) << 44) >> 26; int64_t current_pc = get_pc(); alu_out = si20_signExtend + current_pc; setRegister(rd_reg(instr), alu_out); break; } default: UNREACHABLE(); } } void Simulator::decodeTypeOp8(SimInstruction* instr) { int64_t addr = 0x0; int64_t si14_se = (static_cast(si14(instr)) << 50) >> 48; switch (instr->bits(31, 24) << 24) { case op_ldptr_w: { setRegister(rd_reg(instr), readW(rj(instr) + si14_se, instr)); break; } case op_stptr_w: { writeW(rj(instr) + si14_se, static_cast(rd(instr)), instr); break; } case op_ldptr_d: { setRegister(rd_reg(instr), readDW(rj(instr) + si14_se, instr)); break; } case op_stptr_d: { writeDW(rj(instr) + si14_se, rd(instr), instr); break; } case op_ll_w: { addr = si14_se + rj(instr); setRegister(rd_reg(instr), loadLinkedW(addr, instr)); break; } case op_sc_w: { addr = si14_se + rj(instr); setRegister( rd_reg(instr), storeConditionalW(addr, static_cast(rd(instr)), instr)); break; } case op_ll_d: { addr = si14_se + rj(instr); setRegister(rd_reg(instr), loadLinkedD(addr, instr)); break; } case op_sc_d: { addr = si14_se + rj(instr); setRegister(rd_reg(instr), storeConditionalD(addr, rd(instr), instr)); break; } default: UNREACHABLE(); } } void Simulator::decodeTypeOp10(SimInstruction* instr) { int64_t alu_out = 0x0; int64_t si12_se = (static_cast(si12(instr)) << 52) >> 52; uint64_t si12_ze = (static_cast(ui12(instr)) << 52) >> 52; switch (instr->bits(31, 22) << 22) { case op_bstrins_d: { uint8_t lsbd_ = lsbd(instr); uint8_t msbd_ = msbd(instr); MOZ_ASSERT(lsbd_ <= msbd_); uint8_t size = msbd_ - lsbd_ + 1; if (size < 64) { uint64_t mask = (1ULL << size) - 1; alu_out = (rd_u(instr) & ~(mask << lsbd_)) | ((rj_u(instr) & mask) << lsbd_); setRegister(rd_reg(instr), alu_out); } else if (size == 64) { setRegister(rd_reg(instr), rj(instr)); } break; } case op_bstrpick_d: { uint8_t lsbd_ = lsbd(instr); uint8_t msbd_ = msbd(instr); MOZ_ASSERT(lsbd_ <= msbd_); uint8_t size = msbd_ - lsbd_ + 1; if (size < 64) { uint64_t mask = (1ULL << size) - 1; alu_out = (rj_u(instr) & (mask << lsbd_)) >> lsbd_; setRegister(rd_reg(instr), alu_out); } else if (size == 64) { setRegister(rd_reg(instr), rj(instr)); } break; } case op_slti: { setRegister(rd_reg(instr), rj(instr) < si12_se ? 1 : 0); break; } case op_sltui: { setRegister(rd_reg(instr), rj_u(instr) < static_cast(si12_se) ? 1 : 0); break; } case op_addi_w: { int32_t alu32_out = static_cast(rj(instr)) + static_cast(si12_se); setRegister(rd_reg(instr), alu32_out); break; } case op_addi_d: { setRegister(rd_reg(instr), rj(instr) + si12_se); break; } case op_lu52i_d: { int64_t si12_se = static_cast(si12(instr)) << 52; uint64_t mask = (1ULL << 52) - 1; alu_out = si12_se + (rj(instr) & mask); setRegister(rd_reg(instr), alu_out); break; } case op_andi: { setRegister(rd_reg(instr), rj(instr) & si12_ze); break; } case op_ori: { setRegister(rd_reg(instr), rj_u(instr) | si12_ze); break; } case op_xori: { setRegister(rd_reg(instr), rj_u(instr) ^ si12_ze); break; } case op_ld_b: { setRegister(rd_reg(instr), readB(rj(instr) + si12_se)); break; } case op_ld_h: { setRegister(rd_reg(instr), readH(rj(instr) + si12_se, instr)); break; } case op_ld_w: { setRegister(rd_reg(instr), readW(rj(instr) + si12_se, instr)); break; } case op_ld_d: { setRegister(rd_reg(instr), readDW(rj(instr) + si12_se, instr)); break; } case op_st_b: { writeB(rj(instr) + si12_se, static_cast(rd(instr))); break; } case op_st_h: { writeH(rj(instr) + si12_se, static_cast(rd(instr)), instr); break; } case op_st_w: { writeW(rj(instr) + si12_se, static_cast(rd(instr)), instr); break; } case op_st_d: { writeDW(rj(instr) + si12_se, rd(instr), instr); break; } case op_ld_bu: { setRegister(rd_reg(instr), readBU(rj(instr) + si12_se)); break; } case op_ld_hu: { setRegister(rd_reg(instr), readHU(rj(instr) + si12_se, instr)); break; } case op_ld_wu: { setRegister(rd_reg(instr), readWU(rj(instr) + si12_se, instr)); break; } case op_fld_s: { setFpuRegister(fd_reg(instr), kFPUInvalidResult); // Trash upper 32 bits. setFpuRegisterWord(fd_reg(instr), readW(rj(instr) + si12_se, instr)); break; } case op_fst_s: { int32_t alu_out_32 = static_cast(getFpuRegister(fd_reg(instr))); writeW(rj(instr) + si12_se, alu_out_32, instr); break; } case op_fld_d: { setFpuRegisterDouble(fd_reg(instr), readD(rj(instr) + si12_se, instr)); break; } case op_fst_d: { writeD(rj(instr) + si12_se, getFpuRegisterDouble(fd_reg(instr)), instr); break; } case op_preld: UNIMPLEMENTED(); break; default: UNREACHABLE(); } } void Simulator::decodeTypeOp11(SimInstruction* instr) { int64_t alu_out = 0x0; switch (instr->bits(31, 21) << 21) { case op_bstr_w: { MOZ_ASSERT(instr->bit(21) == 1); uint8_t lsbw_ = lsbw(instr); uint8_t msbw_ = msbw(instr); MOZ_ASSERT(lsbw_ <= msbw_); uint8_t size = msbw_ - lsbw_ + 1; uint64_t mask = (1ULL << size) - 1; if (instr->bit(15) == 0) { // BSTRINS_W alu_out = static_cast((rd_u(instr) & ~(mask << lsbw_)) | ((rj_u(instr) & mask) << lsbw_)); } else { // BSTRPICK_W alu_out = static_cast((rj_u(instr) & (mask << lsbw_)) >> lsbw_); } setRegister(rd_reg(instr), alu_out); break; } default: UNREACHABLE(); } } void Simulator::decodeTypeOp12(SimInstruction* instr) { switch (instr->bits(31, 20) << 20) { case op_fmadd_s: { setFpuRegisterFloat( fd_reg(instr), std::fma(fj_float(instr), fk_float(instr), fa_float(instr))); break; } case op_fmadd_d: { setFpuRegisterDouble( fd_reg(instr), std::fma(fj_double(instr), fk_double(instr), fa_double(instr))); break; } case op_fmsub_s: { setFpuRegisterFloat( fd_reg(instr), std::fma(-fj_float(instr), fk_float(instr), fa_float(instr))); break; } case op_fmsub_d: { setFpuRegisterDouble( fd_reg(instr), std::fma(-fj_double(instr), fk_double(instr), fa_double(instr))); break; } case op_fnmadd_s: { setFpuRegisterFloat( fd_reg(instr), std::fma(-fj_float(instr), fk_float(instr), -fa_float(instr))); break; } case op_fnmadd_d: { setFpuRegisterDouble( fd_reg(instr), std::fma(-fj_double(instr), fk_double(instr), -fa_double(instr))); break; } case op_fnmsub_s: { setFpuRegisterFloat( fd_reg(instr), std::fma(fj_float(instr), fk_float(instr), -fa_float(instr))); break; } case op_fnmsub_d: { setFpuRegisterDouble( fd_reg(instr), std::fma(fj_double(instr), fk_double(instr), -fa_double(instr))); break; } case op_fcmp_cond_s: { MOZ_ASSERT(instr->bits(4, 3) == 0); float fj = fj_float(instr); float fk = fk_float(instr); switch (cond(instr)) { case AssemblerLOONG64::CAF: { setCFRegister(cd_reg(instr), false); break; } case AssemblerLOONG64::CUN: { setCFRegister(cd_reg(instr), std::isnan(fj) || std::isnan(fk)); break; } case AssemblerLOONG64::CEQ: { setCFRegister(cd_reg(instr), fj == fk); break; } case AssemblerLOONG64::CUEQ: { setCFRegister(cd_reg(instr), (fj == fk) || std::isnan(fj) || std::isnan(fk)); break; } case AssemblerLOONG64::CLT: { setCFRegister(cd_reg(instr), fj < fk); break; } case AssemblerLOONG64::CULT: { setCFRegister(cd_reg(instr), (fj < fk) || std::isnan(fj) || std::isnan(fk)); break; } case AssemblerLOONG64::CLE: { setCFRegister(cd_reg(instr), fj <= fk); break; } case AssemblerLOONG64::CULE: { setCFRegister(cd_reg(instr), (fj <= fk) || std::isnan(fj) || std::isnan(fk)); break; } case AssemblerLOONG64::CNE: { setCFRegister(cd_reg(instr), (fj < fk) || (fj > fk)); break; } case AssemblerLOONG64::COR: { setCFRegister(cd_reg(instr), !std::isnan(fj) && !std::isnan(fk)); break; } case AssemblerLOONG64::CUNE: { setCFRegister(cd_reg(instr), (fj < fk) || (fj > fk) || std::isnan(fj) || std::isnan(fk)); break; } case AssemblerLOONG64::SAF: UNIMPLEMENTED(); break; case AssemblerLOONG64::SUN: UNIMPLEMENTED(); break; case AssemblerLOONG64::SEQ: UNIMPLEMENTED(); break; case AssemblerLOONG64::SUEQ: UNIMPLEMENTED(); break; case AssemblerLOONG64::SLT: UNIMPLEMENTED(); break; case AssemblerLOONG64::SULT: UNIMPLEMENTED(); break; case AssemblerLOONG64::SLE: UNIMPLEMENTED(); break; case AssemblerLOONG64::SULE: UNIMPLEMENTED(); break; case AssemblerLOONG64::SNE: UNIMPLEMENTED(); break; case AssemblerLOONG64::SOR: UNIMPLEMENTED(); break; case AssemblerLOONG64::SUNE: UNIMPLEMENTED(); break; default: UNREACHABLE(); } break; } case op_fcmp_cond_d: { MOZ_ASSERT(instr->bits(4, 3) == 0); double fj = fj_double(instr); double fk = fk_double(instr); switch (cond(instr)) { case AssemblerLOONG64::CAF: { setCFRegister(cd_reg(instr), false); break; } case AssemblerLOONG64::CUN: { setCFRegister(cd_reg(instr), std::isnan(fj) || std::isnan(fk)); break; } case AssemblerLOONG64::CEQ: { setCFRegister(cd_reg(instr), fj == fk); break; } case AssemblerLOONG64::CUEQ: { setCFRegister(cd_reg(instr), (fj == fk) || std::isnan(fj) || std::isnan(fk)); break; } case AssemblerLOONG64::CLT: { setCFRegister(cd_reg(instr), fj < fk); break; } case AssemblerLOONG64::CULT: { setCFRegister(cd_reg(instr), (fj < fk) || std::isnan(fj) || std::isnan(fk)); break; } case AssemblerLOONG64::CLE: { setCFRegister(cd_reg(instr), fj <= fk); break; } case AssemblerLOONG64::CULE: { setCFRegister(cd_reg(instr), (fj <= fk) || std::isnan(fj) || std::isnan(fk)); break; } case AssemblerLOONG64::CNE: { setCFRegister(cd_reg(instr), (fj < fk) || (fj > fk)); break; } case AssemblerLOONG64::COR: { setCFRegister(cd_reg(instr), !std::isnan(fj) && !std::isnan(fk)); break; } case AssemblerLOONG64::CUNE: { setCFRegister(cd_reg(instr), (fj != fk) || std::isnan(fj) || std::isnan(fk)); break; } case AssemblerLOONG64::SAF: UNIMPLEMENTED(); break; case AssemblerLOONG64::SUN: UNIMPLEMENTED(); break; case AssemblerLOONG64::SEQ: UNIMPLEMENTED(); break; case AssemblerLOONG64::SUEQ: UNIMPLEMENTED(); break; case AssemblerLOONG64::SLT: UNIMPLEMENTED(); break; case AssemblerLOONG64::SULT: UNIMPLEMENTED(); break; case AssemblerLOONG64::SLE: UNIMPLEMENTED(); break; case AssemblerLOONG64::SULE: UNIMPLEMENTED(); break; case AssemblerLOONG64::SNE: UNIMPLEMENTED(); break; case AssemblerLOONG64::SOR: UNIMPLEMENTED(); break; case AssemblerLOONG64::SUNE: UNIMPLEMENTED(); break; default: UNREACHABLE(); } break; } default: UNREACHABLE(); } } void Simulator::decodeTypeOp14(SimInstruction* instr) { int64_t alu_out = 0x0; switch (instr->bits(31, 18) << 18) { case op_bytepick_d: { uint8_t sa = sa3(instr) * 8; if (sa == 0) { alu_out = rk(instr); } else { int64_t mask = (1ULL << 63) >> (sa - 1); int64_t rk_hi = (rk(instr) & (~mask)) << sa; int64_t rj_lo = (rj(instr) & mask) >> (64 - sa); alu_out = rk_hi | rj_lo; } setRegister(rd_reg(instr), alu_out); break; } case op_fsel: { MOZ_ASSERT(instr->bits(19, 18) == 0); if (ca(instr) == 0) { setFpuRegisterDouble(fd_reg(instr), fj_double(instr)); } else { setFpuRegisterDouble(fd_reg(instr), fk_double(instr)); } break; } default: UNREACHABLE(); } } void Simulator::decodeTypeOp15(SimInstruction* instr) { int64_t alu_out = 0x0; int32_t alu32_out = 0x0; switch (instr->bits(31, 17) << 17) { case op_bytepick_w: { MOZ_ASSERT(instr->bit(17) == 0); uint8_t sa = sa2(instr) * 8; if (sa == 0) { alu32_out = static_cast(rk(instr)); } else { int32_t mask = (1 << 31) >> (sa - 1); int32_t rk_hi = (static_cast(rk(instr)) & (~mask)) << sa; int32_t rj_lo = (static_cast(rj(instr)) & mask) >> (32 - sa); alu32_out = rk_hi | rj_lo; } setRegister(rd_reg(instr), static_cast(alu32_out)); break; } case op_alsl_w: { uint8_t sa = sa2(instr) + 1; alu32_out = (static_cast(rj(instr)) << sa) + static_cast(rk(instr)); setRegister(rd_reg(instr), alu32_out); break; } case op_alsl_wu: { uint8_t sa = sa2(instr) + 1; alu32_out = (static_cast(rj(instr)) << sa) + static_cast(rk(instr)); setRegister(rd_reg(instr), static_cast(alu32_out)); break; } case op_alsl_d: { MOZ_ASSERT(instr->bit(17) == 0); uint8_t sa = sa2(instr) + 1; alu_out = (rj(instr) << sa) + rk(instr); setRegister(rd_reg(instr), alu_out); break; } default: UNREACHABLE(); } } void Simulator::decodeTypeOp16(SimInstruction* instr) { int64_t alu_out; switch (instr->bits(31, 16) << 16) { case op_slli_d: { MOZ_ASSERT(instr->bit(17) == 0); MOZ_ASSERT(instr->bits(17, 16) == 0b01); setRegister(rd_reg(instr), rj(instr) << ui6(instr)); break; } case op_srli_d: { MOZ_ASSERT(instr->bit(17) == 0); setRegister(rd_reg(instr), rj_u(instr) >> ui6(instr)); break; } case op_srai_d: { MOZ_ASSERT(instr->bit(17) == 0); setRegister(rd_reg(instr), rj(instr) >> ui6(instr)); break; } case op_rotri_d: { MOZ_ASSERT(instr->bit(17) == 0); MOZ_ASSERT(instr->bits(17, 16) == 0b01); alu_out = static_cast(RotateRight64(rj_u(instr), ui6(instr))); setRegister(rd_reg(instr), alu_out); break; } default: UNREACHABLE(); } } void Simulator::decodeTypeOp17(SimInstruction* instr) { int64_t alu_out; int32_t alu32_out; switch (instr->bits(31, 15) << 15) { case op_slli_w: { MOZ_ASSERT(instr->bit(17) == 0); MOZ_ASSERT(instr->bits(17, 15) == 0b001); alu32_out = static_cast(rj(instr)) << ui5(instr); setRegister(rd_reg(instr), static_cast(alu32_out)); break; } case op_srai_w: { MOZ_ASSERT(instr->bit(17) == 0); MOZ_ASSERT(instr->bits(17, 15) == 0b001); alu32_out = static_cast(rj(instr)) >> ui5(instr); setRegister(rd_reg(instr), static_cast(alu32_out)); break; } case op_rotri_w: { MOZ_ASSERT(instr->bit(17) == 0); MOZ_ASSERT(instr->bits(17, 15) == 0b001); alu32_out = static_cast( RotateRight32(static_cast(rj_u(instr)), static_cast(ui5(instr)))); setRegister(rd_reg(instr), static_cast(alu32_out)); break; } case op_srli_w: { MOZ_ASSERT(instr->bit(17) == 0); MOZ_ASSERT(instr->bits(17, 15) == 0b001); alu32_out = static_cast(rj(instr)) >> ui5(instr); setRegister(rd_reg(instr), static_cast(alu32_out)); break; } case op_add_w: { int32_t alu32_out = static_cast(rj(instr) + rk(instr)); // Sign-extend result of 32bit operation into 64bit register. setRegister(rd_reg(instr), static_cast(alu32_out)); break; } case op_add_d: setRegister(rd_reg(instr), rj(instr) + rk(instr)); break; case op_sub_w: { int32_t alu32_out = static_cast(rj(instr) - rk(instr)); // Sign-extend result of 32bit operation into 64bit register. setRegister(rd_reg(instr), static_cast(alu32_out)); break; } case op_sub_d: setRegister(rd_reg(instr), rj(instr) - rk(instr)); break; case op_slt: setRegister(rd_reg(instr), rj(instr) < rk(instr) ? 1 : 0); break; case op_sltu: setRegister(rd_reg(instr), rj_u(instr) < rk_u(instr) ? 1 : 0); break; case op_maskeqz: setRegister(rd_reg(instr), rk(instr) == 0 ? 0 : rj(instr)); break; case op_masknez: setRegister(rd_reg(instr), rk(instr) != 0 ? 0 : rj(instr)); break; case op_nor: setRegister(rd_reg(instr), ~(rj(instr) | rk(instr))); break; case op_and: setRegister(rd_reg(instr), rj(instr) & rk(instr)); break; case op_or: setRegister(rd_reg(instr), rj(instr) | rk(instr)); break; case op_xor: setRegister(rd_reg(instr), rj(instr) ^ rk(instr)); break; case op_orn: setRegister(rd_reg(instr), rj(instr) | (~rk(instr))); break; case op_andn: setRegister(rd_reg(instr), rj(instr) & (~rk(instr))); break; case op_sll_w: setRegister(rd_reg(instr), (int32_t)rj(instr) << (rk_u(instr) % 32)); break; case op_srl_w: { alu_out = static_cast((uint32_t)rj_u(instr) >> (rk_u(instr) % 32)); setRegister(rd_reg(instr), alu_out); break; } case op_sra_w: setRegister(rd_reg(instr), (int32_t)rj(instr) >> (rk_u(instr) % 32)); break; case op_sll_d: setRegister(rd_reg(instr), rj(instr) << (rk_u(instr) % 64)); break; case op_srl_d: { alu_out = static_cast(rj_u(instr) >> (rk_u(instr) % 64)); setRegister(rd_reg(instr), alu_out); break; } case op_sra_d: setRegister(rd_reg(instr), rj(instr) >> (rk_u(instr) % 64)); break; case op_rotr_w: { alu_out = static_cast( RotateRight32(static_cast(rj_u(instr)), static_cast(rk_u(instr) % 32))); setRegister(rd_reg(instr), alu_out); break; } case op_rotr_d: { alu_out = static_cast( RotateRight64((rj_u(instr)), (rk_u(instr) % 64))); setRegister(rd_reg(instr), alu_out); break; } case op_mul_w: { alu_out = static_cast(rj(instr)) * static_cast(rk(instr)); setRegister(rd_reg(instr), alu_out); break; } case op_mulh_w: { int32_t rj_lo = static_cast(rj(instr)); int32_t rk_lo = static_cast(rk(instr)); alu_out = static_cast(rj_lo) * static_cast(rk_lo); setRegister(rd_reg(instr), alu_out >> 32); break; } case op_mulh_wu: { uint32_t rj_lo = static_cast(rj_u(instr)); uint32_t rk_lo = static_cast(rk_u(instr)); alu_out = static_cast(rj_lo) * static_cast(rk_lo); setRegister(rd_reg(instr), alu_out >> 32); break; } case op_mul_d: setRegister(rd_reg(instr), rj(instr) * rk(instr)); break; case op_mulh_d: setRegister(rd_reg(instr), MultiplyHighSigned(rj(instr), rk(instr))); break; case op_mulh_du: setRegister(rd_reg(instr), MultiplyHighUnsigned(rj_u(instr), rk_u(instr))); break; case op_mulw_d_w: { int64_t rj_i32 = static_cast(rj(instr)); int64_t rk_i32 = static_cast(rk(instr)); setRegister(rd_reg(instr), rj_i32 * rk_i32); break; } case op_mulw_d_wu: { uint64_t rj_u32 = static_cast(rj_u(instr)); uint64_t rk_u32 = static_cast(rk_u(instr)); setRegister(rd_reg(instr), rj_u32 * rk_u32); break; } case op_div_w: { int32_t rj_i32 = static_cast(rj(instr)); int32_t rk_i32 = static_cast(rk(instr)); if (rj_i32 == INT_MIN && rk_i32 == -1) { setRegister(rd_reg(instr), INT_MIN); } else if (rk_i32 != 0) { setRegister(rd_reg(instr), rj_i32 / rk_i32); } break; } case op_mod_w: { int32_t rj_i32 = static_cast(rj(instr)); int32_t rk_i32 = static_cast(rk(instr)); if (rj_i32 == INT_MIN && rk_i32 == -1) { setRegister(rd_reg(instr), 0); } else if (rk_i32 != 0) { setRegister(rd_reg(instr), rj_i32 % rk_i32); } break; } case op_div_wu: { uint32_t rj_u32 = static_cast(rj(instr)); uint32_t rk_u32 = static_cast(rk(instr)); if (rk_u32 != 0) { setRegister(rd_reg(instr), static_cast(rj_u32 / rk_u32)); } break; } case op_mod_wu: { uint32_t rj_u32 = static_cast(rj(instr)); uint32_t rk_u32 = static_cast(rk(instr)); if (rk_u32 != 0) { setRegister(rd_reg(instr), static_cast(rj_u32 % rk_u32)); } break; } case op_div_d: { if (rj(instr) == INT64_MIN && rk(instr) == -1) { setRegister(rd_reg(instr), INT64_MIN); } else if (rk(instr) != 0) { setRegister(rd_reg(instr), rj(instr) / rk(instr)); } break; } case op_mod_d: { if (rj(instr) == LONG_MIN && rk(instr) == -1) { setRegister(rd_reg(instr), 0); } else if (rk(instr) != 0) { setRegister(rd_reg(instr), rj(instr) % rk(instr)); } break; } case op_div_du: { if (rk_u(instr) != 0) { setRegister(rd_reg(instr), static_cast(rj_u(instr) / rk_u(instr))); } break; } case op_mod_du: { if (rk_u(instr) != 0) { setRegister(rd_reg(instr), static_cast(rj_u(instr) % rk_u(instr))); } break; } case op_break: softwareInterrupt(instr); break; case op_fadd_s: { setFpuRegisterFloat(fd_reg(instr), fj_float(instr) + fk_float(instr)); break; } case op_fadd_d: { setFpuRegisterDouble(fd_reg(instr), fj_double(instr) + fk_double(instr)); break; } case op_fsub_s: { setFpuRegisterFloat(fd_reg(instr), fj_float(instr) - fk_float(instr)); break; } case op_fsub_d: { setFpuRegisterDouble(fd_reg(instr), fj_double(instr) - fk_double(instr)); break; } case op_fmul_s: { setFpuRegisterFloat(fd_reg(instr), fj_float(instr) * fk_float(instr)); break; } case op_fmul_d: { setFpuRegisterDouble(fd_reg(instr), fj_double(instr) * fk_double(instr)); break; } case op_fdiv_s: { setFpuRegisterFloat(fd_reg(instr), fj_float(instr) / fk_float(instr)); break; } case op_fdiv_d: { setFpuRegisterDouble(fd_reg(instr), fj_double(instr) / fk_double(instr)); break; } case op_fmax_s: { setFpuRegisterFloat(fd_reg(instr), FPUMax(fk_float(instr), fj_float(instr))); break; } case op_fmax_d: { setFpuRegisterDouble(fd_reg(instr), FPUMax(fk_double(instr), fj_double(instr))); break; } case op_fmin_s: { setFpuRegisterFloat(fd_reg(instr), FPUMin(fk_float(instr), fj_float(instr))); break; } case op_fmin_d: { setFpuRegisterDouble(fd_reg(instr), FPUMin(fk_double(instr), fj_double(instr))); break; } case op_fmaxa_s: { setFpuRegisterFloat(fd_reg(instr), FPUMaxA(fk_float(instr), fj_float(instr))); break; } case op_fmaxa_d: { setFpuRegisterDouble(fd_reg(instr), FPUMaxA(fk_double(instr), fj_double(instr))); break; } case op_fmina_s: { setFpuRegisterFloat(fd_reg(instr), FPUMinA(fk_float(instr), fj_float(instr))); break; } case op_fmina_d: { setFpuRegisterDouble(fd_reg(instr), FPUMinA(fk_double(instr), fj_double(instr))); break; } case op_ldx_b: setRegister(rd_reg(instr), readB(rj(instr) + rk(instr))); break; case op_ldx_h: setRegister(rd_reg(instr), readH(rj(instr) + rk(instr), instr)); break; case op_ldx_w: setRegister(rd_reg(instr), readW(rj(instr) + rk(instr), instr)); break; case op_ldx_d: setRegister(rd_reg(instr), readDW(rj(instr) + rk(instr), instr)); break; case op_stx_b: writeB(rj(instr) + rk(instr), static_cast(rd(instr))); break; case op_stx_h: writeH(rj(instr) + rk(instr), static_cast(rd(instr)), instr); break; case op_stx_w: writeW(rj(instr) + rk(instr), static_cast(rd(instr)), instr); break; case op_stx_d: writeDW(rj(instr) + rk(instr), rd(instr), instr); break; case op_ldx_bu: setRegister(rd_reg(instr), readBU(rj(instr) + rk(instr))); break; case op_ldx_hu: setRegister(rd_reg(instr), readHU(rj(instr) + rk(instr), instr)); break; case op_ldx_wu: setRegister(rd_reg(instr), readWU(rj(instr) + rk(instr), instr)); break; case op_fldx_s: setFpuRegister(fd_reg(instr), kFPUInvalidResult); // Trash upper 32 bits. setFpuRegisterWord(fd_reg(instr), readW(rj(instr) + rk(instr), instr)); break; case op_fldx_d: setFpuRegister(fd_reg(instr), kFPUInvalidResult); // Trash upper 32 bits. setFpuRegisterDouble(fd_reg(instr), readD(rj(instr) + rk(instr), instr)); break; case op_fstx_s: { int32_t alu_out_32 = static_cast(getFpuRegister(fd_reg(instr))); writeW(rj(instr) + rk(instr), alu_out_32, instr); break; } case op_fstx_d: { writeD(rj(instr) + rk(instr), getFpuRegisterDouble(fd_reg(instr)), instr); break; } case op_amswap_w: UNIMPLEMENTED(); break; case op_amswap_d: UNIMPLEMENTED(); break; case op_amadd_w: UNIMPLEMENTED(); break; case op_amadd_d: UNIMPLEMENTED(); break; case op_amand_w: UNIMPLEMENTED(); break; case op_amand_d: UNIMPLEMENTED(); break; case op_amor_w: UNIMPLEMENTED(); break; case op_amor_d: UNIMPLEMENTED(); break; case op_amxor_w: UNIMPLEMENTED(); break; case op_amxor_d: UNIMPLEMENTED(); break; case op_ammax_w: UNIMPLEMENTED(); break; case op_ammax_d: UNIMPLEMENTED(); break; case op_ammin_w: UNIMPLEMENTED(); break; case op_ammin_d: UNIMPLEMENTED(); break; case op_ammax_wu: UNIMPLEMENTED(); break; case op_ammax_du: UNIMPLEMENTED(); break; case op_ammin_wu: UNIMPLEMENTED(); break; case op_ammin_du: UNIMPLEMENTED(); break; case op_amswap_db_w: UNIMPLEMENTED(); break; case op_amswap_db_d: UNIMPLEMENTED(); break; case op_amadd_db_w: UNIMPLEMENTED(); break; case op_amadd_db_d: UNIMPLEMENTED(); break; case op_amand_db_w: UNIMPLEMENTED(); break; case op_amand_db_d: UNIMPLEMENTED(); break; case op_amor_db_w: UNIMPLEMENTED(); break; case op_amor_db_d: UNIMPLEMENTED(); break; case op_amxor_db_w: UNIMPLEMENTED(); break; case op_amxor_db_d: UNIMPLEMENTED(); break; case op_ammax_db_w: UNIMPLEMENTED(); break; case op_ammax_db_d: UNIMPLEMENTED(); break; case op_ammin_db_w: UNIMPLEMENTED(); break; case op_ammin_db_d: UNIMPLEMENTED(); break; case op_ammax_db_wu: UNIMPLEMENTED(); break; case op_ammax_db_du: UNIMPLEMENTED(); break; case op_ammin_db_wu: UNIMPLEMENTED(); break; case op_ammin_db_du: UNIMPLEMENTED(); break; case op_dbar: // TODO(loong64): dbar simulation break; case op_ibar: UNIMPLEMENTED(); break; case op_fcopysign_s: UNIMPLEMENTED(); break; case op_fcopysign_d: UNIMPLEMENTED(); break; default: UNREACHABLE(); } } void Simulator::decodeTypeOp22(SimInstruction* instr) { int64_t alu_out; switch (instr->bits(31, 10) << 10) { case op_clz_w: { alu_out = U32(rj_u(instr)) ? __builtin_clz(U32(rj_u(instr))) : 32; setRegister(rd_reg(instr), alu_out); break; } case op_ctz_w: { alu_out = U32(rj_u(instr)) ? __builtin_ctz(U32(rj_u(instr))) : 32; setRegister(rd_reg(instr), alu_out); break; } case op_clz_d: { alu_out = U64(rj_u(instr)) ? __builtin_clzll(U64(rj_u(instr))) : 64; setRegister(rd_reg(instr), alu_out); break; } case op_ctz_d: { alu_out = U64(rj_u(instr)) ? __builtin_ctzll(U64(rj_u(instr))) : 64; setRegister(rd_reg(instr), alu_out); break; } case op_revb_2h: { uint32_t input = static_cast(rj(instr)); uint64_t output = 0; uint32_t mask = 0xFF000000; for (int i = 0; i < 4; i++) { uint32_t tmp = mask & input; if (i % 2 == 0) { tmp = tmp >> 8; } else { tmp = tmp << 8; } output = output | tmp; mask = mask >> 8; } alu_out = static_cast(static_cast(output)); setRegister(rd_reg(instr), alu_out); break; } case op_revb_4h: { uint64_t input = rj_u(instr); uint64_t output = 0; uint64_t mask = 0xFF00000000000000; for (int i = 0; i < 8; i++) { uint64_t tmp = mask & input; if (i % 2 == 0) { tmp = tmp >> 8; } else { tmp = tmp << 8; } output = output | tmp; mask = mask >> 8; } alu_out = static_cast(output); setRegister(rd_reg(instr), alu_out); break; } case op_revb_2w: { uint64_t input = rj_u(instr); uint64_t output = 0; uint64_t mask = 0xFF000000FF000000; for (int i = 0; i < 4; i++) { uint64_t tmp = mask & input; if (i <= 1) { tmp = tmp >> (24 - i * 16); } else { tmp = tmp << (i * 16 - 24); } output = output | tmp; mask = mask >> 8; } alu_out = static_cast(output); setRegister(rd_reg(instr), alu_out); break; } case op_revb_d: { uint64_t input = rj_u(instr); uint64_t output = 0; uint64_t mask = 0xFF00000000000000; for (int i = 0; i < 8; i++) { uint64_t tmp = mask & input; if (i <= 3) { tmp = tmp >> (56 - i * 16); } else { tmp = tmp << (i * 16 - 56); } output = output | tmp; mask = mask >> 8; } alu_out = static_cast(output); setRegister(rd_reg(instr), alu_out); break; } case op_revh_2w: { uint64_t input = rj_u(instr); uint64_t output = 0; uint64_t mask = 0xFFFF000000000000; for (int i = 0; i < 4; i++) { uint64_t tmp = mask & input; if (i % 2 == 0) { tmp = tmp >> 16; } else { tmp = tmp << 16; } output = output | tmp; mask = mask >> 16; } alu_out = static_cast(output); setRegister(rd_reg(instr), alu_out); break; } case op_revh_d: { uint64_t input = rj_u(instr); uint64_t output = 0; uint64_t mask = 0xFFFF000000000000; for (int i = 0; i < 4; i++) { uint64_t tmp = mask & input; if (i <= 1) { tmp = tmp >> (48 - i * 32); } else { tmp = tmp << (i * 32 - 48); } output = output | tmp; mask = mask >> 16; } alu_out = static_cast(output); setRegister(rd_reg(instr), alu_out); break; } case op_bitrev_4b: { uint32_t input = static_cast(rj(instr)); uint32_t output = 0; uint8_t i_byte, o_byte; // Reverse the bit in byte for each individual byte for (int i = 0; i < 4; i++) { output = output >> 8; i_byte = input & 0xFF; // Fast way to reverse bits in byte // Devised by Sean Anderson, July 13, 2001 o_byte = static_cast(((i_byte * 0x0802LU & 0x22110LU) | (i_byte * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16); output = output | (static_cast(o_byte << 24)); input = input >> 8; } alu_out = static_cast(static_cast(output)); setRegister(rd_reg(instr), alu_out); break; } case op_bitrev_8b: { uint64_t input = rj_u(instr); uint64_t output = 0; uint8_t i_byte, o_byte; // Reverse the bit in byte for each individual byte for (int i = 0; i < 8; i++) { output = output >> 8; i_byte = input & 0xFF; // Fast way to reverse bits in byte // Devised by Sean Anderson, July 13, 2001 o_byte = static_cast(((i_byte * 0x0802LU & 0x22110LU) | (i_byte * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16); output = output | (static_cast(o_byte) << 56); input = input >> 8; } alu_out = static_cast(output); setRegister(rd_reg(instr), alu_out); break; } case op_bitrev_w: { uint32_t input = static_cast(rj(instr)); uint32_t output = 0; output = ReverseBits(input); alu_out = static_cast(static_cast(output)); setRegister(rd_reg(instr), alu_out); break; } case op_bitrev_d: { alu_out = static_cast(ReverseBits(rj_u(instr))); setRegister(rd_reg(instr), alu_out); break; } case op_ext_w_b: { uint8_t input = static_cast(rj(instr)); alu_out = static_cast(static_cast(input)); setRegister(rd_reg(instr), alu_out); break; } case op_ext_w_h: { uint16_t input = static_cast(rj(instr)); alu_out = static_cast(static_cast(input)); setRegister(rd_reg(instr), alu_out); break; } case op_fabs_s: { setFpuRegisterFloat(fd_reg(instr), std::abs(fj_float(instr))); break; } case op_fabs_d: { setFpuRegisterDouble(fd_reg(instr), std::abs(fj_double(instr))); break; } case op_fneg_s: { setFpuRegisterFloat(fd_reg(instr), -fj_float(instr)); break; } case op_fneg_d: { setFpuRegisterDouble(fd_reg(instr), -fj_double(instr)); break; } case op_fsqrt_s: { if (fj_float(instr) >= 0) { setFpuRegisterFloat(fd_reg(instr), std::sqrt(fj_float(instr))); } else { setFpuRegisterFloat(fd_reg(instr), std::sqrt(-1)); // qnan setFCSRBit(kFCSRInvalidOpFlagBit, true); } break; } case op_fsqrt_d: { if (fj_double(instr) >= 0) { setFpuRegisterDouble(fd_reg(instr), std::sqrt(fj_double(instr))); } else { setFpuRegisterDouble(fd_reg(instr), std::sqrt(-1)); // qnan setFCSRBit(kFCSRInvalidOpFlagBit, true); } break; } case op_fmov_s: { setFpuRegisterFloat(fd_reg(instr), fj_float(instr)); break; } case op_fmov_d: { setFpuRegisterDouble(fd_reg(instr), fj_double(instr)); break; } case op_movgr2fr_w: { setFpuRegisterWord(fd_reg(instr), static_cast(rj(instr))); break; } case op_movgr2fr_d: { setFpuRegister(fd_reg(instr), rj(instr)); break; } case op_movgr2frh_w: { setFpuRegisterHiWord(fd_reg(instr), static_cast(rj(instr))); break; } case op_movfr2gr_s: { setRegister(rd_reg(instr), static_cast(getFpuRegisterWord(fj_reg(instr)))); break; } case op_movfr2gr_d: { setRegister(rd_reg(instr), getFpuRegister(fj_reg(instr))); break; } case op_movfrh2gr_s: { setRegister(rd_reg(instr), getFpuRegisterHiWord(fj_reg(instr))); break; } case op_movgr2fcsr: { // fcsr could be 0-3 MOZ_ASSERT(rd_reg(instr) < 4); FCSR_ = static_cast(rj(instr)); break; } case op_movfcsr2gr: { setRegister(rd_reg(instr), FCSR_); break; } case op_fcvt_s_d: { setFpuRegisterFloat(fd_reg(instr), static_cast(fj_double(instr))); break; } case op_fcvt_d_s: { setFpuRegisterDouble(fd_reg(instr), static_cast(fj_float(instr))); break; } case op_ftintrm_w_s: { float fj = fj_float(instr); float rounded = std::floor(fj); int32_t result = static_cast(rounded); setFpuRegisterWord(fd_reg(instr), result); if (setFCSRRoundError(fj, rounded)) { setFpuRegisterWordInvalidResult(fj, rounded, fd_reg(instr)); } break; } case op_ftintrm_w_d: { double fj = fj_double(instr); double rounded = std::floor(fj); int32_t result = static_cast(rounded); setFpuRegisterWord(fd_reg(instr), result); if (setFCSRRoundError(fj, rounded)) { setFpuRegisterInvalidResult(fj, rounded, fd_reg(instr)); } break; } case op_ftintrm_l_s: { float fj = fj_float(instr); float rounded = std::floor(fj); int64_t result = static_cast(rounded); setFpuRegister(fd_reg(instr), result); if (setFCSRRoundError(fj, rounded)) { setFpuRegisterInvalidResult64(fj, rounded, fd_reg(instr)); } break; } case op_ftintrm_l_d: { double fj = fj_double(instr); double rounded = std::floor(fj); int64_t result = static_cast(rounded); setFpuRegister(fd_reg(instr), result); if (setFCSRRoundError(fj, rounded)) { setFpuRegisterInvalidResult64(fj, rounded, fd_reg(instr)); } break; } case op_ftintrp_w_s: { float fj = fj_float(instr); float rounded = std::ceil(fj); int32_t result = static_cast(rounded); setFpuRegisterWord(fd_reg(instr), result); if (setFCSRRoundError(fj, rounded)) { setFpuRegisterWordInvalidResult(fj, rounded, fd_reg(instr)); } break; } case op_ftintrp_w_d: { double fj = fj_double(instr); double rounded = std::ceil(fj); int32_t result = static_cast(rounded); setFpuRegisterWord(fd_reg(instr), result); if (setFCSRRoundError(fj, rounded)) { setFpuRegisterInvalidResult(fj, rounded, fd_reg(instr)); } break; } case op_ftintrp_l_s: { float fj = fj_float(instr); float rounded = std::ceil(fj); int64_t result = static_cast(rounded); setFpuRegister(fd_reg(instr), result); if (setFCSRRoundError(fj, rounded)) { setFpuRegisterInvalidResult64(fj, rounded, fd_reg(instr)); } break; } case op_ftintrp_l_d: { double fj = fj_double(instr); double rounded = std::ceil(fj); int64_t result = static_cast(rounded); setFpuRegister(fd_reg(instr), result); if (setFCSRRoundError(fj, rounded)) { setFpuRegisterInvalidResult64(fj, rounded, fd_reg(instr)); } break; } case op_ftintrz_w_s: { float fj = fj_float(instr); float rounded = std::trunc(fj); int32_t result = static_cast(rounded); setFpuRegisterWord(fd_reg(instr), result); if (setFCSRRoundError(fj, rounded)) { setFpuRegisterWordInvalidResult(fj, rounded, fd_reg(instr)); } break; } case op_ftintrz_w_d: { double fj = fj_double(instr); double rounded = std::trunc(fj); int32_t result = static_cast(rounded); setFpuRegisterWord(fd_reg(instr), result); if (setFCSRRoundError(fj, rounded)) { setFpuRegisterInvalidResult(fj, rounded, fd_reg(instr)); } break; } case op_ftintrz_l_s: { float fj = fj_float(instr); float rounded = std::trunc(fj); int64_t result = static_cast(rounded); setFpuRegister(fd_reg(instr), result); if (setFCSRRoundError(fj, rounded)) { setFpuRegisterInvalidResult64(fj, rounded, fd_reg(instr)); } break; } case op_ftintrz_l_d: { double fj = fj_double(instr); double rounded = std::trunc(fj); int64_t result = static_cast(rounded); setFpuRegister(fd_reg(instr), result); if (setFCSRRoundError(fj, rounded)) { setFpuRegisterInvalidResult64(fj, rounded, fd_reg(instr)); } break; } case op_ftintrne_w_s: { float fj = fj_float(instr); float rounded = std::floor(fj + 0.5); int32_t result = static_cast(rounded); if ((result & 1) != 0 && result - fj == 0.5) { // If the number is halfway between two integers, // round to the even one. result--; } setFpuRegisterWord(fd_reg(instr), result); if (setFCSRRoundError(fj, rounded)) { setFpuRegisterWordInvalidResult(fj, rounded, fd_reg(instr)); } break; } case op_ftintrne_w_d: { double fj = fj_double(instr); double rounded = std::floor(fj + 0.5); int32_t result = static_cast(rounded); if ((result & 1) != 0 && result - fj == 0.5) { // If the number is halfway between two integers, // round to the even one. result--; } setFpuRegisterWord(fd_reg(instr), result); if (setFCSRRoundError(fj, rounded)) { setFpuRegisterInvalidResult(fj, rounded, fd_reg(instr)); } break; } case op_ftintrne_l_s: { float fj = fj_float(instr); float rounded = std::floor(fj + 0.5); int64_t result = static_cast(rounded); if ((result & 1) != 0 && result - fj == 0.5) { // If the number is halfway between two integers, // round to the even one. result--; } setFpuRegister(fd_reg(instr), result); if (setFCSRRoundError(fj, rounded)) { setFpuRegisterInvalidResult64(fj, rounded, fd_reg(instr)); } break; } case op_ftintrne_l_d: { double fj = fj_double(instr); double rounded = std::floor(fj + 0.5); int64_t result = static_cast(rounded); if ((result & 1) != 0 && result - fj == 0.5) { // If the number is halfway between two integers, // round to the even one. result--; } setFpuRegister(fd_reg(instr), result); if (setFCSRRoundError(fj, rounded)) { setFpuRegisterInvalidResult64(fj, rounded, fd_reg(instr)); } break; } case op_ftint_w_s: { float fj = fj_float(instr); float rounded; int32_t result; roundAccordingToFCSR(fj, &rounded, &result); setFpuRegisterWord(fd_reg(instr), result); if (setFCSRRoundError(fj, rounded)) { setFpuRegisterWordInvalidResult(fj, rounded, fd_reg(instr)); } break; } case op_ftint_w_d: { double fj = fj_double(instr); double rounded; int32_t result; roundAccordingToFCSR(fj, &rounded, &result); setFpuRegisterWord(fd_reg(instr), result); if (setFCSRRoundError(fj, rounded)) { setFpuRegisterWordInvalidResult(fj, rounded, fd_reg(instr)); } break; } case op_ftint_l_s: { float fj = fj_float(instr); float rounded; int64_t result; round64AccordingToFCSR(fj, &rounded, &result); setFpuRegister(fd_reg(instr), result); if (setFCSRRoundError(fj, rounded)) { setFpuRegisterInvalidResult64(fj, rounded, fd_reg(instr)); } break; } case op_ftint_l_d: { double fj = fj_double(instr); double rounded; int64_t result; round64AccordingToFCSR(fj, &rounded, &result); setFpuRegister(fd_reg(instr), result); if (setFCSRRoundError(fj, rounded)) { setFpuRegisterInvalidResult64(fj, rounded, fd_reg(instr)); } break; } case op_ffint_s_w: { alu_out = getFpuRegisterSignedWord(fj_reg(instr)); setFpuRegisterFloat(fd_reg(instr), static_cast(alu_out)); break; } case op_ffint_s_l: { alu_out = getFpuRegister(fj_reg(instr)); setFpuRegisterFloat(fd_reg(instr), static_cast(alu_out)); break; } case op_ffint_d_w: { alu_out = getFpuRegisterSignedWord(fj_reg(instr)); setFpuRegisterDouble(fd_reg(instr), static_cast(alu_out)); break; } case op_ffint_d_l: { alu_out = getFpuRegister(fj_reg(instr)); setFpuRegisterDouble(fd_reg(instr), static_cast(alu_out)); break; } case op_frint_s: { float fj = fj_float(instr); float result, temp_result; double temp; float upper = std::ceil(fj); float lower = std::floor(fj); switch (getFCSRRoundingMode()) { case kRoundToNearest: if (upper - fj < fj - lower) { result = upper; } else if (upper - fj > fj - lower) { result = lower; } else { temp_result = upper / 2; float reminder = std::modf(temp_result, &temp); if (reminder == 0) { result = upper; } else { result = lower; } } break; case kRoundToZero: result = (fj > 0 ? lower : upper); break; case kRoundToPlusInf: result = upper; break; case kRoundToMinusInf: result = lower; break; } setFpuRegisterFloat(fd_reg(instr), result); if (result != fj) { setFCSRBit(kFCSRInexactFlagBit, true); } break; } case op_frint_d: { double fj = fj_double(instr); double result, temp, temp_result; double upper = std::ceil(fj); double lower = std::floor(fj); switch (getFCSRRoundingMode()) { case kRoundToNearest: if (upper - fj < fj - lower) { result = upper; } else if (upper - fj > fj - lower) { result = lower; } else { temp_result = upper / 2; double reminder = std::modf(temp_result, &temp); if (reminder == 0) { result = upper; } else { result = lower; } } break; case kRoundToZero: result = (fj > 0 ? lower : upper); break; case kRoundToPlusInf: result = upper; break; case kRoundToMinusInf: result = lower; break; } setFpuRegisterDouble(fd_reg(instr), result); if (result != fj) { setFCSRBit(kFCSRInexactFlagBit, true); } break; } case op_movfr2cf: printf("Sim UNIMPLEMENTED: MOVFR2CF\n"); UNIMPLEMENTED(); break; case op_movgr2cf: printf("Sim UNIMPLEMENTED: MOVGR2CF\n"); UNIMPLEMENTED(); break; case op_clo_w: printf("Sim UNIMPLEMENTED: FCO_W\n"); UNIMPLEMENTED(); break; case op_cto_w: printf("Sim UNIMPLEMENTED: FTO_W\n"); UNIMPLEMENTED(); break; case op_clo_d: printf("Sim UNIMPLEMENTED: FLO_D\n"); UNIMPLEMENTED(); break; case op_cto_d: printf("Sim UNIMPLEMENTED: FTO_D\n"); UNIMPLEMENTED(); break; // Unimplemented opcodes raised an error in the configuration step before, // so we can use the default here to set the destination register in common // cases. default: UNREACHABLE(); } } void Simulator::decodeTypeOp24(SimInstruction* instr) { switch (instr->bits(31, 8) << 8) { case op_movcf2fr: UNIMPLEMENTED(); break; case op_movcf2gr: setRegister(rd_reg(instr), getCFRegister(cj_reg(instr))); break; UNIMPLEMENTED(); break; default: UNREACHABLE(); } } // Executes the current instruction. void Simulator::instructionDecode(SimInstruction* instr) { if (!SimulatorProcess::ICacheCheckingDisableCount) { AutoLockSimulatorCache als; SimulatorProcess::checkICacheLocked(instr); } pc_modified_ = false; switch (instr->instructionType()) { case SimInstruction::kOp6Type: decodeTypeOp6(instr); break; case SimInstruction::kOp7Type: decodeTypeOp7(instr); break; case SimInstruction::kOp8Type: decodeTypeOp8(instr); break; case SimInstruction::kOp10Type: decodeTypeOp10(instr); break; case SimInstruction::kOp11Type: decodeTypeOp11(instr); break; case SimInstruction::kOp12Type: decodeTypeOp12(instr); break; case SimInstruction::kOp14Type: decodeTypeOp14(instr); break; case SimInstruction::kOp15Type: decodeTypeOp15(instr); break; case SimInstruction::kOp16Type: decodeTypeOp16(instr); break; case SimInstruction::kOp17Type: decodeTypeOp17(instr); break; case SimInstruction::kOp22Type: decodeTypeOp22(instr); break; case SimInstruction::kOp24Type: decodeTypeOp24(instr); break; default: UNSUPPORTED(); } if (!pc_modified_) { setRegister(pc, reinterpret_cast(instr) + SimInstruction::kInstrSize); } } void Simulator::enable_single_stepping(SingleStepCallback cb, void* arg) { single_stepping_ = true; single_step_callback_ = cb; single_step_callback_arg_ = arg; single_step_callback_(single_step_callback_arg_, this, (void*)get_pc()); } void Simulator::disable_single_stepping() { if (!single_stepping_) { return; } single_step_callback_(single_step_callback_arg_, this, (void*)get_pc()); single_stepping_ = false; single_step_callback_ = nullptr; single_step_callback_arg_ = nullptr; } template void Simulator::execute() { if (single_stepping_) { single_step_callback_(single_step_callback_arg_, this, nullptr); } // Get the PC to simulate. Cannot use the accessor here as we need the // raw PC value and not the one used as input to arithmetic instructions. int64_t program_counter = get_pc(); while (program_counter != end_sim_pc) { if (enableStopSimAt && (icount_ == Simulator::StopSimAt)) { loong64Debugger dbg(this); dbg.debug(); } else { if (single_stepping_) { single_step_callback_(single_step_callback_arg_, this, (void*)program_counter); } SimInstruction* instr = reinterpret_cast(program_counter); instructionDecode(instr); icount_++; } program_counter = get_pc(); } if (single_stepping_) { single_step_callback_(single_step_callback_arg_, this, nullptr); } } void Simulator::callInternal(uint8_t* entry) { // Prepare to execute the code at entry. setRegister(pc, reinterpret_cast(entry)); // Put down marker for end of simulation. The simulator will stop simulation // when the PC reaches this value. By saving the "end simulation" value into // the LR the simulation stops when returning to this call point. setRegister(ra, end_sim_pc); // Remember the values of callee-saved registers. // The code below assumes that r9 is not used as sb (static base) in // simulator code and therefore is regarded as a callee-saved register. int64_t s0_val = getRegister(s0); int64_t s1_val = getRegister(s1); int64_t s2_val = getRegister(s2); int64_t s3_val = getRegister(s3); int64_t s4_val = getRegister(s4); int64_t s5_val = getRegister(s5); int64_t s6_val = getRegister(s6); int64_t s7_val = getRegister(s7); int64_t s8_val = getRegister(s8); int64_t gp_val = getRegister(gp); int64_t sp_val = getRegister(sp); int64_t tp_val = getRegister(tp); int64_t fp_val = getRegister(fp); // Set up the callee-saved registers with a known value. To be able to check // that they are preserved properly across JS execution. int64_t callee_saved_value = icount_; setRegister(s0, callee_saved_value); setRegister(s1, callee_saved_value); setRegister(s2, callee_saved_value); setRegister(s3, callee_saved_value); setRegister(s4, callee_saved_value); setRegister(s5, callee_saved_value); setRegister(s6, callee_saved_value); setRegister(s7, callee_saved_value); setRegister(s8, callee_saved_value); setRegister(gp, callee_saved_value); setRegister(tp, callee_saved_value); setRegister(fp, callee_saved_value); // Start the simulation. if (Simulator::StopSimAt != -1) { execute(); } else { execute(); } // Check that the callee-saved registers have been preserved. MOZ_ASSERT(callee_saved_value == getRegister(s0)); MOZ_ASSERT(callee_saved_value == getRegister(s1)); MOZ_ASSERT(callee_saved_value == getRegister(s2)); MOZ_ASSERT(callee_saved_value == getRegister(s3)); MOZ_ASSERT(callee_saved_value == getRegister(s4)); MOZ_ASSERT(callee_saved_value == getRegister(s5)); MOZ_ASSERT(callee_saved_value == getRegister(s6)); MOZ_ASSERT(callee_saved_value == getRegister(s7)); MOZ_ASSERT(callee_saved_value == getRegister(s8)); MOZ_ASSERT(callee_saved_value == getRegister(gp)); MOZ_ASSERT(callee_saved_value == getRegister(tp)); MOZ_ASSERT(callee_saved_value == getRegister(fp)); // Restore callee-saved registers with the original value. setRegister(s0, s0_val); setRegister(s1, s1_val); setRegister(s2, s2_val); setRegister(s3, s3_val); setRegister(s4, s4_val); setRegister(s5, s5_val); setRegister(s6, s6_val); setRegister(s7, s7_val); setRegister(s8, s8_val); setRegister(gp, gp_val); setRegister(sp, sp_val); setRegister(tp, tp_val); setRegister(fp, fp_val); } int64_t Simulator::call(uint8_t* entry, int argument_count, ...) { va_list parameters; va_start(parameters, argument_count); int64_t original_stack = getRegister(sp); // Compute position of stack on entry to generated code. int64_t entry_stack = original_stack; if (argument_count > kCArgSlotCount) { entry_stack = entry_stack - argument_count * sizeof(int64_t); } else { entry_stack = entry_stack - kCArgsSlotsSize; } entry_stack &= ~U64(ABIStackAlignment - 1); intptr_t* stack_argument = reinterpret_cast(entry_stack); // Setup the arguments. for (int i = 0; i < argument_count; i++) { js::jit::Register argReg; if (GetIntArgReg(i, &argReg)) { setRegister(argReg.code(), va_arg(parameters, int64_t)); } else { stack_argument[i] = va_arg(parameters, int64_t); } } va_end(parameters); setRegister(sp, entry_stack); callInternal(entry); // Pop stack passed arguments. MOZ_ASSERT(entry_stack == getRegister(sp)); setRegister(sp, original_stack); int64_t result = getRegister(a0); return result; } uintptr_t Simulator::pushAddress(uintptr_t address) { int new_sp = getRegister(sp) - sizeof(uintptr_t); uintptr_t* stack_slot = reinterpret_cast(new_sp); *stack_slot = address; setRegister(sp, new_sp); return new_sp; } uintptr_t Simulator::popAddress() { int current_sp = getRegister(sp); uintptr_t* stack_slot = reinterpret_cast(current_sp); uintptr_t address = *stack_slot; setRegister(sp, current_sp + sizeof(uintptr_t)); return address; } } // namespace jit } // namespace js js::jit::Simulator* JSContext::simulator() const { return simulator_; }