summaryrefslogtreecommitdiffstats
path: root/js/src/jit/loong64/Simulator-loong64.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jit/loong64/Simulator-loong64.cpp')
-rw-r--r--js/src/jit/loong64/Simulator-loong64.cpp5238
1 files changed, 5238 insertions, 0 deletions
diff --git a/js/src/jit/loong64/Simulator-loong64.cpp b/js/src/jit/loong64/Simulator-loong64.cpp
new file mode 100644
index 0000000000..21193976c3
--- /dev/null
+++ b/js/src/jit/loong64/Simulator-loong64.cpp
@@ -0,0 +1,5238 @@
+/* -*- 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 <float.h>
+#include <limits>
+
+#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<int8_t>(v)
+#define I16(v) static_cast<int16_t>(v)
+#define U16(v) static_cast<uint16_t>(v)
+#define I32(v) static_cast<int32_t>(v)
+#define U32(v) static_cast<uint32_t>(v)
+#define I64(v) static_cast<int64_t>(v)
+#define U64(v) static_cast<uint64_t>(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<const Instr*>(this);
+ }
+
+ // Set the raw instruction bits to value.
+ inline void setInstructionBits(Instr value) {
+ *reinterpret_cast<Instr*>(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<Mutex> {
+ using Base = LockGuard<Mutex>;
+
+ public:
+ explicit AutoLockSimulatorCache()
+ : Base(SimulatorProcess::singleton_->cacheLock_) {}
+};
+
+mozilla::Atomic<size_t, mozilla::ReleaseAcquire>
+ SimulatorProcess::ICacheCheckingDisableCount(
+ 1); // Checking is disabled by default.
+SimulatorProcess* SimulatorProcess::singleton_ = nullptr;
+
+int64_t Simulator::StopSimAt = -1;
+
+Simulator* Simulator::Create() {
+ auto sim = MakeUnique<Simulator>();
+ 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<char**>(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<uint64_t*>(value)) == 1;
+ }
+ return sscanf(desc, "%lu", reinterpret_cast<uint64_t*>(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<char>(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<char>(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<SimInstruction*>(sim_->get_pc());
+ if (!instr->isTrap()) {
+ sim_->instructionDecode(
+ reinterpret_cast<SimInstruction*>(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<SimInstruction*>(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 <register> or print <fpu register> 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<int64_t*>(sim_->getRegister(Simulator::sp));
+ } else { // Command "mem".
+ int64_t value;
+ if (!getValue(arg1, &value)) {
+ printf("%s unrecognized\n", arg1);
+ continue;
+ }
+ cur = reinterpret_cast<int64_t*>(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<uint8_t*>(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<uint8_t*>(value);
+ // Disassemble 10 instructions at <arg1>.
+ end = cur + (10 * SimInstruction::kInstrSize);
+ }
+ } else {
+ // The argument is the number of instructions.
+ int64_t value;
+ if (getValue(arg1, &value)) {
+ cur = reinterpret_cast<uint8_t*>(sim_->get_pc());
+ // Disassemble <arg1> instructions.
+ end = cur + (value * SimInstruction::kInstrSize);
+ }
+ }
+ } else {
+ int64_t value1;
+ int64_t value2;
+ if (getValue(arg1, &value1) && getValue(arg2, &value2)) {
+ cur = reinterpret_cast<uint8_t*>(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<SimInstruction*>(value))) {
+ printf("setting breakpoint failed\n");
+ }
+ } else {
+ printf("%s unrecognized\n", arg1);
+ }
+ } else {
+ printf("break <address>\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<SimInstruction*>(stop_pc);
+ SimInstruction* msg_address = reinterpret_cast<SimInstruction*>(
+ 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 <register>\n");
+ printf(" print register content (alias 'p')\n");
+ printf(" use register name 'all' to print all registers\n");
+ printf("printobject <register>\n");
+ printf(" print an object from a register (alias 'po')\n");
+ printf("stack [<words>]\n");
+ printf(" dump stack content, default dump 10 words)\n");
+ printf("mem <address> [<words>]\n");
+ printf(" dump memory content, default dump 10 words)\n");
+ printf("flags\n");
+ printf(" print flags\n");
+ printf("disasm [<instructions>]\n");
+ printf("disasm [<address/register>]\n");
+ printf("disasm [[<address/register>] <instructions>]\n");
+ printf(" disassemble code, default is 10 instructions\n");
+ printf(" from pc (alias 'di')\n");
+ printf("gdb\n");
+ printf(" enter gdb\n");
+ printf("break <address>\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/<code> : print infos about number <code>\n");
+ printf(" or all stop(s).\n");
+ printf(" stop enable/disable all/<code> : enables / disables\n");
+ printf(" all or number <code> 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<CachePage>();
+ 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<void*>(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<intptr_t>(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<intptr_t>(instr);
+ void* page = reinterpret_cast<void*>(address & (~CachePage::kPageMask));
+ void* line = reinterpret_cast<void*>(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<int> cmpret =
+ memcmp(reinterpret_cast<void*>(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<uintptr_t>(l)) >> 2;
+}
+
+bool SimulatorProcess::ICacheHasher::match(const Key& k, const Lookup& l) {
+ MOZ_ASSERT((reinterpret_cast<intptr_t>(k) & CachePage::kPageMask) == 0);
+ MOZ_ASSERT((reinterpret_cast<intptr_t>(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<char>(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<uintptr_t>(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<int64_t>(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<Redirection>(1);
+ if (!redir) {
+ oomUnsafe.crash("Simulator redirection");
+ }
+ new (redir) Redirection(nativeFunction, type);
+ return redir;
+ }
+
+ static Redirection* FromSwiInstruction(SimInstruction* swiInstruction) {
+ uint8_t* addrOfSwi = reinterpret_cast<uint8_t*>(swiInstruction);
+ uint8_t* addrOfRedirection =
+ addrOfSwi - offsetof(Redirection, swiInstruction_);
+ return reinterpret_cast<Redirection*>(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<int32_t*>(&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<int32_t*>(&FPUregisters_[fpureg]);
+
+ *pword = value;
+}
+
+void Simulator::setFpuRegisterWordInvalidResult(float original, float rounded,
+ int fpureg) {
+ double max_int32 = static_cast<double>(INT32_MAX);
+ double min_int32 = static_cast<double>(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<double>(INT32_MAX);
+ double min_int32 = static_cast<double>(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<double>(INT32_MAX);
+ double min_int32 = static_cast<double>(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<double>(INT32_MAX);
+ double min_int32 = static_cast<double>(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<double>(INT64_MAX);
+ double min_int64 = static_cast<double>(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<double>(INT64_MAX);
+ double min_int64 = static_cast<double>(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<float*>(&FPUregisters_[fpureg]) = value;
+}
+
+void Simulator::setFpuRegisterDouble(int fpureg, double value) {
+ MOZ_ASSERT((fpureg >= 0) &&
+ (fpureg < Simulator::FPURegister::kNumFPURegisters));
+ *mozilla::BitwiseCast<double*>(&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<int32_t*>(&FPUregisters_[fpureg]);
+}
+
+int32_t Simulator::getFpuRegisterSignedWord(int fpureg) const {
+ MOZ_ASSERT((fpureg >= 0) &&
+ (fpureg < Simulator::FPURegister::kNumFPURegisters));
+ return *mozilla::BitwiseCast<int32_t*>(&FPUregisters_[fpureg]);
+}
+
+int32_t Simulator::getFpuRegisterHiWord(int fpureg) const {
+ MOZ_ASSERT((fpureg >= 0) &&
+ (fpureg < Simulator::FPURegister::kNumFPURegisters));
+ return *((mozilla::BitwiseCast<int32_t*>(&FPUregisters_[fpureg])) + 1);
+}
+
+float Simulator::getFpuRegisterFloat(int fpureg) const {
+ MOZ_ASSERT((fpureg >= 0) &&
+ (fpureg < Simulator::FPURegister::kNumFPURegisters));
+ return *mozilla::BitwiseCast<float*>(&FPUregisters_[fpureg]);
+}
+
+double Simulator::getFpuRegisterDouble(int fpureg) const {
+ MOZ_ASSERT((fpureg >= 0) &&
+ (fpureg < Simulator::FPURegister::kNumFPURegisters));
+ return *mozilla::BitwiseCast<double*>(&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 <typename T>
+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<T>::max() ||
+ (long double)rounded < (long double)std::numeric_limits<T>::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 <typename T>
+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<int32_t>(*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<int32_t>(*rounded);
+ break;
+ case kRoundToPlusInf:
+ *rounded = std::ceil(toRound);
+ *rounded_int = static_cast<int32_t>(*rounded);
+ break;
+ case kRoundToMinusInf:
+ *rounded = std::floor(toRound);
+ *rounded_int = static_cast<int32_t>(*rounded);
+ break;
+ }
+}
+
+template <typename T>
+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<int64_t>(*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<int64_t>(*rounded);
+ break;
+ case kRoundToPlusInf:
+ *rounded = std::ceil(toRound);
+ *rounded_int = static_cast<int64_t>(*rounded);
+ break;
+ case kRoundToMinusInf:
+ *rounded = std::floor(toRound);
+ *rounded_int = static_cast<int64_t>(*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<uint8_t*>(addr);
+ return *ptr;
+}
+
+int8_t Simulator::readB(uint64_t addr) {
+ if (handleWasmSegFault(addr, 1)) {
+ return -1;
+ }
+
+ int8_t* ptr = reinterpret_cast<int8_t*>(addr);
+ return *ptr;
+}
+
+void Simulator::writeB(uint64_t addr, uint8_t value) {
+ if (handleWasmSegFault(addr, 1)) {
+ return;
+ }
+
+ uint8_t* ptr = reinterpret_cast<uint8_t*>(addr);
+ *ptr = value;
+}
+
+void Simulator::writeB(uint64_t addr, int8_t value) {
+ if (handleWasmSegFault(addr, 1)) {
+ return;
+ }
+
+ int8_t* ptr = reinterpret_cast<int8_t*>(addr);
+ *ptr = value;
+}
+
+uint16_t Simulator::readHU(uint64_t addr, SimInstruction* instr) {
+ if (handleWasmSegFault(addr, 2)) {
+ return 0xffff;
+ }
+
+ uint16_t* ptr = reinterpret_cast<uint16_t*>(addr);
+ return *ptr;
+}
+
+int16_t Simulator::readH(uint64_t addr, SimInstruction* instr) {
+ if (handleWasmSegFault(addr, 2)) {
+ return -1;
+ }
+
+ int16_t* ptr = reinterpret_cast<int16_t*>(addr);
+ return *ptr;
+}
+
+void Simulator::writeH(uint64_t addr, uint16_t value, SimInstruction* instr) {
+ if (handleWasmSegFault(addr, 2)) {
+ return;
+ }
+
+ uint16_t* ptr = reinterpret_cast<uint16_t*>(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<int16_t*>(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<uint32_t*>(addr);
+ return *ptr;
+}
+
+int32_t Simulator::readW(uint64_t addr, SimInstruction* instr) {
+ if (handleWasmSegFault(addr, 4)) {
+ return -1;
+ }
+
+ int32_t* ptr = reinterpret_cast<int32_t*>(addr);
+ return *ptr;
+}
+
+void Simulator::writeW(uint64_t addr, uint32_t value, SimInstruction* instr) {
+ if (handleWasmSegFault(addr, 4)) {
+ return;
+ }
+
+ uint32_t* ptr = reinterpret_cast<uint32_t*>(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<int32_t*>(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<intptr_t*>(addr);
+ return *ptr;
+}
+
+void Simulator::writeDW(uint64_t addr, int64_t value, SimInstruction* instr) {
+ if (handleWasmSegFault(addr, 8)) {
+ return;
+ }
+
+ int64_t* ptr = reinterpret_cast<int64_t*>(addr);
+ LLBit_ = false;
+ *ptr = value;
+ return;
+}
+
+double Simulator::readD(uint64_t addr, SimInstruction* instr) {
+ if (handleWasmSegFault(addr, 8)) {
+ return NAN;
+ }
+
+ double* ptr = reinterpret_cast<double*>(addr);
+ return *ptr;
+}
+
+void Simulator::writeD(uint64_t addr, double value, SimInstruction* instr) {
+ if (handleWasmSegFault(addr, 8)) {
+ return;
+ }
+
+ double* ptr = reinterpret_cast<double*>(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<volatile int32_t*>(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<intptr_t>(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<intptr_t>(instr), LLAddr_);
+ MOZ_CRASH();
+ }
+
+ if ((addr & 3) == 0) {
+ SharedMem<int32_t*> ptr =
+ SharedMem<int32_t*>::shared(reinterpret_cast<int32_t*>(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<intptr_t>(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<volatile int64_t*>(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<intptr_t>(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<intptr_t>(instr), LLAddr_);
+ MOZ_CRASH();
+ }
+
+ if ((addr & kPointerAlignmentMask) == 0) {
+ SharedMem<int64_t*> ptr =
+ SharedMem<int64_t*>::shared(reinterpret_cast<int64_t*>(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<intptr_t>(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<intptr_t>(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<uint64_t>(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<uint64_t>(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<uint64_t>(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<int32_t> 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<uintptr_t>(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<intptr_t>(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<Prototype_General0>(external);
+ int64_t result = target();
+ setCallResult(result);
+ break;
+ }
+ case Args_General1: {
+ Prototype_General1 target =
+ reinterpret_cast<Prototype_General1>(external);
+ int64_t result = target(arg0);
+ setCallResult(result);
+ break;
+ }
+ case Args_General2: {
+ Prototype_General2 target =
+ reinterpret_cast<Prototype_General2>(external);
+ int64_t result = target(arg0, arg1);
+ setCallResult(result);
+ break;
+ }
+ case Args_General3: {
+ Prototype_General3 target =
+ reinterpret_cast<Prototype_General3>(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<Prototype_General4>(external);
+ int64_t result = target(arg0, arg1, arg2, arg3);
+ setCallResult(result);
+ break;
+ }
+ case Args_General5: {
+ Prototype_General5 target =
+ reinterpret_cast<Prototype_General5>(external);
+ int64_t result = target(arg0, arg1, arg2, arg3, arg4);
+ setCallResult(result);
+ break;
+ }
+ case Args_General6: {
+ Prototype_General6 target =
+ reinterpret_cast<Prototype_General6>(external);
+ int64_t result = target(arg0, arg1, arg2, arg3, arg4, arg5);
+ setCallResult(result);
+ break;
+ }
+ case Args_General7: {
+ Prototype_General7 target =
+ reinterpret_cast<Prototype_General7>(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<Prototype_General8>(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<Prototype_Double_None>(external);
+ double dresult = target();
+ setCallResultDouble(dresult);
+ break;
+ }
+ case Args_Int_Float32: {
+ float fval0;
+ fval0 = getFpuRegisterFloat(0);
+ Prototype_Int_Float32 target =
+ reinterpret_cast<Prototype_Int_Float32>(external);
+ int64_t result = target(fval0);
+ setRegister(a0, result);
+ break;
+ }
+ case Args_Int_Double: {
+ double dval0 = getFpuRegisterDouble(0);
+ Prototype_Int_Double target =
+ reinterpret_cast<Prototype_Int_Double>(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<Prototype_GeneralGeneralGeneralInt64>(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<Prototype_GeneralGeneralInt64Int64>(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<Prototype_Int_DoubleInt>(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<Prototype_Int_DoubleIntInt>(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<Prototype_Int_IntDoubleIntInt>(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<Prototype_Double_Double>(external);
+ double dresult = target(dval0);
+ setCallResultDouble(dresult);
+ break;
+ }
+ case Args_Float32_Float32: {
+ float fval0;
+ fval0 = getFpuRegisterFloat(0);
+ Prototype_Float32_Float32 target =
+ reinterpret_cast<Prototype_Float32_Float32>(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<Prototype_Float32_Float32Float32>(external);
+ float fresult = target(fval0, fval1);
+ setCallResultFloat(fresult);
+ break;
+ }
+ case Args_Double_Int: {
+ Prototype_Double_Int target =
+ reinterpret_cast<Prototype_Double_Int>(external);
+ double dresult = target(arg0);
+ setCallResultDouble(dresult);
+ break;
+ }
+ case Args_Double_DoubleInt: {
+ double dval0 = getFpuRegisterDouble(0);
+ Prototype_Double_DoubleInt target =
+ reinterpret_cast<Prototype_Double_DoubleInt>(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<Prototype_Double_DoubleDouble>(external);
+ double dresult = target(dval0, dval1);
+ setCallResultDouble(dresult);
+ break;
+ }
+ case Args_Double_IntDouble: {
+ double dval0 = getFpuRegisterDouble(0);
+ Prototype_Double_IntDouble target =
+ reinterpret_cast<Prototype_Double_IntDouble>(external);
+ double dresult = target(arg0, dval0);
+ setCallResultDouble(dresult);
+ break;
+ }
+ case Args_Int_IntDouble: {
+ double dval0 = getFpuRegisterDouble(0);
+ Prototype_Int_IntDouble target =
+ reinterpret_cast<Prototype_Int_IntDouble>(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<Prototype_Double_DoubleDoubleDouble>(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<Prototype_Double_DoubleDoubleDoubleDouble>(
+ external);
+ double dresult = target(dval0, dval1, dval2, dval3);
+ setCallResultDouble(dresult);
+ break;
+ }
+ case Args_Int32_General: {
+ int32_t ret = reinterpret_cast<Prototype_Int32_General>(nativeFn)(arg0);
+ setRegister(a0, I64(ret));
+ break;
+ }
+ case Args_Int32_GeneralInt32: {
+ int32_t ret = reinterpret_cast<Prototype_Int32_GeneralInt32>(nativeFn)(
+ arg0, I32(arg1));
+ setRegister(a0, I64(ret));
+ break;
+ }
+ case Args_Int32_GeneralInt32Int32: {
+ int32_t ret = reinterpret_cast<Prototype_Int32_GeneralInt32Int32>(
+ nativeFn)(arg0, I32(arg1), I32(arg2));
+ setRegister(a0, I64(ret));
+ break;
+ }
+ case Args_Int32_GeneralInt32Int32Int32Int32: {
+ int32_t ret =
+ reinterpret_cast<Prototype_Int32_GeneralInt32Int32Int32Int32>(
+ nativeFn)(arg0, I32(arg1), I32(arg2), I32(arg3), I32(arg4));
+ setRegister(a0, I64(ret));
+ break;
+ }
+ case Args_Int32_GeneralInt32Int32Int32Int32Int32: {
+ int32_t ret =
+ reinterpret_cast<Prototype_Int32_GeneralInt32Int32Int32Int32Int32>(
+ 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<Prototype_Int32_GeneralInt32Int32Int32General>(
+ nativeFn)(arg0, I32(arg1), I32(arg2), I32(arg3), arg4);
+ setRegister(a0, I64(ret));
+ break;
+ }
+ case Args_Int32_GeneralInt32Int32Int64: {
+ int32_t ret = reinterpret_cast<Prototype_Int32_GeneralInt32Int32Int64>(
+ nativeFn)(arg0, I32(arg1), I32(arg2), arg3);
+ setRegister(a0, I64(ret));
+ break;
+ }
+ case Args_Int32_GeneralInt32Int32General: {
+ int32_t ret =
+ reinterpret_cast<Prototype_Int32_GeneralInt32Int32General>(
+ nativeFn)(arg0, I32(arg1), I32(arg2), arg3);
+ setRegister(a0, I64(ret));
+ break;
+ }
+ case Args_Int32_GeneralInt32Int64Int64: {
+ int32_t ret = reinterpret_cast<Prototype_Int32_GeneralInt32Int64Int64>(
+ nativeFn)(arg0, I32(arg1), arg2, arg3);
+ setRegister(a0, I64(ret));
+ break;
+ }
+ case Args_Int32_GeneralInt32GeneralInt32: {
+ int32_t ret =
+ reinterpret_cast<Prototype_Int32_GeneralInt32GeneralInt32>(
+ nativeFn)(arg0, I32(arg1), arg2, I32(arg3));
+ setRegister(a0, I64(ret));
+ break;
+ }
+ case Args_Int32_GeneralInt32GeneralInt32Int32: {
+ int32_t ret =
+ reinterpret_cast<Prototype_Int32_GeneralInt32GeneralInt32Int32>(
+ nativeFn)(arg0, I32(arg1), arg2, I32(arg3), I32(arg4));
+ setRegister(a0, I64(ret));
+ break;
+ }
+ case Args_Int32_GeneralGeneral: {
+ int32_t ret = reinterpret_cast<Prototype_Int32_GeneralGeneral>(
+ nativeFn)(arg0, arg1);
+ setRegister(a0, I64(ret));
+ break;
+ }
+ case Args_Int32_GeneralGeneralGeneral: {
+ int32_t ret = reinterpret_cast<Prototype_Int32_GeneralGeneralGeneral>(
+ nativeFn)(arg0, arg1, arg2);
+ setRegister(a0, I64(ret));
+ break;
+ }
+ case Args_Int32_GeneralGeneralInt32Int32: {
+ int32_t ret =
+ reinterpret_cast<Prototype_Int32_GeneralGeneralInt32Int32>(
+ nativeFn)(arg0, arg1, I32(arg2), I32(arg3));
+ setRegister(a0, I64(ret));
+ break;
+ }
+ case js::jit::Args_Int32_GeneralInt64Int32Int32Int32: {
+ int32_t ret =
+ reinterpret_cast<Prototype_Int32_GeneralInt64Int32Int32Int32>(
+ 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<Prototype_Int32_GeneralInt64Int32>(
+ nativeFn)(arg0, arg1, I32(arg2));
+ setRegister(a0, I64(ret));
+ break;
+ }
+ case js::jit::Args_Int32_GeneralInt64Int32Int64: {
+ int32_t ret = reinterpret_cast<Prototype_Int32_GeneralInt64Int32Int64>(
+ nativeFn)(arg0, arg1, I32(arg2), arg3);
+ setRegister(a0, I64(ret));
+ break;
+ }
+ case js::jit::Args_Int32_GeneralInt64Int32Int64General: {
+ int32_t ret =
+ reinterpret_cast<Prototype_Int32_GeneralInt64Int32Int64General>(
+ nativeFn)(arg0, arg1, I32(arg2), arg3, arg4);
+ setRegister(a0, I64(ret));
+ break;
+ }
+ case js::jit::Args_Int32_GeneralInt64Int64Int64: {
+ int32_t ret = reinterpret_cast<Prototype_Int32_GeneralInt64Int64Int64>(
+ nativeFn)(arg0, arg1, arg2, arg3);
+ setRegister(a0, I64(ret));
+ break;
+ }
+ case js::jit::Args_Int32_GeneralInt64Int64General: {
+ int32_t ret =
+ reinterpret_cast<Prototype_Int32_GeneralInt64Int64General>(
+ nativeFn)(arg0, arg1, arg2, arg3);
+ setRegister(a0, I64(ret));
+ break;
+ }
+ case js::jit::Args_Int32_GeneralInt64Int64Int64General: {
+ int32_t ret =
+ reinterpret_cast<Prototype_Int32_GeneralInt64Int64Int64General>(
+ nativeFn)(arg0, arg1, arg2, arg3, arg4);
+ setRegister(a0, I64(ret));
+ break;
+ }
+ case Args_General_GeneralInt32: {
+ int64_t ret = reinterpret_cast<Prototype_General_GeneralInt32>(
+ nativeFn)(arg0, I32(arg1));
+ setRegister(a0, ret);
+ break;
+ }
+ case Args_General_GeneralInt32Int32: {
+ int64_t ret = reinterpret_cast<Prototype_General_GeneralInt32Int32>(
+ nativeFn)(arg0, I32(arg1), I32(arg2));
+ setRegister(a0, ret);
+ break;
+ }
+ case Args_General_GeneralInt32General: {
+ int64_t ret = reinterpret_cast<Prototype_General_GeneralInt32General>(
+ nativeFn)(arg0, I32(arg1), arg2);
+ setRegister(a0, ret);
+ break;
+ }
+ case js::jit::Args_General_GeneralInt32Int32GeneralInt32: {
+ int64_t ret =
+ reinterpret_cast<Prototype_General_GeneralInt32Int32GeneralInt32>(
+ 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<Prototype_Int32_GeneralGeneralInt32General>(
+ nativeFn)(arg0, arg1, I32(arg2), arg3);
+ setRegister(a0, I64(ret));
+ break;
+ }
+ case js::jit::Args_Int64_General: {
+ int64_t ret = reinterpret_cast<Prototype_Int64_General>(nativeFn)(arg0);
+ setRegister(a0, ret);
+ break;
+ }
+ case js::jit::Args_Int64_GeneralInt64: {
+ int64_t ret = reinterpret_cast<Prototype_Int64_GeneralInt64>(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<uint32_t>(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 <typename T>
+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 <typename T>
+static T FPAbs(T a);
+
+template <>
+double FPAbs<double>(double a) {
+ return fabs(a);
+}
+
+template <>
+float FPAbs<float>(float a) {
+ return fabsf(a);
+}
+
+enum class MaxMinKind : int { kMin = 0, kMax = 1 };
+
+template <typename T>
+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<int>(kind) ? b : a;
+ } else {
+ return false;
+ }
+ return true;
+}
+
+template <typename T>
+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 <typename T>
+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 <typename T>
+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 <typename T>
+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<uint32_t>(instr->bits(25, 10) << 16) >> 16;
+ int32_t offs26_high10 = static_cast<int32_t>(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<int32_t>(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<uint32_t>(instr->bits(25, 10) << 16) >> 16;
+ int32_t offs21_high5 = static_cast<int32_t>(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<uint32_t>(instr->bits(25, 10) << 16) >> 16;
+ int32_t offs26_high10 = static_cast<int32_t>(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<int32_t>(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<int32_t>(si16(instr)) << 16;
+ alu_out = static_cast<int64_t>(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<int32_t>(si20(instr) << 12);
+ setRegister(rd_reg(instr), static_cast<int64_t>(si20_upper));
+ break;
+ }
+ case op_lu32i_d: {
+ int32_t si20_signExtend = static_cast<int32_t>(si20(instr) << 12) >> 12;
+ int64_t lower_32bit_mask = 0xFFFFFFFF;
+ alu_out = (static_cast<int64_t>(si20_signExtend) << 32) |
+ (rd(instr) & lower_32bit_mask);
+ setRegister(rd_reg(instr), alu_out);
+ break;
+ }
+ case op_pcaddi: {
+ int32_t si20_signExtend = static_cast<int32_t>(si20(instr) << 12) >> 10;
+ int64_t current_pc = get_pc();
+ alu_out = static_cast<int64_t>(si20_signExtend) + current_pc;
+ setRegister(rd_reg(instr), alu_out);
+ break;
+ }
+ case op_pcalau12i: {
+ int32_t si20_signExtend = static_cast<int32_t>(si20(instr) << 12);
+ int64_t current_pc = get_pc();
+ int64_t clear_lower12bit_mask = 0xFFFFFFFFFFFFF000;
+ alu_out = static_cast<int64_t>(si20_signExtend) + current_pc;
+ setRegister(rd_reg(instr), alu_out & clear_lower12bit_mask);
+ break;
+ }
+ case op_pcaddu12i: {
+ int32_t si20_signExtend = static_cast<int32_t>(si20(instr) << 12);
+ int64_t current_pc = get_pc();
+ alu_out = static_cast<int64_t>(si20_signExtend) + current_pc;
+ setRegister(rd_reg(instr), alu_out);
+ break;
+ }
+ case op_pcaddu18i: {
+ int64_t si20_signExtend = (static_cast<int64_t>(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<int64_t>(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<int32_t>(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<int32_t>(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<int64_t>(si12(instr)) << 52) >> 52;
+ uint64_t si12_ze = (static_cast<uint64_t>(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<uint64_t>(si12_se) ? 1 : 0);
+ break;
+ }
+ case op_addi_w: {
+ int32_t alu32_out =
+ static_cast<int32_t>(rj(instr)) + static_cast<int32_t>(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<int64_t>(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<int8_t>(rd(instr)));
+ break;
+ }
+ case op_st_h: {
+ writeH(rj(instr) + si12_se, static_cast<int16_t>(rd(instr)), instr);
+ break;
+ }
+ case op_st_w: {
+ writeW(rj(instr) + si12_se, static_cast<int32_t>(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<int32_t>(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<int32_t>((rd_u(instr) & ~(mask << lsbw_)) |
+ ((rj_u(instr) & mask) << lsbw_));
+ } else {
+ // BSTRPICK_W
+ alu_out =
+ static_cast<int32_t>((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<int32_t>(rk(instr));
+ } else {
+ int32_t mask = (1 << 31) >> (sa - 1);
+ int32_t rk_hi = (static_cast<int32_t>(rk(instr)) & (~mask)) << sa;
+ int32_t rj_lo = (static_cast<uint32_t>(rj(instr)) & mask) >> (32 - sa);
+ alu32_out = rk_hi | rj_lo;
+ }
+ setRegister(rd_reg(instr), static_cast<int64_t>(alu32_out));
+ break;
+ }
+ case op_alsl_w: {
+ uint8_t sa = sa2(instr) + 1;
+ alu32_out = (static_cast<int32_t>(rj(instr)) << sa) +
+ static_cast<int32_t>(rk(instr));
+ setRegister(rd_reg(instr), alu32_out);
+ break;
+ }
+ case op_alsl_wu: {
+ uint8_t sa = sa2(instr) + 1;
+ alu32_out = (static_cast<int32_t>(rj(instr)) << sa) +
+ static_cast<int32_t>(rk(instr));
+ setRegister(rd_reg(instr), static_cast<uint32_t>(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<int64_t>(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<int32_t>(rj(instr)) << ui5(instr);
+ setRegister(rd_reg(instr), static_cast<int64_t>(alu32_out));
+ break;
+ }
+ case op_srai_w: {
+ MOZ_ASSERT(instr->bit(17) == 0);
+ MOZ_ASSERT(instr->bits(17, 15) == 0b001);
+ alu32_out = static_cast<int32_t>(rj(instr)) >> ui5(instr);
+ setRegister(rd_reg(instr), static_cast<int64_t>(alu32_out));
+ break;
+ }
+ case op_rotri_w: {
+ MOZ_ASSERT(instr->bit(17) == 0);
+ MOZ_ASSERT(instr->bits(17, 15) == 0b001);
+ alu32_out = static_cast<int32_t>(
+ RotateRight32(static_cast<const uint32_t>(rj_u(instr)),
+ static_cast<const uint32_t>(ui5(instr))));
+ setRegister(rd_reg(instr), static_cast<int64_t>(alu32_out));
+ break;
+ }
+ case op_srli_w: {
+ MOZ_ASSERT(instr->bit(17) == 0);
+ MOZ_ASSERT(instr->bits(17, 15) == 0b001);
+ alu32_out = static_cast<uint32_t>(rj(instr)) >> ui5(instr);
+ setRegister(rd_reg(instr), static_cast<int64_t>(alu32_out));
+ break;
+ }
+ case op_add_w: {
+ int32_t alu32_out = static_cast<int32_t>(rj(instr) + rk(instr));
+ // Sign-extend result of 32bit operation into 64bit register.
+ setRegister(rd_reg(instr), static_cast<int64_t>(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<int32_t>(rj(instr) - rk(instr));
+ // Sign-extend result of 32bit operation into 64bit register.
+ setRegister(rd_reg(instr), static_cast<int64_t>(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<int32_t>((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<int64_t>(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<int32_t>(
+ RotateRight32(static_cast<const uint32_t>(rj_u(instr)),
+ static_cast<const uint32_t>(rk_u(instr) % 32)));
+ setRegister(rd_reg(instr), alu_out);
+ break;
+ }
+ case op_rotr_d: {
+ alu_out = static_cast<int64_t>(
+ RotateRight64((rj_u(instr)), (rk_u(instr) % 64)));
+ setRegister(rd_reg(instr), alu_out);
+ break;
+ }
+ case op_mul_w: {
+ alu_out =
+ static_cast<int32_t>(rj(instr)) * static_cast<int32_t>(rk(instr));
+ setRegister(rd_reg(instr), alu_out);
+ break;
+ }
+ case op_mulh_w: {
+ int32_t rj_lo = static_cast<int32_t>(rj(instr));
+ int32_t rk_lo = static_cast<int32_t>(rk(instr));
+ alu_out = static_cast<int64_t>(rj_lo) * static_cast<int64_t>(rk_lo);
+ setRegister(rd_reg(instr), alu_out >> 32);
+ break;
+ }
+ case op_mulh_wu: {
+ uint32_t rj_lo = static_cast<uint32_t>(rj_u(instr));
+ uint32_t rk_lo = static_cast<uint32_t>(rk_u(instr));
+ alu_out = static_cast<uint64_t>(rj_lo) * static_cast<uint64_t>(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<int32_t>(rj(instr));
+ int64_t rk_i32 = static_cast<int32_t>(rk(instr));
+ setRegister(rd_reg(instr), rj_i32 * rk_i32);
+ break;
+ }
+ case op_mulw_d_wu: {
+ uint64_t rj_u32 = static_cast<uint32_t>(rj_u(instr));
+ uint64_t rk_u32 = static_cast<uint32_t>(rk_u(instr));
+ setRegister(rd_reg(instr), rj_u32 * rk_u32);
+ break;
+ }
+ case op_div_w: {
+ int32_t rj_i32 = static_cast<int32_t>(rj(instr));
+ int32_t rk_i32 = static_cast<int32_t>(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<int32_t>(rj(instr));
+ int32_t rk_i32 = static_cast<int32_t>(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<uint32_t>(rj(instr));
+ uint32_t rk_u32 = static_cast<uint32_t>(rk(instr));
+ if (rk_u32 != 0) {
+ setRegister(rd_reg(instr), static_cast<int32_t>(rj_u32 / rk_u32));
+ }
+ break;
+ }
+ case op_mod_wu: {
+ uint32_t rj_u32 = static_cast<uint32_t>(rj(instr));
+ uint32_t rk_u32 = static_cast<uint32_t>(rk(instr));
+ if (rk_u32 != 0) {
+ setRegister(rd_reg(instr), static_cast<int32_t>(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<int64_t>(rj_u(instr) / rk_u(instr)));
+ }
+ break;
+ }
+ case op_mod_du: {
+ if (rk_u(instr) != 0) {
+ setRegister(rd_reg(instr),
+ static_cast<int64_t>(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<int8_t>(rd(instr)));
+ break;
+ case op_stx_h:
+ writeH(rj(instr) + rk(instr), static_cast<int16_t>(rd(instr)), instr);
+ break;
+ case op_stx_w:
+ writeW(rj(instr) + rk(instr), static_cast<int32_t>(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<int32_t>(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<uint32_t>(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<int64_t>(static_cast<int32_t>(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<int64_t>(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<int64_t>(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<int64_t>(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<int64_t>(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<int64_t>(output);
+ setRegister(rd_reg(instr), alu_out);
+ break;
+ }
+ case op_bitrev_4b: {
+ uint32_t input = static_cast<uint32_t>(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<uint8_t>(((i_byte * 0x0802LU & 0x22110LU) |
+ (i_byte * 0x8020LU & 0x88440LU)) *
+ 0x10101LU >>
+ 16);
+
+ output = output | (static_cast<uint32_t>(o_byte << 24));
+ input = input >> 8;
+ }
+
+ alu_out = static_cast<int64_t>(static_cast<int32_t>(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<uint8_t>(((i_byte * 0x0802LU & 0x22110LU) |
+ (i_byte * 0x8020LU & 0x88440LU)) *
+ 0x10101LU >>
+ 16);
+
+ output = output | (static_cast<uint64_t>(o_byte) << 56);
+ input = input >> 8;
+ }
+
+ alu_out = static_cast<int64_t>(output);
+ setRegister(rd_reg(instr), alu_out);
+ break;
+ }
+ case op_bitrev_w: {
+ uint32_t input = static_cast<uint32_t>(rj(instr));
+ uint32_t output = 0;
+ output = ReverseBits(input);
+ alu_out = static_cast<int64_t>(static_cast<int32_t>(output));
+ setRegister(rd_reg(instr), alu_out);
+ break;
+ }
+ case op_bitrev_d: {
+ alu_out = static_cast<int64_t>(ReverseBits(rj_u(instr)));
+ setRegister(rd_reg(instr), alu_out);
+ break;
+ }
+ case op_ext_w_b: {
+ uint8_t input = static_cast<uint8_t>(rj(instr));
+ alu_out = static_cast<int64_t>(static_cast<int8_t>(input));
+ setRegister(rd_reg(instr), alu_out);
+ break;
+ }
+ case op_ext_w_h: {
+ uint16_t input = static_cast<uint16_t>(rj(instr));
+ alu_out = static_cast<int64_t>(static_cast<int16_t>(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<int32_t>(rj(instr)));
+ break;
+ }
+ case op_movgr2fr_d: {
+ setFpuRegister(fd_reg(instr), rj(instr));
+ break;
+ }
+ case op_movgr2frh_w: {
+ setFpuRegisterHiWord(fd_reg(instr), static_cast<int32_t>(rj(instr)));
+ break;
+ }
+ case op_movfr2gr_s: {
+ setRegister(rd_reg(instr),
+ static_cast<int64_t>(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<uint32_t>(rj(instr));
+ break;
+ }
+ case op_movfcsr2gr: {
+ setRegister(rd_reg(instr), FCSR_);
+ break;
+ }
+ case op_fcvt_s_d: {
+ setFpuRegisterFloat(fd_reg(instr), static_cast<float>(fj_double(instr)));
+ break;
+ }
+ case op_fcvt_d_s: {
+ setFpuRegisterDouble(fd_reg(instr), static_cast<double>(fj_float(instr)));
+ break;
+ }
+ case op_ftintrm_w_s: {
+ float fj = fj_float(instr);
+ float rounded = std::floor(fj);
+ int32_t result = static_cast<int32_t>(rounded);
+ setFpuRegisterWord(fd_reg(instr), result);
+ if (setFCSRRoundError<int32_t>(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<int32_t>(rounded);
+ setFpuRegisterWord(fd_reg(instr), result);
+ if (setFCSRRoundError<int32_t>(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<int64_t>(rounded);
+ setFpuRegister(fd_reg(instr), result);
+ if (setFCSRRoundError<int64_t>(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<int64_t>(rounded);
+ setFpuRegister(fd_reg(instr), result);
+ if (setFCSRRoundError<int64_t>(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<int32_t>(rounded);
+ setFpuRegisterWord(fd_reg(instr), result);
+ if (setFCSRRoundError<int32_t>(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<int32_t>(rounded);
+ setFpuRegisterWord(fd_reg(instr), result);
+ if (setFCSRRoundError<int32_t>(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<int64_t>(rounded);
+ setFpuRegister(fd_reg(instr), result);
+ if (setFCSRRoundError<int64_t>(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<int64_t>(rounded);
+ setFpuRegister(fd_reg(instr), result);
+ if (setFCSRRoundError<int64_t>(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<int32_t>(rounded);
+ setFpuRegisterWord(fd_reg(instr), result);
+ if (setFCSRRoundError<int32_t>(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<int32_t>(rounded);
+ setFpuRegisterWord(fd_reg(instr), result);
+ if (setFCSRRoundError<int32_t>(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<int64_t>(rounded);
+ setFpuRegister(fd_reg(instr), result);
+ if (setFCSRRoundError<int64_t>(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<int64_t>(rounded);
+ setFpuRegister(fd_reg(instr), result);
+ if (setFCSRRoundError<int64_t>(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<int32_t>(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<int32_t>(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<int32_t>(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<int32_t>(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<int64_t>(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<int64_t>(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<int64_t>(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<int64_t>(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<float>(fj, &rounded, &result);
+ setFpuRegisterWord(fd_reg(instr), result);
+ if (setFCSRRoundError<int32_t>(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<double>(fj, &rounded, &result);
+ setFpuRegisterWord(fd_reg(instr), result);
+ if (setFCSRRoundError<int32_t>(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<float>(fj, &rounded, &result);
+ setFpuRegister(fd_reg(instr), result);
+ if (setFCSRRoundError<int64_t>(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<double>(fj, &rounded, &result);
+ setFpuRegister(fd_reg(instr), result);
+ if (setFCSRRoundError<int64_t>(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<float>(alu_out));
+ break;
+ }
+ case op_ffint_s_l: {
+ alu_out = getFpuRegister(fj_reg(instr));
+ setFpuRegisterFloat(fd_reg(instr), static_cast<float>(alu_out));
+ break;
+ }
+ case op_ffint_d_w: {
+ alu_out = getFpuRegisterSignedWord(fj_reg(instr));
+ setFpuRegisterDouble(fd_reg(instr), static_cast<double>(alu_out));
+ break;
+ }
+ case op_ffint_d_l: {
+ alu_out = getFpuRegister(fj_reg(instr));
+ setFpuRegisterDouble(fd_reg(instr), static_cast<double>(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<int64_t>(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 <bool enableStopSimAt>
+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<SimInstruction*>(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<int64_t>(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<true>();
+ } else {
+ execute<false>();
+ }
+
+ // 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<intptr_t*>(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<uintptr_t*>(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<uintptr_t*>(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_; }