summaryrefslogtreecommitdiffstats
path: root/js/src/jit/riscv64/Simulator-riscv64.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /js/src/jit/riscv64/Simulator-riscv64.cpp
parentInitial commit. (diff)
downloadfirefox-esr-upstream.tar.xz
firefox-esr-upstream.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/jit/riscv64/Simulator-riscv64.cpp')
-rw-r--r--js/src/jit/riscv64/Simulator-riscv64.cpp4718
1 files changed, 4718 insertions, 0 deletions
diff --git a/js/src/jit/riscv64/Simulator-riscv64.cpp b/js/src/jit/riscv64/Simulator-riscv64.cpp
new file mode 100644
index 0000000000..02a668f185
--- /dev/null
+++ b/js/src/jit/riscv64/Simulator-riscv64.cpp
@@ -0,0 +1,4718 @@
+/* -*- 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 2021 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.
+#ifdef JS_SIMULATOR_RISCV64
+# include "jit/riscv64/Simulator-riscv64.h"
+
+# include "mozilla/Casting.h"
+# include "mozilla/FloatingPoint.h"
+# include "mozilla/IntegerPrintfMacros.h"
+# include "mozilla/Likely.h"
+# include "mozilla/MathAlgorithms.h"
+
+# include <float.h>
+# include <iostream>
+# include <limits>
+
+# include "jit/AtomicOperations.h"
+# include "jit/riscv64/Assembler-riscv64.h"
+# include "js/Conversions.h"
+# include "js/UniquePtr.h"
+# include "js/Utility.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 REGIx_FORMAT PRIx64
+# define REGId_FORMAT PRId64
+
+# define I32_CHECK(v) \
+ ({ \
+ MOZ_ASSERT(I64(I32(v)) == I64(v)); \
+ I32((v)); \
+ })
+
+namespace js {
+namespace jit {
+
+bool Simulator::FLAG_trace_sim = false;
+bool Simulator::FLAG_debug_sim = false;
+bool Simulator::FLAG_riscv_trap_to_simulator_debugger = false;
+bool Simulator::FLAG_riscv_print_watchpoint = false;
+
+static void UNIMPLEMENTED() {
+ printf("UNIMPLEMENTED instruction.\n");
+ MOZ_CRASH();
+}
+static void UNREACHABLE() {
+ printf("UNREACHABLE instruction.\n");
+ MOZ_CRASH();
+}
+# define UNSUPPORTED() \
+ std::cout << "Unrecognized instruction [@pc=0x" << std::hex \
+ << registers_[pc] << "]: 0x" << instr_.InstructionBits() \
+ << std::endl; \
+ printf("Unsupported instruction.\n"); \
+ MOZ_CRASH();
+
+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();
+}
+
+// -----------------------------------------------------------------------------
+// Riscv assembly various constants.
+
+// C/C++ argument slots size.
+const int kCArgSlotCount = 0;
+const int kCArgsSlotsSize = kCArgSlotCount * sizeof(uintptr_t);
+const int kBranchReturnOffset = 2 * kInstrSize;
+
+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;
+
+static bool IsFlag(const char* found, const char* flag) {
+ return strlen(found) == strlen(flag) && strcmp(found, flag) == 0;
+}
+
+Simulator* Simulator::Create() {
+ auto sim = MakeUnique<Simulator>();
+ if (!sim) {
+ return nullptr;
+ }
+
+ if (!sim->init()) {
+ return nullptr;
+ }
+
+ int64_t stopAt;
+ char* stopAtStr = getenv("RISCV_SIM_STOP_AT");
+ if (stopAtStr && sscanf(stopAtStr, "%" PRIi64, &stopAt) == 1) {
+ fprintf(stderr, "\nStopping simulation at icount %" PRIi64 "\n", stopAt);
+ Simulator::StopSimAt = stopAt;
+ }
+ char* str = getenv("RISCV_TRACE_SIM");
+ if (str != nullptr && IsFlag(str, "true")) {
+ FLAG_trace_sim = true;
+ }
+
+ return sim.release();
+}
+
+void Simulator::Destroy(Simulator* sim) { js_delete(sim); }
+
+# if JS_CODEGEN_RISCV64
+void Simulator::TraceRegWr(int64_t value, TraceType t) {
+ if (FLAG_trace_sim) {
+ union {
+ int64_t fmt_int64;
+ int32_t fmt_int32[2];
+ float fmt_float[2];
+ double fmt_double;
+ } v;
+ v.fmt_int64 = value;
+
+ switch (t) {
+ case WORD:
+ SNPrintF(trace_buf_,
+ "%016" REGIx_FORMAT " (%" PRId64 ") int32:%" PRId32
+ " uint32:%" PRIu32,
+ v.fmt_int64, icount_, v.fmt_int32[0], v.fmt_int32[0]);
+ break;
+ case DWORD:
+ SNPrintF(trace_buf_,
+ "%016" REGIx_FORMAT " (%" PRId64 ") int64:%" REGId_FORMAT
+ " uint64:%" PRIu64,
+ value, icount_, value, value);
+ break;
+ case FLOAT:
+ SNPrintF(trace_buf_, "%016" REGIx_FORMAT " (%" PRId64 ") flt:%e",
+ v.fmt_int64, icount_, v.fmt_float[0]);
+ break;
+ case DOUBLE:
+ SNPrintF(trace_buf_, "%016" REGIx_FORMAT " (%" PRId64 ") dbl:%e",
+ v.fmt_int64, icount_, v.fmt_double);
+ break;
+ default:
+ UNREACHABLE();
+ }
+ }
+}
+
+# elif JS_CODEGEN_RISCV32
+template <typename T>
+void Simulator::TraceRegWr(T value, TraceType t) {
+ if (::v8::internal::FLAG_trace_sim) {
+ union {
+ int32_t fmt_int32;
+ float fmt_float;
+ double fmt_double;
+ } v;
+ if (t != DOUBLE) {
+ v.fmt_int32 = value;
+ } else {
+ DCHECK_EQ(sizeof(T), 8);
+ v.fmt_double = value;
+ }
+ switch (t) {
+ case WORD:
+ SNPrintF(trace_buf_,
+ "%016" REGIx_FORMAT " (%" PRId64 ") int32:%" REGId_FORMAT
+ " uint32:%" PRIu32,
+ v.fmt_int32, icount_, v.fmt_int32, v.fmt_int32);
+ break;
+ case FLOAT:
+ SNPrintF(trace_buf_, "%016" REGIx_FORMAT " (%" PRId64 ") flt:%e",
+ v.fmt_int32, icount_, v.fmt_float);
+ break;
+ case DOUBLE:
+ SNPrintF(trace_buf_, "%016" PRIx64 " (%" PRId64 ") dbl:%e",
+ static_cast<int64_t>(v.fmt_double), icount_, v.fmt_double);
+ break;
+ default:
+ UNREACHABLE();
+ }
+ }
+}
+# endif
+// The RiscvDebugger class is used by the simulator while debugging simulated
+// code.
+class RiscvDebugger {
+ public:
+ explicit RiscvDebugger(Simulator* sim) : sim_(sim) {}
+
+ void Debug();
+ // Print all registers with a nice formatting.
+ void PrintRegs(char name_prefix, int start_index, int end_index);
+ void printAllRegs();
+ void printAllRegsIncludingFPU();
+
+ static const Instr kNopInstr = 0x0;
+
+ private:
+ Simulator* sim_;
+
+ int64_t GetRegisterValue(int regnum);
+ int64_t GetFPURegisterValue(int regnum);
+ float GetFPURegisterValueFloat(int regnum);
+ double GetFPURegisterValueDouble(int regnum);
+# ifdef CAN_USE_RVV_INSTRUCTIONS
+ __int128_t GetVRegisterValue(int regnum);
+# endif
+ bool GetValue(const char* desc, int64_t* value);
+};
+
+int64_t RiscvDebugger::GetRegisterValue(int regnum) {
+ if (regnum == Simulator::Register::kNumSimuRegisters) {
+ return sim_->get_pc();
+ } else {
+ return sim_->getRegister(regnum);
+ }
+}
+
+int64_t RiscvDebugger::GetFPURegisterValue(int regnum) {
+ if (regnum == Simulator::FPURegister::kNumFPURegisters) {
+ return sim_->get_pc();
+ } else {
+ return sim_->getFpuRegister(regnum);
+ }
+}
+
+float RiscvDebugger::GetFPURegisterValueFloat(int regnum) {
+ if (regnum == Simulator::FPURegister::kNumFPURegisters) {
+ return sim_->get_pc();
+ } else {
+ return sim_->getFpuRegisterFloat(regnum);
+ }
+}
+
+double RiscvDebugger::GetFPURegisterValueDouble(int regnum) {
+ if (regnum == Simulator::FPURegister::kNumFPURegisters) {
+ return sim_->get_pc();
+ } else {
+ return sim_->getFpuRegisterDouble(regnum);
+ }
+}
+
+# ifdef CAN_USE_RVV_INSTRUCTIONS
+__int128_t RiscvDebugger::GetVRegisterValue(int regnum) {
+ if (regnum == kNumVRegisters) {
+ return sim_->get_pc();
+ } else {
+ return sim_->get_vregister(regnum);
+ }
+}
+# endif
+
+bool RiscvDebugger::GetValue(const char* desc, int64_t* value) {
+ int regnum = Registers::FromName(desc);
+ int fpuregnum = FloatRegisters::FromName(desc);
+
+ if (regnum != Registers::invalid_reg) {
+ *value = GetRegisterValue(regnum);
+ return true;
+ } else if (fpuregnum != FloatRegisters::invalid_reg) {
+ *value = GetFPURegisterValue(fpuregnum);
+ return true;
+ } else if (strncmp(desc, "0x", 2) == 0) {
+ return sscanf(desc + 2, "%" SCNx64, reinterpret_cast<int64_t*>(value)) == 1;
+ } else {
+ return sscanf(desc, "%" SCNu64, reinterpret_cast<int64_t*>(value)) == 1;
+ }
+}
+
+# define REG_INFO(name) \
+ name, GetRegisterValue(Registers::FromName(name)), \
+ GetRegisterValue(Registers::FromName(name))
+
+void RiscvDebugger::PrintRegs(char name_prefix, int start_index,
+ int end_index) {
+ EmbeddedVector<char, 10> name1, name2;
+ MOZ_ASSERT(name_prefix == 'a' || name_prefix == 't' || name_prefix == 's');
+ MOZ_ASSERT(start_index >= 0 && end_index <= 99);
+ int num_registers = (end_index - start_index) + 1;
+ for (int i = 0; i < num_registers / 2; i++) {
+ SNPrintF(name1, "%c%d", name_prefix, start_index + 2 * i);
+ SNPrintF(name2, "%c%d", name_prefix, start_index + 2 * i + 1);
+ printf("%3s: 0x%016" REGIx_FORMAT " %14" REGId_FORMAT
+ " \t%3s: 0x%016" REGIx_FORMAT " %14" REGId_FORMAT " \n",
+ REG_INFO(name1.start()), REG_INFO(name2.start()));
+ }
+ if (num_registers % 2 == 1) {
+ SNPrintF(name1, "%c%d", name_prefix, end_index);
+ printf("%3s: 0x%016" REGIx_FORMAT " %14" REGId_FORMAT " \n",
+ REG_INFO(name1.start()));
+ }
+}
+
+void RiscvDebugger::printAllRegs() {
+ printf("\n");
+ // ra, sp, gp
+ printf("%3s: 0x%016" REGIx_FORMAT " %14" REGId_FORMAT
+ "\t%3s: 0x%016" REGIx_FORMAT " %14" REGId_FORMAT
+ "\t%3s: 0x%016" REGIx_FORMAT " %14" REGId_FORMAT "\n",
+ REG_INFO("ra"), REG_INFO("sp"), REG_INFO("gp"));
+
+ // tp, fp, pc
+ printf("%3s: 0x%016" REGIx_FORMAT " %14" REGId_FORMAT
+ "\t%3s: 0x%016" REGIx_FORMAT " %14" REGId_FORMAT
+ "\t%3s: 0x%016" REGIx_FORMAT " %14" REGId_FORMAT "\n",
+ REG_INFO("tp"), REG_INFO("fp"), REG_INFO("pc"));
+
+ // print register a0, .., a7
+ PrintRegs('a', 0, 7);
+ // print registers s1, ..., s11
+ PrintRegs('s', 1, 11);
+ // print registers t0, ..., t6
+ PrintRegs('t', 0, 6);
+}
+
+# undef REG_INFO
+
+void RiscvDebugger::printAllRegsIncludingFPU() {
+# define FPU_REG_INFO(n) \
+ FloatRegisters::GetName(n), GetFPURegisterValue(n), \
+ GetFPURegisterValueDouble(n)
+
+ printAllRegs();
+
+ printf("\n\n");
+ // f0, f1, f2, ... f31.
+ MOZ_ASSERT(kNumFPURegisters % 2 == 0);
+ for (int i = 0; i < kNumFPURegisters; i += 2)
+ printf("%3s: 0x%016" PRIx64 " %16.4e \t%3s: 0x%016" PRIx64 " %16.4e\n",
+ FPU_REG_INFO(i), FPU_REG_INFO(i + 1));
+# undef FPU_REG_INFO
+}
+
+void RiscvDebugger::Debug() {
+ intptr_t last_pc = -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;
+
+ while (!done && (sim_->get_pc() != Simulator::end_sim_pc)) {
+ if (last_pc != sim_->get_pc()) {
+ disasm::NameConverter converter;
+ disasm::Disassembler dasm(converter);
+ // Use a reasonably large buffer.
+ EmbeddedVector<char, 256> buffer;
+ dasm.InstructionDecode(buffer, reinterpret_cast<byte*>(sim_->get_pc()));
+ printf(" 0x%016" REGIx_FORMAT " %s\n", sim_->get_pc(), buffer.start());
+ last_pc = 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()) ||
+ instr->InstructionBits() == rtCallRedirInstr) {
+ sim_->icount_++;
+ sim_->InstructionDecode(
+ reinterpret_cast<Instruction*>(sim_->get_pc()));
+ } else {
+ // Allow si to jump over generated breakpoints.
+ printf("/!\\ Jumping over generated breakpoint.\n");
+ sim_->set_pc(sim_->get_pc() + kInstrSize);
+ }
+ } else if ((strcmp(cmd, "c") == 0) || (strcmp(cmd, "cont") == 0)) {
+ // Leave the debugger shell.
+ done = true;
+ } else if ((strcmp(cmd, "p") == 0) || (strcmp(cmd, "print") == 0)) {
+ if (argc == 2) {
+ int64_t value;
+ int64_t fvalue;
+ double dvalue;
+ if (strcmp(arg1, "all") == 0) {
+ printAllRegs();
+ } else if (strcmp(arg1, "allf") == 0) {
+ printAllRegsIncludingFPU();
+ } else {
+ int regnum = Registers::FromName(arg1);
+ int fpuregnum = FloatRegisters::FromName(arg1);
+# ifdef CAN_USE_RVV_INSTRUCTIONS
+ int vregnum = VRegisters::FromName(arg1);
+# endif
+ if (regnum != Registers::invalid_reg) {
+ value = GetRegisterValue(regnum);
+ printf("%s: 0x%08" REGIx_FORMAT " %" REGId_FORMAT " \n", arg1,
+ value, value);
+ } else if (fpuregnum != FloatRegisters::invalid_reg) {
+ fvalue = GetFPURegisterValue(fpuregnum);
+ dvalue = GetFPURegisterValueDouble(fpuregnum);
+ printf("%3s: 0x%016" PRIx64 " %16.4e\n",
+ FloatRegisters::GetName(fpuregnum), fvalue, dvalue);
+# ifdef CAN_USE_RVV_INSTRUCTIONS
+ } else if (vregnum != kInvalidVRegister) {
+ __int128_t v = GetVRegisterValue(vregnum);
+ printf("\t%s:0x%016" REGIx_FORMAT "%016" REGIx_FORMAT "\n",
+ VRegisters::GetName(vregnum), (uint64_t)(v >> 64),
+ (uint64_t)v);
+# endif
+ } else {
+ printf("%s unrecognized\n", arg1);
+ }
+ }
+ } else {
+ if (argc == 3) {
+ if (strcmp(arg2, "single") == 0) {
+ int64_t value;
+ float fvalue;
+ int fpuregnum = FloatRegisters::FromName(arg1);
+
+ if (fpuregnum != FloatRegisters::invalid_reg) {
+ value = GetFPURegisterValue(fpuregnum);
+ value &= 0xFFFFFFFFUL;
+ fvalue = GetFPURegisterValueFloat(fpuregnum);
+ printf("%s: 0x%08" PRIx64 " %11.4e\n", arg1, value, fvalue);
+ } else {
+ printf("%s unrecognized\n", arg1);
+ }
+ } else {
+ printf("print <fpu register> single\n");
+ }
+ } else {
+ printf("print <register> or print <fpu register> single\n");
+ }
+ }
+ } else if ((strcmp(cmd, "po") == 0) ||
+ (strcmp(cmd, "printobject") == 0)) {
+ UNIMPLEMENTED();
+ } else if (strcmp(cmd, "stack") == 0 || strcmp(cmd, "mem") == 0) {
+ int64_t* cur = nullptr;
+ int64_t* end = nullptr;
+ int next_arg = 1;
+ if (argc < 2) {
+ printf("Need to specify <address> to memhex command\n");
+ continue;
+ }
+ 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(" 0x%012" PRIxPTR " : 0x%016" REGIx_FORMAT
+ " %14" REGId_FORMAT " ",
+ reinterpret_cast<intptr_t>(cur), *cur, *cur);
+ printf("\n");
+ cur++;
+ }
+ } else if ((strcmp(cmd, "watch") == 0)) {
+ if (argc < 2) {
+ printf("Need to specify <address> to mem command\n");
+ continue;
+ }
+ int64_t value;
+ if (!GetValue(arg1, &value)) {
+ printf("%s unrecognized\n", arg1);
+ continue;
+ }
+ sim_->watch_address_ = reinterpret_cast<int64_t*>(value);
+ sim_->watch_value_ = *(sim_->watch_address_);
+ } else if ((strcmp(cmd, "disasm") == 0) || (strcmp(cmd, "dpc") == 0) ||
+ (strcmp(cmd, "di") == 0)) {
+ disasm::NameConverter converter;
+ disasm::Disassembler dasm(converter);
+ // Use a reasonably large buffer.
+ EmbeddedVector<char, 256> buffer;
+
+ byte* cur = nullptr;
+ byte* end = nullptr;
+
+ if (argc == 1) {
+ cur = reinterpret_cast<byte*>(sim_->get_pc());
+ end = cur + (10 * kInstrSize);
+ } else if (argc == 2) {
+ auto regnum = Registers::FromName(arg1);
+ if (regnum != Registers::invalid_reg || strncmp(arg1, "0x", 2) == 0) {
+ // The argument is an address or a register name.
+ sreg_t value;
+ if (GetValue(arg1, &value)) {
+ cur = reinterpret_cast<byte*>(value);
+ // Disassemble 10 instructions at <arg1>.
+ end = cur + (10 * kInstrSize);
+ }
+ } else {
+ // The argument is the number of instructions.
+ sreg_t value;
+ if (GetValue(arg1, &value)) {
+ cur = reinterpret_cast<byte*>(sim_->get_pc());
+ // Disassemble <arg1> instructions.
+ end = cur + (value * kInstrSize);
+ }
+ }
+ } else {
+ sreg_t value1;
+ sreg_t value2;
+ if (GetValue(arg1, &value1) && GetValue(arg2, &value2)) {
+ cur = reinterpret_cast<byte*>(value1);
+ end = cur + (value2 * kInstrSize);
+ }
+ }
+ while (cur < end) {
+ dasm.InstructionDecode(buffer, cur);
+ printf(" 0x%08" PRIxPTR " %s\n", reinterpret_cast<intptr_t>(cur),
+ buffer.start());
+ cur += kInstrSize;
+ }
+ } else if (strcmp(cmd, "trace") == 0) {
+ Simulator::FLAG_trace_sim = true;
+ Simulator::FLAG_riscv_print_watchpoint = true;
+ } else if (strcmp(cmd, "break") == 0 || strcmp(cmd, "b") == 0 ||
+ strcmp(cmd, "tbreak") == 0) {
+ bool is_tbreak = strcmp(cmd, "tbreak") == 0;
+ if (argc == 2) {
+ int64_t value;
+ if (GetValue(arg1, &value)) {
+ sim_->SetBreakpoint(reinterpret_cast<SimInstruction*>(value),
+ is_tbreak);
+ } else {
+ printf("%s unrecognized\n", arg1);
+ }
+ } else {
+ sim_->ListBreakpoints();
+ printf("Use `break <address>` to set or disable a breakpoint\n");
+ printf(
+ "Use `tbreak <address>` to set or disable a temporary "
+ "breakpoint\n");
+ }
+ } else if (strcmp(cmd, "flags") == 0) {
+ printf("No flags on RISC-V !\n");
+ } else if (strcmp(cmd, "stop") == 0) {
+ int64_t value;
+ 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, "stat") == 0) || (strcmp(cmd, "st") == 0)) {
+ UNIMPLEMENTED();
+ } else if ((strcmp(cmd, "h") == 0) || (strcmp(cmd, "help") == 0)) {
+ printf("cont (alias 'c')\n");
+ printf(" Continue execution\n");
+ printf("stepi (alias 'si')\n");
+ printf(" Step one instruction\n");
+ printf("print (alias 'p')\n");
+ printf(" print <register>\n");
+ printf(" Print register content\n");
+ printf(" Use register name 'all' to print all GPRs\n");
+ printf(" Use register name 'allf' to print all GPRs and FPRs\n");
+ printf("printobject (alias 'po')\n");
+ printf(" printobject <register>\n");
+ printf(" Print an object from a register\n");
+ printf("stack\n");
+ printf(" stack [<words>]\n");
+ printf(" Dump stack content, default dump 10 words)\n");
+ printf("mem\n");
+ printf(" mem <address> [<words>]\n");
+ printf(" Dump memory content, default dump 10 words)\n");
+ printf("watch\n");
+ printf(" watch <address> \n");
+ printf(" watch memory content.)\n");
+ printf("flags\n");
+ printf(" print flags\n");
+ printf("disasm (alias 'di')\n");
+ printf(" disasm [<instructions>]\n");
+ printf(" disasm [<address/register>] (e.g., disasm pc) \n");
+ printf(" disasm [[<address/register>] <instructions>]\n");
+ printf(" Disassemble code, default is 10 instructions\n");
+ printf(" from pc\n");
+ printf("gdb \n");
+ printf(" Return to gdb if the simulator was started with gdb\n");
+ printf("break (alias 'b')\n");
+ printf(" break : list all breakpoints\n");
+ printf(" break <address> : set / enable / disable a breakpoint.\n");
+ printf("tbreak\n");
+ printf(" tbreak : list all breakpoints\n");
+ printf(
+ " tbreak <address> : set / enable / disable a temporary "
+ "breakpoint.\n");
+ printf(" Set a breakpoint enabled only for one stop. \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 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");
+ } else {
+ printf("Unknown command: %s\n", cmd);
+ }
+ }
+ }
+
+# undef COMMAND_SIZE
+# undef ARG_SIZE
+
+# undef STR
+# undef XSTR
+}
+
+void Simulator::SetBreakpoint(SimInstruction* location, bool is_tbreak) {
+ for (unsigned i = 0; i < breakpoints_.size(); i++) {
+ if (breakpoints_.at(i).location == location) {
+ if (breakpoints_.at(i).is_tbreak != is_tbreak) {
+ printf("Change breakpoint at %p to %s breakpoint\n",
+ reinterpret_cast<void*>(location),
+ is_tbreak ? "temporary" : "regular");
+ breakpoints_.at(i).is_tbreak = is_tbreak;
+ return;
+ }
+ printf("Existing breakpoint at %p was %s\n",
+ reinterpret_cast<void*>(location),
+ breakpoints_.at(i).enabled ? "disabled" : "enabled");
+ breakpoints_.at(i).enabled = !breakpoints_.at(i).enabled;
+ return;
+ }
+ }
+ Breakpoint new_breakpoint = {location, true, is_tbreak};
+ breakpoints_.push_back(new_breakpoint);
+ printf("Set a %sbreakpoint at %p\n", is_tbreak ? "temporary " : "",
+ reinterpret_cast<void*>(location));
+}
+
+void Simulator::ListBreakpoints() {
+ printf("Breakpoints:\n");
+ for (unsigned i = 0; i < breakpoints_.size(); i++) {
+ printf("%p : %s %s\n",
+ reinterpret_cast<void*>(breakpoints_.at(i).location),
+ breakpoints_.at(i).enabled ? "enabled" : "disabled",
+ breakpoints_.at(i).is_tbreak ? ": temporary" : "");
+ }
+}
+
+void Simulator::CheckBreakpoints() {
+ bool hit_a_breakpoint = false;
+ bool is_tbreak = false;
+ SimInstruction* pc_ = reinterpret_cast<SimInstruction*>(get_pc());
+ for (unsigned i = 0; i < breakpoints_.size(); i++) {
+ if ((breakpoints_.at(i).location == pc_) && breakpoints_.at(i).enabled) {
+ hit_a_breakpoint = true;
+ if (breakpoints_.at(i).is_tbreak) {
+ // Disable a temporary breakpoint.
+ is_tbreak = true;
+ breakpoints_.at(i).enabled = false;
+ }
+ break;
+ }
+ }
+ if (hit_a_breakpoint) {
+ printf("Hit %sa breakpoint at %p.\n", is_tbreak ? "and disabled " : "",
+ reinterpret_cast<void*>(pc_));
+ RiscvDebugger dbg(this);
+ dbg.Debug();
+ }
+}
+
+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.
+ int cmpret = memcmp(reinterpret_cast<void*>(instr),
+ cache_page->cachedData(offset), 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 < Simulator::Register::kNumSimuRegisters; i++) {
+ registers_[i] = 0;
+ }
+ for (int i = 0; i < Simulator::FPURegister::kNumFPURegisters; i++) {
+ FPUregisters_[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_(rtCallRedirInstr),
+ type_(type),
+ next_(nullptr) {
+ next_ = SimulatorProcess::redirection();
+ if (!SimulatorProcess::ICacheCheckingDisableCount) {
+ FlushICacheLocked(SimulatorProcess::icache(), addressOfSwiInstruction(),
+ 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(Instruction* 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("MIPS_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 < Simulator::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::setFpuRegisterLo(int fpureg, int32_t value) {
+ MOZ_ASSERT((fpureg >= 0) &&
+ (fpureg < Simulator::FPURegister::kNumFPURegisters));
+ *mozilla::BitwiseCast<int32_t*>(&FPUregisters_[fpureg]) = value;
+}
+
+void Simulator::setFpuRegisterHi(int fpureg, int32_t value) {
+ MOZ_ASSERT((fpureg >= 0) &&
+ (fpureg < Simulator::FPURegister::kNumFPURegisters));
+ *((mozilla::BitwiseCast<int32_t*>(&FPUregisters_[fpureg])) + 1) = value;
+}
+
+void Simulator::setFpuRegisterFloat(int fpureg, float value) {
+ MOZ_ASSERT((fpureg >= 0) &&
+ (fpureg < Simulator::FPURegister::kNumFPURegisters));
+ *mozilla::BitwiseCast<int64_t*>(&FPUregisters_[fpureg]) = box_float(value);
+}
+
+void Simulator::setFpuRegisterFloat(int fpureg, Float32 value) {
+ MOZ_ASSERT((fpureg >= 0) && (fpureg < kNumFPURegisters));
+ Float64 t = Float64::FromBits(box_float(value.get_bits()));
+ memcpy(&FPUregisters_[fpureg], &t, 8);
+}
+
+void Simulator::setFpuRegisterDouble(int fpureg, double value) {
+ MOZ_ASSERT((fpureg >= 0) &&
+ (fpureg < Simulator::FPURegister::kNumFPURegisters));
+ *mozilla::BitwiseCast<double*>(&FPUregisters_[fpureg]) = value;
+}
+
+void Simulator::setFpuRegisterDouble(int fpureg, Float64 value) {
+ MOZ_ASSERT((fpureg >= 0) && (fpureg < kNumFPURegisters));
+ memcpy(&FPUregisters_[fpureg], &value, 8);
+}
+
+// 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 < Simulator::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::getFpuRegisterLo(int fpureg) const {
+ MOZ_ASSERT((fpureg >= 0) &&
+ (fpureg < Simulator::FPURegister::kNumFPURegisters));
+ return *mozilla::BitwiseCast<int32_t*>(&FPUregisters_[fpureg]);
+}
+
+int32_t Simulator::getFpuRegisterHi(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]);
+}
+
+Float32 Simulator::getFpuRegisterFloat32(int fpureg) const {
+ MOZ_ASSERT((fpureg >= 0) && (fpureg < kNumFPURegisters));
+ if (!is_boxed_float(FPUregisters_[fpureg])) {
+ return Float32::FromBits(0x7ffc0000);
+ }
+ return Float32::FromBits(
+ *bit_cast<uint32_t*>(const_cast<int64_t*>(&FPUregisters_[fpureg])));
+}
+
+double Simulator::getFpuRegisterDouble(int fpureg) const {
+ MOZ_ASSERT((fpureg >= 0) &&
+ (fpureg < Simulator::FPURegister::kNumFPURegisters));
+ return *mozilla::BitwiseCast<double*>(&FPUregisters_[fpureg]);
+}
+
+Float64 Simulator::getFpuRegisterFloat64(int fpureg) const {
+ MOZ_ASSERT((fpureg >= 0) && (fpureg < kNumFPURegisters));
+ return Float64::FromBits(FPUregisters_[fpureg]);
+}
+
+void Simulator::setCallResultDouble(double result) {
+ setFpuRegisterDouble(fa0, result);
+}
+
+void Simulator::setCallResultFloat(float result) {
+ setFpuRegisterFloat(fa0, 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));
+}
+
+// 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;
+}
+
+// TODO(plind): consider making icount_ printing a flag option.
+template <typename T>
+void Simulator::TraceMemRd(sreg_t addr, T value, sreg_t reg_value) {
+ if (FLAG_trace_sim) {
+ if (std::is_integral<T>::value) {
+ switch (sizeof(T)) {
+ case 1:
+ SNPrintF(trace_buf_,
+ "%016" REGIx_FORMAT " (%" PRId64 ") int8:%" PRId8
+ " uint8:%" PRIu8 " <-- [addr: %" REGIx_FORMAT "]",
+ reg_value, icount_, static_cast<int8_t>(value),
+ static_cast<uint8_t>(value), addr);
+ break;
+ case 2:
+ SNPrintF(trace_buf_,
+ "%016" REGIx_FORMAT " (%" PRId64 ") int16:%" PRId16
+ " uint16:%" PRIu16 " <-- [addr: %" REGIx_FORMAT "]",
+ reg_value, icount_, static_cast<int16_t>(value),
+ static_cast<uint16_t>(value), addr);
+ break;
+ case 4:
+ SNPrintF(trace_buf_,
+ "%016" REGIx_FORMAT " (%" PRId64 ") int32:%" PRId32
+ " uint32:%" PRIu32 " <-- [addr: %" REGIx_FORMAT "]",
+ reg_value, icount_, static_cast<int32_t>(value),
+ static_cast<uint32_t>(value), addr);
+ break;
+ case 8:
+ SNPrintF(trace_buf_,
+ "%016" REGIx_FORMAT " (%" PRId64 ") int64:%" PRId64
+ " uint64:%" PRIu64 " <-- [addr: %" REGIx_FORMAT "]",
+ reg_value, icount_, static_cast<int64_t>(value),
+ static_cast<uint64_t>(value), addr);
+ break;
+ default:
+ UNREACHABLE();
+ }
+ } else if (std::is_same<float, T>::value) {
+ SNPrintF(trace_buf_,
+ "%016" REGIx_FORMAT " (%" PRId64
+ ") flt:%e <-- [addr: %" REGIx_FORMAT "]",
+ reg_value, icount_, static_cast<float>(value), addr);
+ } else if (std::is_same<double, T>::value) {
+ SNPrintF(trace_buf_,
+ "%016" REGIx_FORMAT " (%" PRId64
+ ") dbl:%e <-- [addr: %" REGIx_FORMAT "]",
+ reg_value, icount_, static_cast<double>(value), addr);
+ } else {
+ UNREACHABLE();
+ }
+ }
+}
+
+void Simulator::TraceMemRdFloat(sreg_t addr, Float32 value, int64_t reg_value) {
+ if (FLAG_trace_sim) {
+ SNPrintF(trace_buf_,
+ "%016" PRIx64 " (%" PRId64
+ ") flt:%e <-- [addr: %" REGIx_FORMAT "]",
+ reg_value, icount_, static_cast<float>(value.get_scalar()), addr);
+ }
+}
+
+void Simulator::TraceMemRdDouble(sreg_t addr, double value, int64_t reg_value) {
+ if (FLAG_trace_sim) {
+ SNPrintF(trace_buf_,
+ "%016" PRIx64 " (%" PRId64
+ ") dbl:%e <-- [addr: %" REGIx_FORMAT "]",
+ reg_value, icount_, static_cast<double>(value), addr);
+ }
+}
+
+void Simulator::TraceMemRdDouble(sreg_t addr, Float64 value,
+ int64_t reg_value) {
+ if (FLAG_trace_sim) {
+ SNPrintF(trace_buf_,
+ "%016" PRIx64 " (%" PRId64
+ ") dbl:%e <-- [addr: %" REGIx_FORMAT "]",
+ reg_value, icount_, static_cast<double>(value.get_scalar()), addr);
+ }
+}
+
+template <typename T>
+void Simulator::TraceMemWr(sreg_t addr, T value) {
+ if (FLAG_trace_sim) {
+ switch (sizeof(T)) {
+ case 1:
+ SNPrintF(trace_buf_,
+ " (%" PRIu64 ") int8:%" PRId8
+ " uint8:%" PRIu8 " --> [addr: %" REGIx_FORMAT "]",
+ icount_, static_cast<int8_t>(value),
+ static_cast<uint8_t>(value), addr);
+ break;
+ case 2:
+ SNPrintF(trace_buf_,
+ " (%" PRIu64 ") int16:%" PRId16
+ " uint16:%" PRIu16 " --> [addr: %" REGIx_FORMAT "]",
+ icount_, static_cast<int16_t>(value),
+ static_cast<uint16_t>(value), addr);
+ break;
+ case 4:
+ if (std::is_integral<T>::value) {
+ SNPrintF(trace_buf_,
+ " (%" PRIu64 ") int32:%" PRId32
+ " uint32:%" PRIu32 " --> [addr: %" REGIx_FORMAT "]",
+ icount_, static_cast<int32_t>(value),
+ static_cast<uint32_t>(value), addr);
+ } else {
+ SNPrintF(trace_buf_,
+ " (%" PRIu64
+ ") flt:%e --> [addr: %" REGIx_FORMAT "]",
+ icount_, static_cast<float>(value), addr);
+ }
+ break;
+ case 8:
+ if (std::is_integral<T>::value) {
+ SNPrintF(trace_buf_,
+ " (%" PRIu64 ") int64:%" PRId64
+ " uint64:%" PRIu64 " --> [addr: %" REGIx_FORMAT "]",
+ icount_, static_cast<int64_t>(value),
+ static_cast<uint64_t>(value), addr);
+ } else {
+ SNPrintF(trace_buf_,
+ " (%" PRIu64
+ ") dbl:%e --> [addr: %" REGIx_FORMAT "]",
+ icount_, static_cast<double>(value), addr);
+ }
+ break;
+ default:
+ UNREACHABLE();
+ }
+ }
+}
+
+void Simulator::TraceMemWrDouble(sreg_t addr, double value) {
+ if (FLAG_trace_sim) {
+ SNPrintF(trace_buf_,
+ " (%" PRIu64
+ ") dbl:%e --> [addr: %" REGIx_FORMAT "]",
+ icount_, value, addr);
+ }
+}
+
+template <typename T>
+void Simulator::TraceLr(sreg_t addr, T value, sreg_t reg_value) {
+ if (FLAG_trace_sim) {
+ if (std::is_integral<T>::value) {
+ switch (sizeof(T)) {
+ case 4:
+ SNPrintF(trace_buf_,
+ "%016" REGIx_FORMAT " (%" PRId64 ") int32:%" PRId32
+ " uint32:%" PRIu32 " <-- [addr: %" REGIx_FORMAT "]",
+ reg_value, icount_, static_cast<int32_t>(value),
+ static_cast<uint32_t>(value), addr);
+ break;
+ case 8:
+ SNPrintF(trace_buf_,
+ "%016" REGIx_FORMAT " (%" PRId64 ") int64:%" PRId64
+ " uint64:%" PRIu64 " <-- [addr: %" REGIx_FORMAT "]",
+ reg_value, icount_, static_cast<int64_t>(value),
+ static_cast<uint64_t>(value), addr);
+ break;
+ default:
+ UNREACHABLE();
+ }
+ } else {
+ UNREACHABLE();
+ }
+ }
+}
+
+template <typename T>
+void Simulator::TraceSc(sreg_t addr, T value) {
+ if (FLAG_trace_sim) {
+ switch (sizeof(T)) {
+ case 4:
+ SNPrintF(trace_buf_,
+ "%016" REGIx_FORMAT " (%" PRIu64 ") int32:%" PRId32
+ " uint32:%" PRIu32 " --> [addr: %" REGIx_FORMAT "]",
+ getRegister(rd_reg()), icount_, static_cast<int32_t>(value),
+ static_cast<uint32_t>(value), addr);
+ break;
+ case 8:
+ SNPrintF(trace_buf_,
+ "%016" REGIx_FORMAT " (%" PRIu64 ") int64:%" PRId64
+ " uint64:%" PRIu64 " --> [addr: %" REGIx_FORMAT "]",
+ getRegister(rd_reg()), icount_, static_cast<int64_t>(value),
+ static_cast<uint64_t>(value), addr);
+ break;
+ default:
+ UNREACHABLE();
+ }
+ }
+}
+
+// TODO(RISCV): check whether the specific board supports unaligned load/store
+// (determined by EEI). For now, we assume the board does not support unaligned
+// load/store (e.g., trapping)
+template <typename T>
+T Simulator::ReadMem(sreg_t addr, Instruction* instr) {
+ if (handleWasmSegFault(addr, sizeof(T))) {
+ return -1;
+ }
+ if (addr >= 0 && addr < 0x400) {
+ // This has to be a nullptr-dereference, drop into debugger.
+ printf("Memory read from bad address: 0x%08" REGIx_FORMAT
+ " , pc=0x%08" PRIxPTR " \n",
+ addr, reinterpret_cast<intptr_t>(instr));
+ DieOrDebug();
+ }
+ T* ptr = reinterpret_cast<T*>(addr);
+ T value = *ptr;
+ return value;
+}
+
+template <typename T>
+void Simulator::WriteMem(sreg_t addr, T value, Instruction* instr) {
+ if (handleWasmSegFault(addr, sizeof(T))) {
+ value = -1;
+ return;
+ }
+ if (addr >= 0 && addr < 0x400) {
+ // This has to be a nullptr-dereference, drop into debugger.
+ printf("Memory write to bad address: 0x%08" REGIx_FORMAT
+ " , pc=0x%08" PRIxPTR " \n",
+ addr, reinterpret_cast<intptr_t>(instr));
+ DieOrDebug();
+ }
+ T* ptr = reinterpret_cast<T*>(addr);
+ if (!std::is_same<double, T>::value) {
+ TraceMemWr(addr, value);
+ } else {
+ TraceMemWrDouble(addr, value);
+ }
+ *ptr = value;
+}
+
+template <>
+void Simulator::WriteMem(sreg_t addr, Float32 value, Instruction* instr) {
+ if (handleWasmSegFault(addr, 4)) {
+ value = Float32(-1.0f);
+ return;
+ }
+ if (addr >= 0 && addr < 0x400) {
+ // This has to be a nullptr-dereference, drop into debugger.
+ printf("Memory write to bad address: 0x%08" REGIx_FORMAT
+ " , pc=0x%08" PRIxPTR " \n",
+ addr, reinterpret_cast<intptr_t>(instr));
+ DieOrDebug();
+ }
+ float* ptr = reinterpret_cast<float*>(addr);
+ TraceMemWr(addr, value.get_scalar());
+ memcpy(ptr, &value, 4);
+}
+
+template <>
+void Simulator::WriteMem(sreg_t addr, Float64 value, Instruction* instr) {
+ if (handleWasmSegFault(addr, 8)) {
+ value = Float64(-1.0);
+ return;
+ }
+ if (addr >= 0 && addr < 0x400) {
+ // This has to be a nullptr-dereference, drop into debugger.
+ printf("Memory write to bad address: 0x%08" REGIx_FORMAT
+ " , pc=0x%08" PRIxPTR " \n",
+ addr, reinterpret_cast<intptr_t>(instr));
+ DieOrDebug();
+ }
+ double* ptr = reinterpret_cast<double*>(addr);
+ TraceMemWrDouble(addr, value.get_scalar());
+ memcpy(ptr, &value, 8);
+}
+
+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 v1 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_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 int64_t (*Prototype_Int_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_GeneralGeneralInt32General)(int32_t, int32_t,
+ int32_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_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 int64_t (*Prototype_Int64_General)(int64_t);
+typedef int64_t (*Prototype_Int64_GeneralInt64)(int64_t, int64_t);
+typedef int32_t (*Prototype_Int32_GeneralInt64Int64General)(int64_t, int64_t,
+ int64_t, int64_t);
+// Generated by Assembler::break_()/stop(), ebreak code is passed as immediate
+// field of a subsequent LUI instruction; otherwise returns -1
+static inline uint32_t get_ebreak_code(Instruction* instr) {
+ MOZ_ASSERT(instr->InstructionBits() == kBreakInstr);
+ uint8_t* cur = reinterpret_cast<uint8_t*>(instr);
+ Instruction* next_instr = reinterpret_cast<Instruction*>(cur + kInstrSize);
+ if (next_instr->BaseOpcodeFieldRaw() == LUI)
+ return (next_instr->Imm20UValue());
+ else
+ return -1;
+}
+
+// Software interrupt instructions are used by the simulator to call into C++.
+void Simulator::SoftwareInterrupt() {
+ // There are two instructions that could get us here, the ebreak or ecall
+ // instructions are "SYSTEM" class opcode distinuished by Imm12Value field w/
+ // the rest of instruction fields being zero
+ // We first check if we met a call_rt_redirected.
+ if (instr_.InstructionBits() == rtCallRedirInstr) {
+ Redirection* redirection = Redirection::FromSwiInstruction(instr_.instr());
+ uintptr_t nativeFn =
+ reinterpret_cast<uintptr_t>(redirection->nativeFunction());
+
+ intptr_t arg0 = getRegister(a0);
+ intptr_t arg1 = getRegister(a1);
+ intptr_t arg2 = getRegister(a2);
+ intptr_t arg3 = getRegister(a3);
+ intptr_t arg4 = getRegister(a4);
+ intptr_t arg5 = getRegister(a5);
+ intptr_t arg6 = getRegister(a6);
+ intptr_t arg7 = getRegister(a7);
+
+ // This is dodgy but it works because the C entry stubs are never moved.
+ // See comment in codegen-arm.cc and bug 1242173.
+ intptr_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);
+ }
+ if (FLAG_trace_sim) {
+ printf(
+ "Call to host function at %p with args %ld, %ld, %ld, %ld, %ld, %ld, "
+ "%ld, %ld\n",
+ reinterpret_cast<void*>(external), arg0, arg1, arg2, arg3, arg4, arg5,
+ arg6, arg7);
+ }
+ switch (redirection->type()) {
+ case Args_General0: {
+ Prototype_General0 target =
+ reinterpret_cast<Prototype_General0>(external);
+ int64_t result = target();
+ if (FLAG_trace_sim) printf("ret %ld\n", result);
+ setCallResult(result);
+ break;
+ }
+ case Args_General1: {
+ Prototype_General1 target =
+ reinterpret_cast<Prototype_General1>(external);
+ int64_t result = target(arg0);
+ if (FLAG_trace_sim) printf("ret %ld\n", result);
+ setCallResult(result);
+ break;
+ }
+ case Args_General2: {
+ Prototype_General2 target =
+ reinterpret_cast<Prototype_General2>(external);
+ int64_t result = target(arg0, arg1);
+ if (FLAG_trace_sim) printf("ret %ld\n", result);
+ setCallResult(result);
+ break;
+ }
+ case Args_General3: {
+ Prototype_General3 target =
+ reinterpret_cast<Prototype_General3>(external);
+ int64_t result = target(arg0, arg1, arg2);
+ if (FLAG_trace_sim) printf("ret %ld\n", result);
+ 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);
+ if (FLAG_trace_sim) printf("ret %ld\n", result);
+ setCallResult(result);
+ break;
+ }
+ case Args_General5: {
+ Prototype_General5 target =
+ reinterpret_cast<Prototype_General5>(external);
+ int64_t result = target(arg0, arg1, arg2, arg3, arg4);
+ if (FLAG_trace_sim) printf("ret %ld\n", result);
+ setCallResult(result);
+ break;
+ }
+ case Args_General6: {
+ Prototype_General6 target =
+ reinterpret_cast<Prototype_General6>(external);
+ int64_t result = target(arg0, arg1, arg2, arg3, arg4, arg5);
+ if (FLAG_trace_sim) printf("ret %ld\n", result);
+ setCallResult(result);
+ break;
+ }
+ case Args_General7: {
+ Prototype_General7 target =
+ reinterpret_cast<Prototype_General7>(external);
+ int64_t result = target(arg0, arg1, arg2, arg3, arg4, arg5, arg6);
+ if (FLAG_trace_sim) printf("ret %ld\n", result);
+ setCallResult(result);
+ break;
+ }
+ case Args_General8: {
+ Prototype_General8 target =
+ reinterpret_cast<Prototype_General8>(external);
+ int64_t result = target(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
+ if (FLAG_trace_sim) printf("ret %ld\n", result);
+ setCallResult(result);
+ break;
+ }
+ case Args_Double_None: {
+ Prototype_Double_None target =
+ reinterpret_cast<Prototype_Double_None>(external);
+ double dresult = target();
+ if (FLAG_trace_sim) printf("ret %f\n", dresult);
+ setCallResultDouble(dresult);
+ break;
+ }
+ case Args_Int_Double: {
+ double dval0 = getFpuRegisterDouble(fa0);
+ Prototype_Int_Double target =
+ reinterpret_cast<Prototype_Int_Double>(external);
+ int64_t result = target(dval0);
+ if (FLAG_trace_sim) printf("ret %ld\n", result);
+ 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 (FLAG_trace_sim) printf("ret %ld\n", result);
+ 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 (FLAG_trace_sim) printf("ret %ld\n", result);
+ 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(fa0);
+ Prototype_Int_DoubleInt target =
+ reinterpret_cast<Prototype_Int_DoubleInt>(external);
+ int64_t result = target(dval, arg0);
+ if (FLAG_trace_sim) printf("ret %ld\n", result);
+ setRegister(a0, result);
+ break;
+ }
+ case Args_Int_DoubleIntInt: {
+ double dval = getFpuRegisterDouble(fa0);
+ Prototype_Int_DoubleIntInt target =
+ reinterpret_cast<Prototype_Int_DoubleIntInt>(external);
+ int64_t result = target(dval, arg1, arg2);
+ if (FLAG_trace_sim) printf("ret %ld\n", result);
+ setRegister(a0, result);
+ break;
+ }
+ case Args_Int_IntDoubleIntInt: {
+ double dval = getFpuRegisterDouble(fa0);
+ Prototype_Int_IntDoubleIntInt target =
+ reinterpret_cast<Prototype_Int_IntDoubleIntInt>(external);
+ int64_t result = target(arg0, dval, arg2, arg3);
+ if (FLAG_trace_sim) printf("ret %ld\n", result);
+ setRegister(a0, result);
+ break;
+ }
+ case Args_Double_Double: {
+ double dval0 = getFpuRegisterDouble(fa0);
+ Prototype_Double_Double target =
+ reinterpret_cast<Prototype_Double_Double>(external);
+ double dresult = target(dval0);
+ if (FLAG_trace_sim) printf("ret %f\n", dresult);
+ setCallResultDouble(dresult);
+ break;
+ }
+ case Args_Float32_Float32: {
+ float fval0;
+ fval0 = getFpuRegisterFloat(fa0);
+ Prototype_Float32_Float32 target =
+ reinterpret_cast<Prototype_Float32_Float32>(external);
+ float fresult = target(fval0);
+ if (FLAG_trace_sim) printf("ret %f\n", fresult);
+ setCallResultFloat(fresult);
+ break;
+ }
+ case Args_Int_Float32: {
+ float fval0;
+ fval0 = getFpuRegisterFloat(fa0);
+ Prototype_Int_Float32 target =
+ reinterpret_cast<Prototype_Int_Float32>(external);
+ int64_t result = target(fval0);
+ if (FLAG_trace_sim) printf("ret %ld\n", result);
+ setRegister(a0, result);
+ break;
+ }
+ case Args_Float32_Float32Float32: {
+ float fval0;
+ float fval1;
+ fval0 = getFpuRegisterFloat(fa0);
+ fval1 = getFpuRegisterFloat(fa1);
+ Prototype_Float32_Float32Float32 target =
+ reinterpret_cast<Prototype_Float32_Float32Float32>(external);
+ float fresult = target(fval0, fval1);
+ if (FLAG_trace_sim) printf("ret %f\n", fresult);
+ setCallResultFloat(fresult);
+ break;
+ }
+ case Args_Double_Int: {
+ Prototype_Double_Int target =
+ reinterpret_cast<Prototype_Double_Int>(external);
+ double dresult = target(arg0);
+ if (FLAG_trace_sim) printf("ret %f\n", dresult);
+ setCallResultDouble(dresult);
+ break;
+ }
+ case Args_Double_DoubleInt: {
+ double dval0 = getFpuRegisterDouble(fa0);
+ Prototype_Double_DoubleInt target =
+ reinterpret_cast<Prototype_Double_DoubleInt>(external);
+ double dresult = target(dval0, arg0);
+ if (FLAG_trace_sim) printf("ret %f\n", dresult);
+ setCallResultDouble(dresult);
+ break;
+ }
+ case Args_Double_DoubleDouble: {
+ double dval0 = getFpuRegisterDouble(fa0);
+ double dval1 = getFpuRegisterDouble(fa1);
+ Prototype_Double_DoubleDouble target =
+ reinterpret_cast<Prototype_Double_DoubleDouble>(external);
+ double dresult = target(dval0, dval1);
+ if (FLAG_trace_sim) printf("ret %f\n", dresult);
+ setCallResultDouble(dresult);
+ break;
+ }
+ case Args_Double_IntDouble: {
+ double dval0 = getFpuRegisterDouble(fa0);
+ Prototype_Double_IntDouble target =
+ reinterpret_cast<Prototype_Double_IntDouble>(external);
+ double dresult = target(arg0, dval0);
+ if (FLAG_trace_sim) printf("ret %f\n", dresult);
+ setCallResultDouble(dresult);
+ break;
+ }
+ case Args_Int_IntDouble: {
+ double dval0 = getFpuRegisterDouble(fa0);
+ Prototype_Int_IntDouble target =
+ reinterpret_cast<Prototype_Int_IntDouble>(external);
+ int64_t result = target(arg0, dval0);
+ if (FLAG_trace_sim) printf("ret %ld\n", result);
+ setRegister(a0, result);
+ break;
+ }
+ case Args_Double_DoubleDoubleDouble: {
+ double dval0 = getFpuRegisterDouble(fa0);
+ double dval1 = getFpuRegisterDouble(fa1);
+ double dval2 = getFpuRegisterDouble(fa2);
+ Prototype_Double_DoubleDoubleDouble target =
+ reinterpret_cast<Prototype_Double_DoubleDoubleDouble>(external);
+ double dresult = target(dval0, dval1, dval2);
+ if (FLAG_trace_sim) printf("ret %f\n", dresult);
+ setCallResultDouble(dresult);
+ break;
+ }
+ case Args_Double_DoubleDoubleDoubleDouble: {
+ double dval0 = getFpuRegisterDouble(fa0);
+ double dval1 = getFpuRegisterDouble(fa1);
+ double dval2 = getFpuRegisterDouble(fa2);
+ double dval3 = getFpuRegisterDouble(fa3);
+ Prototype_Double_DoubleDoubleDoubleDouble target =
+ reinterpret_cast<Prototype_Double_DoubleDoubleDoubleDouble>(
+ external);
+ double dresult = target(dval0, dval1, dval2, dval3);
+ if (FLAG_trace_sim) printf("ret %f\n", dresult);
+ setCallResultDouble(dresult);
+ break;
+ }
+ case Args_Int32_General: {
+ int32_t ret = reinterpret_cast<Prototype_Int32_General>(nativeFn)(arg0);
+ if (FLAG_trace_sim) printf("ret %d\n", ret);
+ setRegister(a0, I64(ret));
+ break;
+ }
+ case Args_Int32_GeneralInt32: {
+ int32_t ret = reinterpret_cast<Prototype_Int32_GeneralInt32>(nativeFn)(
+ arg0, I32(arg1));
+ if (FLAG_trace_sim) printf("ret %d\n", ret);
+ setRegister(a0, I64(ret));
+ break;
+ }
+ case Args_Int32_GeneralInt32Int32: {
+ int32_t ret = reinterpret_cast<Prototype_Int32_GeneralInt32Int32>(
+ nativeFn)(arg0, I32(arg1), I32(arg2));
+ if (FLAG_trace_sim) printf("ret %d\n", ret);
+ 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));
+ if (FLAG_trace_sim) printf("ret %d\n", ret);
+ 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));
+ if (FLAG_trace_sim) printf("ret %d\n", ret);
+ 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);
+ if (FLAG_trace_sim) printf("ret %d\n", ret);
+ 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);
+ if (FLAG_trace_sim) printf("ret %d\n", ret);
+ setRegister(a0, I64(ret));
+ break;
+ }
+ case Args_Int32_GeneralInt32Int32Int64: {
+ int32_t ret = reinterpret_cast<Prototype_Int32_GeneralInt32Int32Int64>(
+ nativeFn)(arg0, I32(arg1), I32(arg2), arg3);
+ if (FLAG_trace_sim) printf("ret %d\n", ret);
+ setRegister(a0, I64(ret));
+ break;
+ }
+ case Args_Int32_GeneralInt32Int32General: {
+ int32_t ret =
+ reinterpret_cast<Prototype_Int32_GeneralInt32Int32General>(
+ nativeFn)(arg0, I32(arg1), I32(arg2), arg3);
+ if (FLAG_trace_sim) printf("ret %d\n", ret);
+ setRegister(a0, I64(ret));
+ break;
+ }
+ case Args_Int32_GeneralInt32Int64Int64: {
+ int32_t ret = reinterpret_cast<Prototype_Int32_GeneralInt32Int64Int64>(
+ nativeFn)(arg0, I32(arg1), arg2, arg3);
+ if (FLAG_trace_sim) printf("ret %d\n", ret);
+ setRegister(a0, I64(ret));
+ break;
+ }
+ case Args_Int32_GeneralInt32GeneralInt32: {
+ int32_t ret =
+ reinterpret_cast<Prototype_Int32_GeneralInt32GeneralInt32>(
+ nativeFn)(arg0, I32(arg1), arg2, I32(arg3));
+ if (FLAG_trace_sim) printf("ret %d\n", ret);
+ 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));
+ if (FLAG_trace_sim) printf("ret %d\n", ret);
+ setRegister(a0, I64(ret));
+ break;
+ }
+ case Args_Int32_GeneralGeneral: {
+ int32_t ret = reinterpret_cast<Prototype_Int32_GeneralGeneral>(
+ nativeFn)(arg0, arg1);
+ if (FLAG_trace_sim) printf("ret %d\n", ret);
+ setRegister(a0, I64(ret));
+ break;
+ }
+ case Args_Int32_GeneralGeneralGeneral: {
+ int32_t ret = reinterpret_cast<Prototype_Int32_GeneralGeneralGeneral>(
+ nativeFn)(arg0, arg1, arg2);
+ if (FLAG_trace_sim) printf("ret %d\n", ret);
+ setRegister(a0, I64(ret));
+ break;
+ }
+ case Args_Int32_GeneralGeneralInt32Int32: {
+ int32_t ret =
+ reinterpret_cast<Prototype_Int32_GeneralGeneralInt32Int32>(
+ nativeFn)(arg0, arg1, I32(arg2), I32(arg3));
+ if (FLAG_trace_sim) printf("ret %d\n", ret);
+ 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));
+ if (FLAG_trace_sim) printf("ret %d\n", ret);
+ setRegister(a0, I64(ret));
+ break;
+ }
+ case js::jit::Args_Int32_GeneralInt64Int32: {
+ int32_t ret = reinterpret_cast<Prototype_Int32_GeneralInt64Int32>(
+ nativeFn)(arg0, arg1, I32(arg2));
+ if (FLAG_trace_sim) printf("ret %d\n", ret);
+ 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);
+ if (FLAG_trace_sim) printf("ret %d\n", ret);
+ 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);
+ if (FLAG_trace_sim) printf("ret %d\n", ret);
+ setRegister(a0, I64(ret));
+ break;
+ }
+ case js::jit::Args_Int32_GeneralInt64Int64Int64: {
+ int32_t ret = reinterpret_cast<Prototype_Int32_GeneralInt64Int64Int64>(
+ nativeFn)(arg0, arg1, arg2, arg3);
+ if (FLAG_trace_sim) printf("ret %d\n", ret);
+ 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);
+ if (FLAG_trace_sim) printf("ret %d\n", ret);
+ setRegister(a0, I64(ret));
+ break;
+ }
+ case Args_General_GeneralInt32: {
+ int64_t ret = reinterpret_cast<Prototype_General_GeneralInt32>(
+ nativeFn)(arg0, I32(arg1));
+ if (FLAG_trace_sim) printf("ret %ld\n", ret);
+ setRegister(a0, ret);
+ break;
+ }
+ case Args_General_GeneralInt32Int32: {
+ int64_t ret = reinterpret_cast<Prototype_General_GeneralInt32Int32>(
+ nativeFn)(arg0, I32(arg1), I32(arg2));
+ if (FLAG_trace_sim) printf("ret %ld\n", ret);
+ setRegister(a0, ret);
+ break;
+ }
+ case Args_General_GeneralInt32General: {
+ int64_t ret = reinterpret_cast<Prototype_General_GeneralInt32General>(
+ nativeFn)(arg0, I32(arg1), arg2);
+ if (FLAG_trace_sim) printf("ret %ld\n", ret);
+ 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_GeneralGeneralInt32General: {
+ Prototype_Int32_GeneralGeneralInt32General target =
+ reinterpret_cast<Prototype_Int32_GeneralGeneralInt32General>(
+ external);
+ int64_t result = target(I32(arg0), I32(arg1), I32(arg2), I32(arg3));
+ setRegister(a0, I64(result));
+ 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_Int64_General: {
+ int64_t ret = reinterpret_cast<Prototype_Int64_General>(nativeFn)(arg0);
+ if (FLAG_trace_sim) printf("ret %ld\n", ret);
+ setRegister(a0, ret);
+ break;
+ }
+ case js::jit::Args_Int64_GeneralInt64: {
+ int64_t ret = reinterpret_cast<Prototype_Int64_GeneralInt64>(nativeFn)(
+ arg0, arg1);
+ if (FLAG_trace_sim) printf("ret %ld\n", ret);
+ setRegister(a0, 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;
+ }
+ 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_.InstructionBits() == kBreakInstr &&
+ (get_ebreak_code(instr_.instr()) <= kMaxStopCode)) {
+ uint32_t code = get_ebreak_code(instr_.instr());
+ if (isWatchpoint(code)) {
+ printWatchpoint(code);
+ } else if (IsTracepoint(code)) {
+ if (!FLAG_debug_sim) {
+ MOZ_CRASH("Add --debug-sim when tracepoint instruction is used.\n");
+ }
+ // printf("%d %d %d %d %d %d %d\n", code, code & LOG_TRACE, code &
+ // LOG_REGS,
+ // code & kDebuggerTracingDirectivesMask, TRACE_ENABLE,
+ // TRACE_DISABLE, kDebuggerTracingDirectivesMask);
+ switch (code & kDebuggerTracingDirectivesMask) {
+ case TRACE_ENABLE:
+ if (code & LOG_TRACE) {
+ FLAG_trace_sim = true;
+ }
+ if (code & LOG_REGS) {
+ RiscvDebugger dbg(this);
+ dbg.printAllRegs();
+ }
+ break;
+ case TRACE_DISABLE:
+ if (code & LOG_TRACE) {
+ FLAG_trace_sim = false;
+ }
+ break;
+ default:
+ UNREACHABLE();
+ }
+ } else {
+ increaseStopCounter(code);
+ handleStop(code);
+ }
+ } else {
+ // uint8_t code = get_ebreak_code(instr_.instr()) - kMaxStopCode - 1;
+ // switch (LNode::Opcode(code)) {
+ // #define EMIT_OP(OP, ...) \
+// case LNode::Opcode::OP:\
+// std::cout << #OP << std::endl; \
+// break;
+ // LIR_OPCODE_LIST(EMIT_OP);
+ // #undef EMIT_OP
+ // }
+ DieOrDebug();
+ }
+}
+
+// Stop helper functions.
+bool Simulator::isWatchpoint(uint32_t code) {
+ return (code <= kMaxWatchpointCode);
+}
+
+bool Simulator::IsTracepoint(uint32_t code) {
+ return (code <= kMaxTracepointCode && code > kMaxWatchpointCode);
+}
+
+void Simulator::printWatchpoint(uint32_t code) {
+ RiscvDebugger dbg(this);
+ ++break_count_;
+ if (FLAG_riscv_print_watchpoint) {
+ 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) {
+ // Stop if it is enabled, otherwise go on jumping over the stop
+ // and the message address.
+ if (isEnabledStop(code)) {
+ RiscvDebugger dbg(this);
+ dbg.Debug();
+ } else {
+ set_pc(get_pc() + 2 * kInstrSize);
+ }
+}
+
+bool Simulator::isStopInstruction(SimInstruction* instr) {
+ if (instr->InstructionBits() != kBreakInstr) return false;
+ int32_t code = get_ebreak_code(instr->instr());
+ return code != -1 && static_cast<uint32_t>(code) > kMaxWatchpointCode &&
+ static_cast<uint32_t>(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::SignalException(Exception e) {
+ printf("Error: Exception %i raised.", static_cast<int>(e));
+ MOZ_CRASH();
+}
+
+// TODO(plind): refactor this messy debug code when we do unaligned access.
+void Simulator::DieOrDebug() {
+ if (FLAG_riscv_trap_to_simulator_debugger) {
+ RiscvDebugger dbg(this);
+ dbg.Debug();
+ } else {
+ MOZ_CRASH("Die");
+ }
+}
+
+// Executes the current instruction.
+void Simulator::InstructionDecode(Instruction* instr) {
+ // if (FLAG_check_icache) {
+ // CheckICache(SimulatorProcess::icache(), instr);
+ // }
+ pc_modified_ = false;
+
+ EmbeddedVector<char, 256> buffer;
+
+ if (FLAG_trace_sim || FLAG_debug_sim) {
+ SNPrintF(trace_buf_, " ");
+ disasm::NameConverter converter;
+ disasm::Disassembler dasm(converter);
+ // Use a reasonably large buffer.
+ dasm.InstructionDecode(buffer, reinterpret_cast<byte*>(instr));
+
+ // printf("EXECUTING 0x%08" PRIxPTR " %-44s\n",
+ // reinterpret_cast<intptr_t>(instr), buffer.begin());
+ }
+
+ instr_ = instr;
+ switch (instr_.InstructionType()) {
+ case Instruction::kRType:
+ DecodeRVRType();
+ break;
+ case Instruction::kR4Type:
+ DecodeRVR4Type();
+ break;
+ case Instruction::kIType:
+ DecodeRVIType();
+ break;
+ case Instruction::kSType:
+ DecodeRVSType();
+ break;
+ case Instruction::kBType:
+ DecodeRVBType();
+ break;
+ case Instruction::kUType:
+ DecodeRVUType();
+ break;
+ case Instruction::kJType:
+ DecodeRVJType();
+ break;
+ case Instruction::kCRType:
+ DecodeCRType();
+ break;
+ case Instruction::kCAType:
+ DecodeCAType();
+ break;
+ case Instruction::kCJType:
+ DecodeCJType();
+ break;
+ case Instruction::kCBType:
+ DecodeCBType();
+ break;
+ case Instruction::kCIType:
+ DecodeCIType();
+ break;
+ case Instruction::kCIWType:
+ DecodeCIWType();
+ break;
+ case Instruction::kCSSType:
+ DecodeCSSType();
+ break;
+ case Instruction::kCLType:
+ DecodeCLType();
+ break;
+ case Instruction::kCSType:
+ DecodeCSType();
+ break;
+# ifdef CAN_USE_RVV_INSTRUCTIONS
+ case Instruction::kVType:
+ DecodeVType();
+ break;
+# endif
+ default:
+ UNSUPPORTED();
+ }
+
+ if (FLAG_trace_sim) {
+ printf(" 0x%012" PRIxPTR " %-44s\t%s\n",
+ reinterpret_cast<intptr_t>(instr), buffer.start(),
+ trace_buf_.start());
+ }
+
+ if (!pc_modified_) {
+ setRegister(pc, reinterpret_cast<sreg_t>(instr) + instr->InstructionSize());
+ }
+
+ if (watch_address_ != nullptr) {
+ printf(" 0x%012" PRIxPTR " : 0x%016" REGIx_FORMAT " %14" REGId_FORMAT
+ " \n",
+ reinterpret_cast<intptr_t>(watch_address_), *watch_address_,
+ *watch_address_);
+ if (watch_value_ != *watch_address_) {
+ RiscvDebugger dbg(this);
+ dbg.Debug();
+ watch_value_ = *watch_address_;
+ }
+ }
+}
+
+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)) {
+ RiscvDebugger dbg(this);
+ dbg.Debug();
+ }
+ if (single_stepping_) {
+ single_step_callback_(single_step_callback_arg_, this,
+ (void*)program_counter);
+ }
+ Instruction* instr = reinterpret_cast<Instruction*>(program_counter);
+ InstructionDecode(instr);
+ icount_++;
+ program_counter = get_pc();
+ }
+
+ if (single_stepping_) {
+ single_step_callback_(single_step_callback_arg_, this, nullptr);
+ }
+}
+
+// RISCV Instruction Decode Routine
+void Simulator::DecodeRVRType() {
+ switch (instr_.InstructionBits() & kRTypeMask) {
+ case RO_ADD: {
+ set_rd(sext_xlen(rs1() + rs2()));
+ break;
+ }
+ case RO_SUB: {
+ set_rd(sext_xlen(rs1() - rs2()));
+ break;
+ }
+ case RO_SLL: {
+ set_rd(sext_xlen(rs1() << (rs2() & (xlen - 1))));
+ break;
+ }
+ case RO_SLT: {
+ set_rd(sreg_t(rs1()) < sreg_t(rs2()));
+ break;
+ }
+ case RO_SLTU: {
+ set_rd(reg_t(rs1()) < reg_t(rs2()));
+ break;
+ }
+ case RO_XOR: {
+ set_rd(rs1() ^ rs2());
+ break;
+ }
+ case RO_SRL: {
+ set_rd(sext_xlen(zext_xlen(rs1()) >> (rs2() & (xlen - 1))));
+ break;
+ }
+ case RO_SRA: {
+ set_rd(sext_xlen(sext_xlen(rs1()) >> (rs2() & (xlen - 1))));
+ break;
+ }
+ case RO_OR: {
+ set_rd(rs1() | rs2());
+ break;
+ }
+ case RO_AND: {
+ set_rd(rs1() & rs2());
+ break;
+ }
+# ifdef JS_CODEGEN_RISCV64
+ case RO_ADDW: {
+ set_rd(sext32(rs1() + rs2()));
+ break;
+ }
+ case RO_SUBW: {
+ set_rd(sext32(rs1() - rs2()));
+ break;
+ }
+ case RO_SLLW: {
+ set_rd(sext32(rs1() << (rs2() & 0x1F)));
+ break;
+ }
+ case RO_SRLW: {
+ set_rd(sext32(uint32_t(rs1()) >> (rs2() & 0x1F)));
+ break;
+ }
+ case RO_SRAW: {
+ set_rd(sext32(int32_t(rs1()) >> (rs2() & 0x1F)));
+ break;
+ }
+# endif /* JS_CODEGEN_RISCV64 */
+ // TODO(riscv): Add RISCV M extension macro
+ case RO_MUL: {
+ set_rd(rs1() * rs2());
+ break;
+ }
+ case RO_MULH: {
+ set_rd(mulh(rs1(), rs2()));
+ break;
+ }
+ case RO_MULHSU: {
+ set_rd(mulhsu(rs1(), rs2()));
+ break;
+ }
+ case RO_MULHU: {
+ set_rd(mulhu(rs1(), rs2()));
+ break;
+ }
+ case RO_DIV: {
+ sreg_t lhs = sext_xlen(rs1());
+ sreg_t rhs = sext_xlen(rs2());
+ if (rhs == 0) {
+ set_rd(-1);
+ } else if (lhs == INTPTR_MIN && rhs == -1) {
+ set_rd(lhs);
+ } else {
+ set_rd(sext_xlen(lhs / rhs));
+ }
+ break;
+ }
+ case RO_DIVU: {
+ reg_t lhs = zext_xlen(rs1());
+ reg_t rhs = zext_xlen(rs2());
+ if (rhs == 0) {
+ set_rd(UINTPTR_MAX);
+ } else {
+ set_rd(zext_xlen(lhs / rhs));
+ }
+ break;
+ }
+ case RO_REM: {
+ sreg_t lhs = sext_xlen(rs1());
+ sreg_t rhs = sext_xlen(rs2());
+ if (rhs == 0) {
+ set_rd(lhs);
+ } else if (lhs == INTPTR_MIN && rhs == -1) {
+ set_rd(0);
+ } else {
+ set_rd(sext_xlen(lhs % rhs));
+ }
+ break;
+ }
+ case RO_REMU: {
+ reg_t lhs = zext_xlen(rs1());
+ reg_t rhs = zext_xlen(rs2());
+ if (rhs == 0) {
+ set_rd(lhs);
+ } else {
+ set_rd(zext_xlen(lhs % rhs));
+ }
+ break;
+ }
+# ifdef JS_CODEGEN_RISCV64
+ case RO_MULW: {
+ set_rd(sext32(sext32(rs1()) * sext32(rs2())));
+ break;
+ }
+ case RO_DIVW: {
+ sreg_t lhs = sext32(rs1());
+ sreg_t rhs = sext32(rs2());
+ if (rhs == 0) {
+ set_rd(-1);
+ } else if (lhs == INT32_MIN && rhs == -1) {
+ set_rd(lhs);
+ } else {
+ set_rd(sext32(lhs / rhs));
+ }
+ break;
+ }
+ case RO_DIVUW: {
+ reg_t lhs = zext32(rs1());
+ reg_t rhs = zext32(rs2());
+ if (rhs == 0) {
+ set_rd(UINT32_MAX);
+ } else {
+ set_rd(zext32(lhs / rhs));
+ }
+ break;
+ }
+ case RO_REMW: {
+ sreg_t lhs = sext32(rs1());
+ sreg_t rhs = sext32(rs2());
+ if (rhs == 0) {
+ set_rd(lhs);
+ } else if (lhs == INT32_MIN && rhs == -1) {
+ set_rd(0);
+ } else {
+ set_rd(sext32(lhs % rhs));
+ }
+ break;
+ }
+ case RO_REMUW: {
+ reg_t lhs = zext32(rs1());
+ reg_t rhs = zext32(rs2());
+ if (rhs == 0) {
+ set_rd(zext32(lhs));
+ } else {
+ set_rd(zext32(lhs % rhs));
+ }
+ break;
+ }
+# endif /*JS_CODEGEN_RISCV64*/
+ // TODO(riscv): End Add RISCV M extension macro
+ default: {
+ switch (instr_.BaseOpcode()) {
+ case AMO:
+ DecodeRVRAType();
+ break;
+ case OP_FP:
+ DecodeRVRFPType();
+ break;
+ default:
+ UNSUPPORTED();
+ }
+ }
+ }
+}
+
+template <typename T>
+T Simulator::FMaxMinHelper(T a, T b, MaxMinKind kind) {
+ // set invalid bit for signaling nan
+ if ((a == std::numeric_limits<T>::signaling_NaN()) ||
+ (b == std::numeric_limits<T>::signaling_NaN())) {
+ set_csr_bits(csr_fflags, kInvalidOperation);
+ }
+
+ T result = 0;
+ if (std::isnan(a) && std::isnan(b)) {
+ result = std::numeric_limits<float>::quiet_NaN();
+ } else if (std::isnan(a)) {
+ result = b;
+ } else if (std::isnan(b)) {
+ result = a;
+ } else if (b == a) { // Handle -0.0 == 0.0 case.
+ if (kind == MaxMinKind::kMax) {
+ result = std::signbit(b) ? a : b;
+ } else {
+ result = std::signbit(b) ? b : a;
+ }
+ } else {
+ result = (kind == MaxMinKind::kMax) ? fmax(a, b) : fmin(a, b);
+ }
+
+ return result;
+}
+
+float Simulator::RoundF2FHelper(float input_val, int rmode) {
+ if (rmode == DYN) rmode = get_dynamic_rounding_mode();
+
+ float rounded = 0;
+ switch (rmode) {
+ case RNE: { // Round to Nearest, tiest to Even
+ rounded = floorf(input_val);
+ float error = input_val - rounded;
+
+ // Take care of correctly handling the range [-0.5, -0.0], which must
+ // yield -0.0.
+ if ((-0.5 <= input_val) && (input_val < 0.0)) {
+ rounded = -0.0;
+
+ // If the error is greater than 0.5, or is equal to 0.5 and the integer
+ // result is odd, round up.
+ } else if ((error > 0.5) ||
+ ((error == 0.5) && (std::fmod(rounded, 2) != 0))) {
+ rounded++;
+ }
+ break;
+ }
+ case RTZ: // Round towards Zero
+ rounded = std::truncf(input_val);
+ break;
+ case RDN: // Round Down (towards -infinity)
+ rounded = floorf(input_val);
+ break;
+ case RUP: // Round Up (towards +infinity)
+ rounded = ceilf(input_val);
+ break;
+ case RMM: // Round to Nearest, tiest to Max Magnitude
+ rounded = std::roundf(input_val);
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ return rounded;
+}
+
+double Simulator::RoundF2FHelper(double input_val, int rmode) {
+ if (rmode == DYN) rmode = get_dynamic_rounding_mode();
+
+ double rounded = 0;
+ switch (rmode) {
+ case RNE: { // Round to Nearest, tiest to Even
+ rounded = std::floor(input_val);
+ double error = input_val - rounded;
+
+ // Take care of correctly handling the range [-0.5, -0.0], which must
+ // yield -0.0.
+ if ((-0.5 <= input_val) && (input_val < 0.0)) {
+ rounded = -0.0;
+
+ // If the error is greater than 0.5, or is equal to 0.5 and the integer
+ // result is odd, round up.
+ } else if ((error > 0.5) ||
+ ((error == 0.5) && (std::fmod(rounded, 2) != 0))) {
+ rounded++;
+ }
+ break;
+ }
+ case RTZ: // Round towards Zero
+ rounded = std::trunc(input_val);
+ break;
+ case RDN: // Round Down (towards -infinity)
+ rounded = std::floor(input_val);
+ break;
+ case RUP: // Round Up (towards +infinity)
+ rounded = std::ceil(input_val);
+ break;
+ case RMM: // Round to Nearest, tiest to Max Magnitude
+ rounded = std::round(input_val);
+ break;
+ default:
+ UNREACHABLE();
+ }
+ return rounded;
+}
+
+// convert rounded floating-point to integer types, handle input values that
+// are out-of-range, underflow, or NaN, and set appropriate fflags
+template <typename I_TYPE, typename F_TYPE>
+I_TYPE Simulator::RoundF2IHelper(F_TYPE original, int rmode) {
+ MOZ_ASSERT(std::is_integral<I_TYPE>::value);
+
+ MOZ_ASSERT((std::is_same<F_TYPE, float>::value ||
+ std::is_same<F_TYPE, double>::value));
+
+ I_TYPE max_i = std::numeric_limits<I_TYPE>::max();
+ I_TYPE min_i = std::numeric_limits<I_TYPE>::min();
+
+ if (!std::isfinite(original)) {
+ set_fflags(kInvalidOperation);
+ if (std::isnan(original) ||
+ original == std::numeric_limits<F_TYPE>::infinity()) {
+ return max_i;
+ } else {
+ MOZ_ASSERT(original == -std::numeric_limits<F_TYPE>::infinity());
+ return min_i;
+ }
+ }
+
+ F_TYPE rounded = RoundF2FHelper(original, rmode);
+ if (original != rounded) set_fflags(kInexact);
+
+ if (!std::isfinite(rounded)) {
+ set_fflags(kInvalidOperation);
+ if (std::isnan(rounded) ||
+ rounded == std::numeric_limits<F_TYPE>::infinity()) {
+ return max_i;
+ } else {
+ MOZ_ASSERT(rounded == -std::numeric_limits<F_TYPE>::infinity());
+ return min_i;
+ }
+ }
+
+ // Since integer max values are either all 1s (for unsigned) or all 1s
+ // except for sign-bit (for signed), they cannot be represented precisely in
+ // floating point, in order to precisely tell whether the rounded floating
+ // point is within the max range, we compare against (max_i+1) which would
+ // have a single 1 w/ many trailing zeros
+ float max_i_plus_1 =
+ std::is_same<uint64_t, I_TYPE>::value
+ ? 0x1p64f // uint64_t::max + 1 cannot be represented in integers,
+ // so use its float representation directly
+ : static_cast<float>(static_cast<uint64_t>(max_i) + 1);
+ if (rounded >= max_i_plus_1) {
+ set_fflags(kOverflow | kInvalidOperation);
+ return max_i;
+ }
+
+ // Since min_i (either 0 for unsigned, or for signed) is represented
+ // precisely in floating-point, comparing rounded directly against min_i
+ if (rounded <= min_i) {
+ if (rounded < min_i) set_fflags(kOverflow | kInvalidOperation);
+ return min_i;
+ }
+
+ F_TYPE underflow_fval =
+ std::is_same<F_TYPE, float>::value ? FLT_MIN : DBL_MIN;
+ if (rounded < underflow_fval && rounded > -underflow_fval && rounded != 0) {
+ set_fflags(kUnderflow);
+ }
+
+ return static_cast<I_TYPE>(rounded);
+}
+
+template <typename T>
+static int64_t FclassHelper(T value) {
+ switch (std::fpclassify(value)) {
+ case FP_INFINITE:
+ return (std::signbit(value) ? kNegativeInfinity : kPositiveInfinity);
+ case FP_NAN:
+ return (isSnan(value) ? kSignalingNaN : kQuietNaN);
+ case FP_NORMAL:
+ return (std::signbit(value) ? kNegativeNormalNumber
+ : kPositiveNormalNumber);
+ case FP_SUBNORMAL:
+ return (std::signbit(value) ? kNegativeSubnormalNumber
+ : kPositiveSubnormalNumber);
+ case FP_ZERO:
+ return (std::signbit(value) ? kNegativeZero : kPositiveZero);
+ default:
+ UNREACHABLE();
+ }
+ UNREACHABLE();
+ return FP_ZERO;
+}
+
+template <typename T>
+bool Simulator::CompareFHelper(T input1, T input2, FPUCondition cc) {
+ MOZ_ASSERT(std::is_floating_point<T>::value);
+ bool result = false;
+ switch (cc) {
+ case LT:
+ case LE:
+ // FLT, FLE are signaling compares
+ if (std::isnan(input1) || std::isnan(input2)) {
+ set_fflags(kInvalidOperation);
+ result = false;
+ } else {
+ result = (cc == LT) ? (input1 < input2) : (input1 <= input2);
+ }
+ break;
+ case EQ:
+ if (std::numeric_limits<T>::signaling_NaN() == input1 ||
+ std::numeric_limits<T>::signaling_NaN() == input2) {
+ set_fflags(kInvalidOperation);
+ }
+ if (std::isnan(input1) || std::isnan(input2)) {
+ result = false;
+ } else {
+ result = (input1 == input2);
+ }
+ break;
+ case NE:
+ if (std::numeric_limits<T>::signaling_NaN() == input1 ||
+ std::numeric_limits<T>::signaling_NaN() == input2) {
+ set_fflags(kInvalidOperation);
+ }
+ if (std::isnan(input1) || std::isnan(input2)) {
+ result = true;
+ } else {
+ result = (input1 != input2);
+ }
+ break;
+ default:
+ UNREACHABLE();
+ }
+ return result;
+}
+
+template <typename T>
+static inline bool is_invalid_fmul(T src1, T src2) {
+ return (isinf(src1) && src2 == static_cast<T>(0.0)) ||
+ (src1 == static_cast<T>(0.0) && isinf(src2));
+}
+
+template <typename T>
+static inline bool is_invalid_fadd(T src1, T src2) {
+ return (isinf(src1) && isinf(src2) &&
+ std::signbit(src1) != std::signbit(src2));
+}
+
+template <typename T>
+static inline bool is_invalid_fsub(T src1, T src2) {
+ return (isinf(src1) && isinf(src2) &&
+ std::signbit(src1) == std::signbit(src2));
+}
+
+template <typename T>
+static inline bool is_invalid_fdiv(T src1, T src2) {
+ return ((src1 == 0 && src2 == 0) || (isinf(src1) && isinf(src2)));
+}
+
+template <typename T>
+static inline bool is_invalid_fsqrt(T src1) {
+ return (src1 < 0);
+}
+
+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 1;
+ }
+
+ LLBit_ = false;
+ LLAddr_ = 0;
+ int32_t expected = int32_t(lastLLValue_);
+ int32_t old =
+ AtomicOperations::compareExchangeSeqCst(ptr, expected, int32_t(value));
+ return (old == expected) ? 0 : 1;
+ }
+ 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 1;
+ }
+
+ LLBit_ = false;
+ LLAddr_ = 0;
+ int64_t expected = lastLLValue_;
+ int64_t old =
+ AtomicOperations::compareExchangeSeqCst(ptr, expected, int64_t(value));
+ return (old == expected) ? 0 : 1;
+ }
+ printf("Unaligned SC at 0x%016" PRIx64 ", pc=0x%016" PRIxPTR "\n", addr,
+ reinterpret_cast<intptr_t>(instr));
+ MOZ_CRASH();
+ return 0;
+}
+
+void Simulator::DecodeRVRAType() {
+ // TODO(riscv): Add macro for RISCV A extension
+ // Special handling for A extension instructions because it uses func5
+ // For all A extension instruction, V8 simulator is pure sequential. No
+ // Memory address lock or other synchronizaiton behaviors.
+ switch (instr_.InstructionBits() & kRATypeMask) {
+ case RO_LR_W: {
+ sreg_t addr = rs1();
+ set_rd(loadLinkedW(addr, &instr_));
+ TraceLr(addr, getRegister(rd_reg()), getRegister(rd_reg()));
+ break;
+ }
+ case RO_SC_W: {
+ sreg_t addr = rs1();
+ auto value = static_cast<int32_t>(rs2());
+ auto result =
+ storeConditionalW(addr, static_cast<int32_t>(rs2()), &instr_);
+ set_rd(result);
+ if (!result) {
+ TraceSc(addr, value);
+ }
+ break;
+ }
+ case RO_AMOSWAP_W: {
+ if ((rs1() & 0x3) != 0) {
+ DieOrDebug();
+ }
+ set_rd(sext32(amo<uint32_t>(
+ rs1(), [&](uint32_t lhs) { return (uint32_t)rs2(); }, instr_.instr(),
+ WORD)));
+ break;
+ }
+ case RO_AMOADD_W: {
+ if ((rs1() & 0x3) != 0) {
+ DieOrDebug();
+ }
+ set_rd(sext32(amo<uint32_t>(
+ rs1(), [&](uint32_t lhs) { return lhs + (uint32_t)rs2(); },
+ instr_.instr(), WORD)));
+ break;
+ }
+ case RO_AMOXOR_W: {
+ if ((rs1() & 0x3) != 0) {
+ DieOrDebug();
+ }
+ set_rd(sext32(amo<uint32_t>(
+ rs1(), [&](uint32_t lhs) { return lhs ^ (uint32_t)rs2(); },
+ instr_.instr(), WORD)));
+ break;
+ }
+ case RO_AMOAND_W: {
+ if ((rs1() & 0x3) != 0) {
+ DieOrDebug();
+ }
+ set_rd(sext32(amo<uint32_t>(
+ rs1(), [&](uint32_t lhs) { return lhs & (uint32_t)rs2(); },
+ instr_.instr(), WORD)));
+ break;
+ }
+ case RO_AMOOR_W: {
+ if ((rs1() & 0x3) != 0) {
+ DieOrDebug();
+ }
+ set_rd(sext32(amo<uint32_t>(
+ rs1(), [&](uint32_t lhs) { return lhs | (uint32_t)rs2(); },
+ instr_.instr(), WORD)));
+ break;
+ }
+ case RO_AMOMIN_W: {
+ if ((rs1() & 0x3) != 0) {
+ DieOrDebug();
+ }
+ set_rd(sext32(amo<int32_t>(
+ rs1(), [&](int32_t lhs) { return std::min(lhs, (int32_t)rs2()); },
+ instr_.instr(), WORD)));
+ break;
+ }
+ case RO_AMOMAX_W: {
+ if ((rs1() & 0x3) != 0) {
+ DieOrDebug();
+ }
+ set_rd(sext32(amo<int32_t>(
+ rs1(), [&](int32_t lhs) { return std::max(lhs, (int32_t)rs2()); },
+ instr_.instr(), WORD)));
+ break;
+ }
+ case RO_AMOMINU_W: {
+ if ((rs1() & 0x3) != 0) {
+ DieOrDebug();
+ }
+ set_rd(sext32(amo<uint32_t>(
+ rs1(), [&](uint32_t lhs) { return std::min(lhs, (uint32_t)rs2()); },
+ instr_.instr(), WORD)));
+ break;
+ }
+ case RO_AMOMAXU_W: {
+ if ((rs1() & 0x3) != 0) {
+ DieOrDebug();
+ }
+ set_rd(sext32(amo<uint32_t>(
+ rs1(), [&](uint32_t lhs) { return std::max(lhs, (uint32_t)rs2()); },
+ instr_.instr(), WORD)));
+ break;
+ }
+# ifdef JS_CODEGEN_RISCV64
+ case RO_LR_D: {
+ sreg_t addr = rs1();
+ set_rd(loadLinkedD(addr, &instr_));
+ TraceLr(addr, getRegister(rd_reg()), getRegister(rd_reg()));
+ break;
+ }
+ case RO_SC_D: {
+ sreg_t addr = rs1();
+ auto value = static_cast<int64_t>(rs2());
+ auto result =
+ storeConditionalD(addr, static_cast<int64_t>(rs2()), &instr_);
+ set_rd(result);
+ if (!result) {
+ TraceSc(addr, value);
+ }
+ break;
+ }
+ case RO_AMOSWAP_D: {
+ set_rd(amo<int64_t>(
+ rs1(), [&](int64_t lhs) { return rs2(); }, instr_.instr(), DWORD));
+ break;
+ }
+ case RO_AMOADD_D: {
+ set_rd(amo<int64_t>(
+ rs1(), [&](int64_t lhs) { return lhs + rs2(); }, instr_.instr(),
+ DWORD));
+ break;
+ }
+ case RO_AMOXOR_D: {
+ set_rd(amo<int64_t>(
+ rs1(), [&](int64_t lhs) { return lhs ^ rs2(); }, instr_.instr(),
+ DWORD));
+ break;
+ }
+ case RO_AMOAND_D: {
+ set_rd(amo<int64_t>(
+ rs1(), [&](int64_t lhs) { return lhs & rs2(); }, instr_.instr(),
+ DWORD));
+ break;
+ }
+ case RO_AMOOR_D: {
+ set_rd(amo<int64_t>(
+ rs1(), [&](int64_t lhs) { return lhs | rs2(); }, instr_.instr(),
+ DWORD));
+ break;
+ }
+ case RO_AMOMIN_D: {
+ set_rd(amo<int64_t>(
+ rs1(), [&](int64_t lhs) { return std::min(lhs, rs2()); },
+ instr_.instr(), DWORD));
+ break;
+ }
+ case RO_AMOMAX_D: {
+ set_rd(amo<int64_t>(
+ rs1(), [&](int64_t lhs) { return std::max(lhs, rs2()); },
+ instr_.instr(), DWORD));
+ break;
+ }
+ case RO_AMOMINU_D: {
+ set_rd(amo<uint64_t>(
+ rs1(), [&](uint64_t lhs) { return std::min(lhs, (uint64_t)rs2()); },
+ instr_.instr(), DWORD));
+ break;
+ }
+ case RO_AMOMAXU_D: {
+ set_rd(amo<uint64_t>(
+ rs1(), [&](uint64_t lhs) { return std::max(lhs, (uint64_t)rs2()); },
+ instr_.instr(), DWORD));
+ break;
+ }
+# endif /*JS_CODEGEN_RISCV64*/
+ // TODO(riscv): End Add macro for RISCV A extension
+ default: {
+ UNSUPPORTED();
+ }
+ }
+}
+
+void Simulator::DecodeRVRFPType() {
+ // OP_FP instructions (F/D) uses func7 first. Some further uses func3 and
+ // rs2()
+
+ // kRATypeMask is only for func7
+ switch (instr_.InstructionBits() & kRFPTypeMask) {
+ // TODO(riscv): Add macro for RISCV F extension
+ case RO_FADD_S: {
+ // TODO(riscv): use rm value (round mode)
+ auto fn = [this](float frs1, float frs2) {
+ if (is_invalid_fadd(frs1, frs2)) {
+ this->set_fflags(kInvalidOperation);
+ return std::numeric_limits<float>::quiet_NaN();
+ } else {
+ return frs1 + frs2;
+ }
+ };
+ set_frd(CanonicalizeFPUOp2<float>(fn));
+ break;
+ }
+ case RO_FSUB_S: {
+ // TODO(riscv): use rm value (round mode)
+ auto fn = [this](float frs1, float frs2) {
+ if (is_invalid_fsub(frs1, frs2)) {
+ this->set_fflags(kInvalidOperation);
+ return std::numeric_limits<float>::quiet_NaN();
+ } else {
+ return frs1 - frs2;
+ }
+ };
+ set_frd(CanonicalizeFPUOp2<float>(fn));
+ break;
+ }
+ case RO_FMUL_S: {
+ // TODO(riscv): use rm value (round mode)
+ auto fn = [this](float frs1, float frs2) {
+ if (is_invalid_fmul(frs1, frs2)) {
+ this->set_fflags(kInvalidOperation);
+ return std::numeric_limits<float>::quiet_NaN();
+ } else {
+ return frs1 * frs2;
+ }
+ };
+ set_frd(CanonicalizeFPUOp2<float>(fn));
+ break;
+ }
+ case RO_FDIV_S: {
+ // TODO(riscv): use rm value (round mode)
+ auto fn = [this](float frs1, float frs2) {
+ if (is_invalid_fdiv(frs1, frs2)) {
+ this->set_fflags(kInvalidOperation);
+ return std::numeric_limits<float>::quiet_NaN();
+ } else if (frs2 == 0.0f) {
+ this->set_fflags(kDivideByZero);
+ return (std::signbit(frs1) == std::signbit(frs2)
+ ? std::numeric_limits<float>::infinity()
+ : -std::numeric_limits<float>::infinity());
+ } else {
+ return frs1 / frs2;
+ }
+ };
+ set_frd(CanonicalizeFPUOp2<float>(fn));
+ break;
+ }
+ case RO_FSQRT_S: {
+ if (instr_.Rs2Value() == 0b00000) {
+ // TODO(riscv): use rm value (round mode)
+ auto fn = [this](float frs) {
+ if (is_invalid_fsqrt(frs)) {
+ this->set_fflags(kInvalidOperation);
+ return std::numeric_limits<float>::quiet_NaN();
+ } else {
+ return std::sqrt(frs);
+ }
+ };
+ set_frd(CanonicalizeFPUOp1<float>(fn));
+ } else {
+ UNSUPPORTED();
+ }
+ break;
+ }
+ case RO_FSGNJ_S: { // RO_FSGNJN_S RO_FSQNJX_S
+ switch (instr_.Funct3Value()) {
+ case 0b000: { // RO_FSGNJ_S
+ set_frd(fsgnj32(frs1_boxed(), frs2_boxed(), false, false));
+ break;
+ }
+ case 0b001: { // RO_FSGNJN_S
+ set_frd(fsgnj32(frs1_boxed(), frs2_boxed(), true, false));
+ break;
+ }
+ case 0b010: { // RO_FSQNJX_S
+ set_frd(fsgnj32(frs1_boxed(), frs2_boxed(), false, true));
+ break;
+ }
+ default: {
+ UNSUPPORTED();
+ }
+ }
+ break;
+ }
+ case RO_FMIN_S: { // RO_FMAX_S
+ switch (instr_.Funct3Value()) {
+ case 0b000: { // RO_FMIN_S
+ set_frd(FMaxMinHelper(frs1(), frs2(), MaxMinKind::kMin));
+ break;
+ }
+ case 0b001: { // RO_FMAX_S
+ set_frd(FMaxMinHelper(frs1(), frs2(), MaxMinKind::kMax));
+ break;
+ }
+ default: {
+ UNSUPPORTED();
+ }
+ }
+ break;
+ }
+ case RO_FCVT_W_S: { // RO_FCVT_WU_S , 64F RO_FCVT_L_S RO_FCVT_LU_S
+ float original_val = frs1();
+ switch (instr_.Rs2Value()) {
+ case 0b00000: { // RO_FCVT_W_S
+ set_rd(RoundF2IHelper<int32_t>(original_val, instr_.RoundMode()));
+ break;
+ }
+ case 0b00001: { // RO_FCVT_WU_S
+ set_rd(sext32(
+ RoundF2IHelper<uint32_t>(original_val, instr_.RoundMode())));
+ break;
+ }
+# ifdef JS_CODEGEN_RISCV64
+ case 0b00010: { // RO_FCVT_L_S
+ set_rd(RoundF2IHelper<int64_t>(original_val, instr_.RoundMode()));
+ break;
+ }
+ case 0b00011: { // RO_FCVT_LU_S
+ set_rd(RoundF2IHelper<uint64_t>(original_val, instr_.RoundMode()));
+ break;
+ }
+# endif /* JS_CODEGEN_RISCV64 */
+ default: {
+ UNSUPPORTED();
+ }
+ }
+ break;
+ }
+ case RO_FMV: { // RO_FCLASS_S
+ switch (instr_.Funct3Value()) {
+ case 0b000: {
+ if (instr_.Rs2Value() == 0b00000) {
+ // RO_FMV_X_W
+ set_rd(sext32(getFpuRegister(rs1_reg())));
+ } else {
+ UNSUPPORTED();
+ }
+ break;
+ }
+ case 0b001: { // RO_FCLASS_S
+ set_rd(FclassHelper(frs1()));
+ break;
+ }
+ default: {
+ UNSUPPORTED();
+ }
+ }
+ break;
+ }
+ case RO_FLE_S: { // RO_FEQ_S RO_FLT_S RO_FLE_S
+ switch (instr_.Funct3Value()) {
+ case 0b010: { // RO_FEQ_S
+ set_rd(CompareFHelper(frs1(), frs2(), EQ));
+ break;
+ }
+ case 0b001: { // RO_FLT_S
+ set_rd(CompareFHelper(frs1(), frs2(), LT));
+ break;
+ }
+ case 0b000: { // RO_FLE_S
+ set_rd(CompareFHelper(frs1(), frs2(), LE));
+ break;
+ }
+ default: {
+ UNSUPPORTED();
+ }
+ }
+ break;
+ }
+ case RO_FCVT_S_W: { // RO_FCVT_S_WU , 64F RO_FCVT_S_L RO_FCVT_S_LU
+ switch (instr_.Rs2Value()) {
+ case 0b00000: { // RO_FCVT_S_W
+ set_frd(static_cast<float>((int32_t)rs1()));
+ break;
+ }
+ case 0b00001: { // RO_FCVT_S_WU
+ set_frd(static_cast<float>((uint32_t)rs1()));
+ break;
+ }
+# ifdef JS_CODEGEN_RISCV64
+ case 0b00010: { // RO_FCVT_S_L
+ set_frd(static_cast<float>((int64_t)rs1()));
+ break;
+ }
+ case 0b00011: { // RO_FCVT_S_LU
+ set_frd(static_cast<float>((uint64_t)rs1()));
+ break;
+ }
+# endif /* JS_CODEGEN_RISCV64 */
+ default: {
+ UNSUPPORTED();
+ }
+ }
+ break;
+ }
+ case RO_FMV_W_X: {
+ if (instr_.Funct3Value() == 0b000) {
+ // since FMV preserves source bit-pattern, no need to canonize
+ Float32 result = Float32::FromBits((uint32_t)rs1());
+ set_frd(result);
+ } else {
+ UNSUPPORTED();
+ }
+ break;
+ }
+ // TODO(riscv): Add macro for RISCV D extension
+ case RO_FADD_D: {
+ // TODO(riscv): use rm value (round mode)
+ auto fn = [this](double drs1, double drs2) {
+ if (is_invalid_fadd(drs1, drs2)) {
+ this->set_fflags(kInvalidOperation);
+ return std::numeric_limits<double>::quiet_NaN();
+ } else {
+ return drs1 + drs2;
+ }
+ };
+ set_drd(CanonicalizeFPUOp2<double>(fn));
+ break;
+ }
+ case RO_FSUB_D: {
+ // TODO(riscv): use rm value (round mode)
+ auto fn = [this](double drs1, double drs2) {
+ if (is_invalid_fsub(drs1, drs2)) {
+ this->set_fflags(kInvalidOperation);
+ return std::numeric_limits<double>::quiet_NaN();
+ } else {
+ return drs1 - drs2;
+ }
+ };
+ set_drd(CanonicalizeFPUOp2<double>(fn));
+ break;
+ }
+ case RO_FMUL_D: {
+ // TODO(riscv): use rm value (round mode)
+ auto fn = [this](double drs1, double drs2) {
+ if (is_invalid_fmul(drs1, drs2)) {
+ this->set_fflags(kInvalidOperation);
+ return std::numeric_limits<double>::quiet_NaN();
+ } else {
+ return drs1 * drs2;
+ }
+ };
+ set_drd(CanonicalizeFPUOp2<double>(fn));
+ break;
+ }
+ case RO_FDIV_D: {
+ // TODO(riscv): use rm value (round mode)
+ auto fn = [this](double drs1, double drs2) {
+ if (is_invalid_fdiv(drs1, drs2)) {
+ this->set_fflags(kInvalidOperation);
+ return std::numeric_limits<double>::quiet_NaN();
+ } else if (drs2 == 0.0) {
+ this->set_fflags(kDivideByZero);
+ return (std::signbit(drs1) == std::signbit(drs2)
+ ? std::numeric_limits<double>::infinity()
+ : -std::numeric_limits<double>::infinity());
+ } else {
+ return drs1 / drs2;
+ }
+ };
+ set_drd(CanonicalizeFPUOp2<double>(fn));
+ break;
+ }
+ case RO_FSQRT_D: {
+ if (instr_.Rs2Value() == 0b00000) {
+ // TODO(riscv): use rm value (round mode)
+ auto fn = [this](double drs) {
+ if (is_invalid_fsqrt(drs)) {
+ this->set_fflags(kInvalidOperation);
+ return std::numeric_limits<double>::quiet_NaN();
+ } else {
+ return std::sqrt(drs);
+ }
+ };
+ set_drd(CanonicalizeFPUOp1<double>(fn));
+ } else {
+ UNSUPPORTED();
+ }
+ break;
+ }
+ case RO_FSGNJ_D: { // RO_FSGNJN_D RO_FSQNJX_D
+ switch (instr_.Funct3Value()) {
+ case 0b000: { // RO_FSGNJ_D
+ set_drd(fsgnj64(drs1_boxed(), drs2_boxed(), false, false));
+ break;
+ }
+ case 0b001: { // RO_FSGNJN_D
+ set_drd(fsgnj64(drs1_boxed(), drs2_boxed(), true, false));
+ break;
+ }
+ case 0b010: { // RO_FSQNJX_D
+ set_drd(fsgnj64(drs1_boxed(), drs2_boxed(), false, true));
+ break;
+ }
+ default: {
+ UNSUPPORTED();
+ }
+ }
+ break;
+ }
+ case RO_FMIN_D: { // RO_FMAX_D
+ switch (instr_.Funct3Value()) {
+ case 0b000: { // RO_FMIN_D
+ set_drd(FMaxMinHelper(drs1(), drs2(), MaxMinKind::kMin));
+ break;
+ }
+ case 0b001: { // RO_FMAX_D
+ set_drd(FMaxMinHelper(drs1(), drs2(), MaxMinKind::kMax));
+ break;
+ }
+ default: {
+ UNSUPPORTED();
+ }
+ }
+ break;
+ }
+ case (RO_FCVT_S_D & kRFPTypeMask): {
+ if (instr_.Rs2Value() == 0b00001) {
+ auto fn = [](double drs) { return static_cast<float>(drs); };
+ set_frd(CanonicalizeDoubleToFloatOperation(fn));
+ } else {
+ UNSUPPORTED();
+ }
+ break;
+ }
+ case RO_FCVT_D_S: {
+ if (instr_.Rs2Value() == 0b00000) {
+ auto fn = [](float frs) { return static_cast<double>(frs); };
+ set_drd(CanonicalizeFloatToDoubleOperation(fn));
+ } else {
+ UNSUPPORTED();
+ }
+ break;
+ }
+ case RO_FLE_D: { // RO_FEQ_D RO_FLT_D RO_FLE_D
+ switch (instr_.Funct3Value()) {
+ case 0b010: { // RO_FEQ_S
+ set_rd(CompareFHelper(drs1(), drs2(), EQ));
+ break;
+ }
+ case 0b001: { // RO_FLT_D
+ set_rd(CompareFHelper(drs1(), drs2(), LT));
+ break;
+ }
+ case 0b000: { // RO_FLE_D
+ set_rd(CompareFHelper(drs1(), drs2(), LE));
+ break;
+ }
+ default: {
+ UNSUPPORTED();
+ }
+ }
+ break;
+ }
+ case (RO_FCLASS_D & kRFPTypeMask): { // RO_FCLASS_D , 64D RO_FMV_X_D
+ if (instr_.Rs2Value() != 0b00000) {
+ UNSUPPORTED();
+ }
+ switch (instr_.Funct3Value()) {
+ case 0b001: { // RO_FCLASS_D
+ set_rd(FclassHelper(drs1()));
+ break;
+ }
+# ifdef JS_CODEGEN_RISCV64
+ case 0b000: { // RO_FMV_X_D
+ set_rd(bit_cast<int64_t>(drs1()));
+ break;
+ }
+# endif /* JS_CODEGEN_RISCV64 */
+ default: {
+ UNSUPPORTED();
+ }
+ }
+ break;
+ }
+ case RO_FCVT_W_D: { // RO_FCVT_WU_D , 64F RO_FCVT_L_D RO_FCVT_LU_D
+ double original_val = drs1();
+ switch (instr_.Rs2Value()) {
+ case 0b00000: { // RO_FCVT_W_D
+ set_rd(RoundF2IHelper<int32_t>(original_val, instr_.RoundMode()));
+ break;
+ }
+ case 0b00001: { // RO_FCVT_WU_D
+ set_rd(sext32(
+ RoundF2IHelper<uint32_t>(original_val, instr_.RoundMode())));
+ break;
+ }
+# ifdef JS_CODEGEN_RISCV64
+ case 0b00010: { // RO_FCVT_L_D
+ set_rd(RoundF2IHelper<int64_t>(original_val, instr_.RoundMode()));
+ break;
+ }
+ case 0b00011: { // RO_FCVT_LU_D
+ set_rd(RoundF2IHelper<uint64_t>(original_val, instr_.RoundMode()));
+ break;
+ }
+# endif /* JS_CODEGEN_RISCV64 */
+ default: {
+ UNSUPPORTED();
+ }
+ }
+ break;
+ }
+ case RO_FCVT_D_W: { // RO_FCVT_D_WU , 64F RO_FCVT_D_L RO_FCVT_D_LU
+ switch (instr_.Rs2Value()) {
+ case 0b00000: { // RO_FCVT_D_W
+ set_drd((int32_t)rs1());
+ break;
+ }
+ case 0b00001: { // RO_FCVT_D_WU
+ set_drd((uint32_t)rs1());
+ break;
+ }
+# ifdef JS_CODEGEN_RISCV64
+ case 0b00010: { // RO_FCVT_D_L
+ set_drd((int64_t)rs1());
+ break;
+ }
+ case 0b00011: { // RO_FCVT_D_LU
+ set_drd((uint64_t)rs1());
+ break;
+ }
+# endif /* JS_CODEGEN_RISCV64 */
+ default: {
+ UNSUPPORTED();
+ }
+ }
+ break;
+ }
+# ifdef JS_CODEGEN_RISCV64
+ case RO_FMV_D_X: {
+ if (instr_.Funct3Value() == 0b000 && instr_.Rs2Value() == 0b00000) {
+ // Since FMV preserves source bit-pattern, no need to canonize
+ set_drd(bit_cast<double>(rs1()));
+ } else {
+ UNSUPPORTED();
+ }
+ break;
+ }
+# endif /* JS_CODEGEN_RISCV64 */
+ default: {
+ UNSUPPORTED();
+ }
+ }
+}
+
+void Simulator::DecodeRVR4Type() {
+ switch (instr_.InstructionBits() & kR4TypeMask) {
+ // TODO(riscv): use F Extension macro block
+ case RO_FMADD_S: {
+ // TODO(riscv): use rm value (round mode)
+ auto fn = [this](float frs1, float frs2, float frs3) {
+ if (is_invalid_fmul(frs1, frs2) || is_invalid_fadd(frs1 * frs2, frs3)) {
+ this->set_fflags(kInvalidOperation);
+ return std::numeric_limits<float>::quiet_NaN();
+ } else {
+ return std::fma(frs1, frs2, frs3);
+ }
+ };
+ set_frd(CanonicalizeFPUOp3<float>(fn));
+ break;
+ }
+ case RO_FMSUB_S: {
+ // TODO(riscv): use rm value (round mode)
+ auto fn = [this](float frs1, float frs2, float frs3) {
+ if (is_invalid_fmul(frs1, frs2) || is_invalid_fsub(frs1 * frs2, frs3)) {
+ this->set_fflags(kInvalidOperation);
+ return std::numeric_limits<float>::quiet_NaN();
+ } else {
+ return std::fma(frs1, frs2, -frs3);
+ }
+ };
+ set_frd(CanonicalizeFPUOp3<float>(fn));
+ break;
+ }
+ case RO_FNMSUB_S: {
+ // TODO(riscv): use rm value (round mode)
+ auto fn = [this](float frs1, float frs2, float frs3) {
+ if (is_invalid_fmul(frs1, frs2) || is_invalid_fsub(frs3, frs1 * frs2)) {
+ this->set_fflags(kInvalidOperation);
+ return std::numeric_limits<float>::quiet_NaN();
+ } else {
+ return -std::fma(frs1, frs2, -frs3);
+ }
+ };
+ set_frd(CanonicalizeFPUOp3<float>(fn));
+ break;
+ }
+ case RO_FNMADD_S: {
+ // TODO(riscv): use rm value (round mode)
+ auto fn = [this](float frs1, float frs2, float frs3) {
+ if (is_invalid_fmul(frs1, frs2) || is_invalid_fadd(frs1 * frs2, frs3)) {
+ this->set_fflags(kInvalidOperation);
+ return std::numeric_limits<float>::quiet_NaN();
+ } else {
+ return -std::fma(frs1, frs2, frs3);
+ }
+ };
+ set_frd(CanonicalizeFPUOp3<float>(fn));
+ break;
+ }
+ // TODO(riscv): use F Extension macro block
+ case RO_FMADD_D: {
+ // TODO(riscv): use rm value (round mode)
+ auto fn = [this](double drs1, double drs2, double drs3) {
+ if (is_invalid_fmul(drs1, drs2) || is_invalid_fadd(drs1 * drs2, drs3)) {
+ this->set_fflags(kInvalidOperation);
+ return std::numeric_limits<double>::quiet_NaN();
+ } else {
+ return std::fma(drs1, drs2, drs3);
+ }
+ };
+ set_drd(CanonicalizeFPUOp3<double>(fn));
+ break;
+ }
+ case RO_FMSUB_D: {
+ // TODO(riscv): use rm value (round mode)
+ auto fn = [this](double drs1, double drs2, double drs3) {
+ if (is_invalid_fmul(drs1, drs2) || is_invalid_fsub(drs1 * drs2, drs3)) {
+ this->set_fflags(kInvalidOperation);
+ return std::numeric_limits<double>::quiet_NaN();
+ } else {
+ return std::fma(drs1, drs2, -drs3);
+ }
+ };
+ set_drd(CanonicalizeFPUOp3<double>(fn));
+ break;
+ }
+ case RO_FNMSUB_D: {
+ // TODO(riscv): use rm value (round mode)
+ auto fn = [this](double drs1, double drs2, double drs3) {
+ if (is_invalid_fmul(drs1, drs2) || is_invalid_fsub(drs3, drs1 * drs2)) {
+ this->set_fflags(kInvalidOperation);
+ return std::numeric_limits<double>::quiet_NaN();
+ } else {
+ return -std::fma(drs1, drs2, -drs3);
+ }
+ };
+ set_drd(CanonicalizeFPUOp3<double>(fn));
+ break;
+ }
+ case RO_FNMADD_D: {
+ // TODO(riscv): use rm value (round mode)
+ auto fn = [this](double drs1, double drs2, double drs3) {
+ if (is_invalid_fmul(drs1, drs2) || is_invalid_fadd(drs1 * drs2, drs3)) {
+ this->set_fflags(kInvalidOperation);
+ return std::numeric_limits<double>::quiet_NaN();
+ } else {
+ return -std::fma(drs1, drs2, drs3);
+ }
+ };
+ set_drd(CanonicalizeFPUOp3<double>(fn));
+ break;
+ }
+ default:
+ UNSUPPORTED();
+ }
+}
+
+# ifdef CAN_USE_RVV_INSTRUCTIONS
+bool Simulator::DecodeRvvVL() {
+ uint32_t instr_temp =
+ instr_.InstructionBits() & (kRvvMopMask | kRvvNfMask | kBaseOpcodeMask);
+ if (RO_V_VL == instr_temp) {
+ if (!(instr_.InstructionBits() & (kRvvRs2Mask))) {
+ switch (instr_.vl_vs_width()) {
+ case 8: {
+ RVV_VI_LD(0, (i * nf + fn), int8, false);
+ break;
+ }
+ case 16: {
+ RVV_VI_LD(0, (i * nf + fn), int16, false);
+ break;
+ }
+ case 32: {
+ RVV_VI_LD(0, (i * nf + fn), int32, false);
+ break;
+ }
+ case 64: {
+ RVV_VI_LD(0, (i * nf + fn), int64, false);
+ break;
+ }
+ default:
+ UNIMPLEMENTED_RISCV();
+ break;
+ }
+ return true;
+ } else {
+ UNIMPLEMENTED_RISCV();
+ return true;
+ }
+ } else if (RO_V_VLS == instr_temp) {
+ UNIMPLEMENTED_RISCV();
+ return true;
+ } else if (RO_V_VLX == instr_temp) {
+ UNIMPLEMENTED_RISCV();
+ return true;
+ } else if (RO_V_VLSEG2 == instr_temp || RO_V_VLSEG3 == instr_temp ||
+ RO_V_VLSEG4 == instr_temp || RO_V_VLSEG5 == instr_temp ||
+ RO_V_VLSEG6 == instr_temp || RO_V_VLSEG7 == instr_temp ||
+ RO_V_VLSEG8 == instr_temp) {
+ if (!(instr_.InstructionBits() & (kRvvRs2Mask))) {
+ UNIMPLEMENTED_RISCV();
+ return true;
+ } else {
+ UNIMPLEMENTED_RISCV();
+ return true;
+ }
+ } else if (RO_V_VLSSEG2 == instr_temp || RO_V_VLSSEG3 == instr_temp ||
+ RO_V_VLSSEG4 == instr_temp || RO_V_VLSSEG5 == instr_temp ||
+ RO_V_VLSSEG6 == instr_temp || RO_V_VLSSEG7 == instr_temp ||
+ RO_V_VLSSEG8 == instr_temp) {
+ UNIMPLEMENTED_RISCV();
+ return true;
+ } else if (RO_V_VLXSEG2 == instr_temp || RO_V_VLXSEG3 == instr_temp ||
+ RO_V_VLXSEG4 == instr_temp || RO_V_VLXSEG5 == instr_temp ||
+ RO_V_VLXSEG6 == instr_temp || RO_V_VLXSEG7 == instr_temp ||
+ RO_V_VLXSEG8 == instr_temp) {
+ UNIMPLEMENTED_RISCV();
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool Simulator::DecodeRvvVS() {
+ uint32_t instr_temp =
+ instr_.InstructionBits() & (kRvvMopMask | kRvvNfMask | kBaseOpcodeMask);
+ if (RO_V_VS == instr_temp) {
+ if (!(instr_.InstructionBits() & (kRvvRs2Mask))) {
+ switch (instr_.vl_vs_width()) {
+ case 8: {
+ RVV_VI_ST(0, (i * nf + fn), uint8, false);
+ break;
+ }
+ case 16: {
+ RVV_VI_ST(0, (i * nf + fn), uint16, false);
+ break;
+ }
+ case 32: {
+ RVV_VI_ST(0, (i * nf + fn), uint32, false);
+ break;
+ }
+ case 64: {
+ RVV_VI_ST(0, (i * nf + fn), uint64, false);
+ break;
+ }
+ default:
+ UNIMPLEMENTED_RISCV();
+ break;
+ }
+ } else {
+ UNIMPLEMENTED_RISCV();
+ }
+ return true;
+ } else if (RO_V_VSS == instr_temp) {
+ UNIMPLEMENTED_RISCV();
+ return true;
+ } else if (RO_V_VSX == instr_temp) {
+ UNIMPLEMENTED_RISCV();
+ return true;
+ } else if (RO_V_VSU == instr_temp) {
+ UNIMPLEMENTED_RISCV();
+ return true;
+ } else if (RO_V_VSSEG2 == instr_temp || RO_V_VSSEG3 == instr_temp ||
+ RO_V_VSSEG4 == instr_temp || RO_V_VSSEG5 == instr_temp ||
+ RO_V_VSSEG6 == instr_temp || RO_V_VSSEG7 == instr_temp ||
+ RO_V_VSSEG8 == instr_temp) {
+ UNIMPLEMENTED_RISCV();
+ return true;
+ } else if (RO_V_VSSSEG2 == instr_temp || RO_V_VSSSEG3 == instr_temp ||
+ RO_V_VSSSEG4 == instr_temp || RO_V_VSSSEG5 == instr_temp ||
+ RO_V_VSSSEG6 == instr_temp || RO_V_VSSSEG7 == instr_temp ||
+ RO_V_VSSSEG8 == instr_temp) {
+ UNIMPLEMENTED_RISCV();
+ return true;
+ } else if (RO_V_VSXSEG2 == instr_temp || RO_V_VSXSEG3 == instr_temp ||
+ RO_V_VSXSEG4 == instr_temp || RO_V_VSXSEG5 == instr_temp ||
+ RO_V_VSXSEG6 == instr_temp || RO_V_VSXSEG7 == instr_temp ||
+ RO_V_VSXSEG8 == instr_temp) {
+ UNIMPLEMENTED_RISCV();
+ return true;
+ } else {
+ return false;
+ }
+}
+# endif
+
+void Simulator::DecodeRVIType() {
+ switch (instr_.InstructionBits() & kITypeMask) {
+ case RO_JALR: {
+ set_rd(get_pc() + kInstrSize);
+ // Note: No need to shift 2 for JALR's imm12, but set lowest bit to 0.
+ sreg_t next_pc = (rs1() + imm12()) & ~sreg_t(1);
+ set_pc(next_pc);
+ break;
+ }
+ case RO_LB: {
+ sreg_t addr = rs1() + imm12();
+ int8_t val = ReadMem<int8_t>(addr, instr_.instr());
+ set_rd(sext_xlen(val), false);
+ TraceMemRd(addr, val, getRegister(rd_reg()));
+ break;
+ }
+ case RO_LH: {
+ sreg_t addr = rs1() + imm12();
+ int16_t val = ReadMem<int16_t>(addr, instr_.instr());
+ set_rd(sext_xlen(val), false);
+ TraceMemRd(addr, val, getRegister(rd_reg()));
+ break;
+ }
+ case RO_LW: {
+ sreg_t addr = rs1() + imm12();
+ int32_t val = ReadMem<int32_t>(addr, instr_.instr());
+ set_rd(sext_xlen(val), false);
+ TraceMemRd(addr, val, getRegister(rd_reg()));
+ break;
+ }
+ case RO_LBU: {
+ sreg_t addr = rs1() + imm12();
+ uint8_t val = ReadMem<uint8_t>(addr, instr_.instr());
+ set_rd(zext_xlen(val), false);
+ TraceMemRd(addr, val, getRegister(rd_reg()));
+ break;
+ }
+ case RO_LHU: {
+ sreg_t addr = rs1() + imm12();
+ uint16_t val = ReadMem<uint16_t>(addr, instr_.instr());
+ set_rd(zext_xlen(val), false);
+ TraceMemRd(addr, val, getRegister(rd_reg()));
+ break;
+ }
+# ifdef JS_CODEGEN_RISCV64
+ case RO_LWU: {
+ int64_t addr = rs1() + imm12();
+ uint32_t val = ReadMem<uint32_t>(addr, instr_.instr());
+ set_rd(zext_xlen(val), false);
+ TraceMemRd(addr, val, getRegister(rd_reg()));
+ break;
+ }
+ case RO_LD: {
+ int64_t addr = rs1() + imm12();
+ int64_t val = ReadMem<int64_t>(addr, instr_.instr());
+ set_rd(sext_xlen(val), false);
+ TraceMemRd(addr, val, getRegister(rd_reg()));
+ break;
+ }
+# endif /*JS_CODEGEN_RISCV64*/
+ case RO_ADDI: {
+ set_rd(sext_xlen(rs1() + imm12()));
+ break;
+ }
+ case RO_SLTI: {
+ set_rd(sreg_t(rs1()) < sreg_t(imm12()));
+ break;
+ }
+ case RO_SLTIU: {
+ set_rd(reg_t(rs1()) < reg_t(imm12()));
+ break;
+ }
+ case RO_XORI: {
+ set_rd(imm12() ^ rs1());
+ break;
+ }
+ case RO_ORI: {
+ set_rd(imm12() | rs1());
+ break;
+ }
+ case RO_ANDI: {
+ set_rd(imm12() & rs1());
+ break;
+ }
+ case RO_SLLI: {
+ require(shamt6() < xlen);
+ set_rd(sext_xlen(rs1() << shamt6()));
+ break;
+ }
+ case RO_SRLI: { // RO_SRAI
+ if (!instr_.IsArithShift()) {
+ require(shamt6() < xlen);
+ set_rd(sext_xlen(zext_xlen(rs1()) >> shamt6()));
+ } else {
+ require(shamt6() < xlen);
+ set_rd(sext_xlen(sext_xlen(rs1()) >> shamt6()));
+ }
+ break;
+ }
+# ifdef JS_CODEGEN_RISCV64
+ case RO_ADDIW: {
+ set_rd(sext32(rs1() + imm12()));
+ break;
+ }
+ case RO_SLLIW: {
+ set_rd(sext32(rs1() << shamt5()));
+ break;
+ }
+ case RO_SRLIW: { // RO_SRAIW
+ if (!instr_.IsArithShift()) {
+ set_rd(sext32(uint32_t(rs1()) >> shamt5()));
+ } else {
+ set_rd(sext32(int32_t(rs1()) >> shamt5()));
+ }
+ break;
+ }
+# endif /*JS_CODEGEN_RISCV64*/
+ case RO_FENCE: {
+ // DO nothing in sumulator
+ break;
+ }
+ case RO_ECALL: { // RO_EBREAK
+ if (instr_.Imm12Value() == 0) { // ECALL
+ SoftwareInterrupt();
+ } else if (instr_.Imm12Value() == 1) { // EBREAK
+ uint8_t code = get_ebreak_code(instr_.instr());
+ if (code == kWasmTrapCode) {
+ uint8_t* newPC;
+ if (wasm::HandleIllegalInstruction(registerState(), &newPC)) {
+ set_pc(int64_t(newPC));
+ return;
+ }
+ }
+ SoftwareInterrupt();
+ } else {
+ UNSUPPORTED();
+ }
+ break;
+ }
+ // TODO(riscv): use Zifencei Standard Extension macro block
+ case RO_FENCE_I: {
+ // spike: flush icache.
+ break;
+ }
+ // TODO(riscv): use Zicsr Standard Extension macro block
+ case RO_CSRRW: {
+ if (rd_reg() != zero_reg) {
+ set_rd(zext_xlen(read_csr_value(csr_reg())));
+ }
+ write_csr_value(csr_reg(), rs1());
+ break;
+ }
+ case RO_CSRRS: {
+ set_rd(zext_xlen(read_csr_value(csr_reg())));
+ if (rs1_reg() != zero_reg) {
+ set_csr_bits(csr_reg(), rs1());
+ }
+ break;
+ }
+ case RO_CSRRC: {
+ set_rd(zext_xlen(read_csr_value(csr_reg())));
+ if (rs1_reg() != zero_reg) {
+ clear_csr_bits(csr_reg(), rs1());
+ }
+ break;
+ }
+ case RO_CSRRWI: {
+ if (rd_reg() != zero_reg) {
+ set_rd(zext_xlen(read_csr_value(csr_reg())));
+ }
+ write_csr_value(csr_reg(), imm5CSR());
+ break;
+ }
+ case RO_CSRRSI: {
+ set_rd(zext_xlen(read_csr_value(csr_reg())));
+ if (imm5CSR() != 0) {
+ set_csr_bits(csr_reg(), imm5CSR());
+ }
+ break;
+ }
+ case RO_CSRRCI: {
+ set_rd(zext_xlen(read_csr_value(csr_reg())));
+ if (imm5CSR() != 0) {
+ clear_csr_bits(csr_reg(), imm5CSR());
+ }
+ break;
+ }
+ // TODO(riscv): use F Extension macro block
+ case RO_FLW: {
+ sreg_t addr = rs1() + imm12();
+ uint32_t val = ReadMem<uint32_t>(addr, instr_.instr());
+ set_frd(Float32::FromBits(val), false);
+ TraceMemRdFloat(addr, Float32::FromBits(val), getFpuRegister(frd_reg()));
+ break;
+ }
+ // TODO(riscv): use D Extension macro block
+ case RO_FLD: {
+ sreg_t addr = rs1() + imm12();
+ uint64_t val = ReadMem<uint64_t>(addr, instr_.instr());
+ set_drd(Float64::FromBits(val), false);
+ TraceMemRdDouble(addr, Float64::FromBits(val), getFpuRegister(frd_reg()));
+ break;
+ }
+ default: {
+# ifdef CAN_USE_RVV_INSTRUCTIONS
+ if (!DecodeRvvVL()) {
+ UNSUPPORTED();
+ }
+ break;
+# else
+ UNSUPPORTED();
+# endif
+ }
+ }
+}
+
+void Simulator::DecodeRVSType() {
+ switch (instr_.InstructionBits() & kSTypeMask) {
+ case RO_SB:
+ WriteMem<uint8_t>(rs1() + s_imm12(), (uint8_t)rs2(), instr_.instr());
+ break;
+ case RO_SH:
+ WriteMem<uint16_t>(rs1() + s_imm12(), (uint16_t)rs2(), instr_.instr());
+ break;
+ case RO_SW:
+ WriteMem<uint32_t>(rs1() + s_imm12(), (uint32_t)rs2(), instr_.instr());
+ break;
+# ifdef JS_CODEGEN_RISCV64
+ case RO_SD:
+ WriteMem<uint64_t>(rs1() + s_imm12(), (uint64_t)rs2(), instr_.instr());
+ break;
+# endif /*JS_CODEGEN_RISCV64*/
+ // TODO(riscv): use F Extension macro block
+ case RO_FSW: {
+ WriteMem<Float32>(rs1() + s_imm12(), getFpuRegisterFloat32(rs2_reg()),
+ instr_.instr());
+ break;
+ }
+ // TODO(riscv): use D Extension macro block
+ case RO_FSD: {
+ WriteMem<Float64>(rs1() + s_imm12(), getFpuRegisterFloat64(rs2_reg()),
+ instr_.instr());
+ break;
+ }
+ default:
+# ifdef CAN_USE_RVV_INSTRUCTIONS
+ if (!DecodeRvvVS()) {
+ UNSUPPORTED();
+ }
+ break;
+# else
+ UNSUPPORTED();
+# endif
+ }
+}
+
+void Simulator::DecodeRVBType() {
+ switch (instr_.InstructionBits() & kBTypeMask) {
+ case RO_BEQ:
+ if (rs1() == rs2()) {
+ int64_t next_pc = get_pc() + boffset();
+ set_pc(next_pc);
+ }
+ break;
+ case RO_BNE:
+ if (rs1() != rs2()) {
+ int64_t next_pc = get_pc() + boffset();
+ set_pc(next_pc);
+ }
+ break;
+ case RO_BLT:
+ if (rs1() < rs2()) {
+ int64_t next_pc = get_pc() + boffset();
+ set_pc(next_pc);
+ }
+ break;
+ case RO_BGE:
+ if (rs1() >= rs2()) {
+ int64_t next_pc = get_pc() + boffset();
+ set_pc(next_pc);
+ }
+ break;
+ case RO_BLTU:
+ if ((reg_t)rs1() < (reg_t)rs2()) {
+ int64_t next_pc = get_pc() + boffset();
+ set_pc(next_pc);
+ }
+ break;
+ case RO_BGEU:
+ if ((reg_t)rs1() >= (reg_t)rs2()) {
+ int64_t next_pc = get_pc() + boffset();
+ set_pc(next_pc);
+ }
+ break;
+ default:
+ UNSUPPORTED();
+ }
+}
+void Simulator::DecodeRVUType() {
+ // U Type doesn't have additoinal mask
+ switch (instr_.BaseOpcodeFieldRaw()) {
+ case LUI:
+ set_rd(u_imm20());
+ break;
+ case AUIPC:
+ set_rd(sext_xlen(u_imm20() + get_pc()));
+ break;
+ default:
+ UNSUPPORTED();
+ }
+}
+void Simulator::DecodeRVJType() {
+ // J Type doesn't have additional mask
+ switch (instr_.BaseOpcodeValue()) {
+ case JAL: {
+ set_rd(get_pc() + kInstrSize);
+ int64_t next_pc = get_pc() + imm20J();
+ set_pc(next_pc);
+ break;
+ }
+ default:
+ UNSUPPORTED();
+ }
+}
+void Simulator::DecodeCRType() {
+ switch (instr_.RvcFunct4Value()) {
+ case 0b1000:
+ if (instr_.RvcRs1Value() != 0 && instr_.RvcRs2Value() == 0) { // c.jr
+ set_pc(rvc_rs1());
+ } else if (instr_.RvcRdValue() != 0 &&
+ instr_.RvcRs2Value() != 0) { // c.mv
+ set_rvc_rd(sext_xlen(rvc_rs2()));
+ } else {
+ UNSUPPORTED();
+ }
+ break;
+ case 0b1001:
+ if (instr_.RvcRs1Value() == 0 && instr_.RvcRs2Value() == 0) { // c.ebreak
+ DieOrDebug();
+ } else if (instr_.RvcRdValue() != 0 &&
+ instr_.RvcRs2Value() == 0) { // c.jalr
+ setRegister(ra, get_pc() + kShortInstrSize);
+ set_pc(rvc_rs1());
+ } else if (instr_.RvcRdValue() != 0 &&
+ instr_.RvcRs2Value() != 0) { // c.add
+ set_rvc_rd(sext_xlen(rvc_rs1() + rvc_rs2()));
+ } else {
+ UNSUPPORTED();
+ }
+ break;
+ default:
+ UNSUPPORTED();
+ }
+}
+
+void Simulator::DecodeCAType() {
+ switch (instr_.InstructionBits() & kCATypeMask) {
+ case RO_C_SUB:
+ set_rvc_rs1s(sext_xlen(rvc_rs1s() - rvc_rs2s()));
+ break;
+ case RO_C_XOR:
+ set_rvc_rs1s(rvc_rs1s() ^ rvc_rs2s());
+ break;
+ case RO_C_OR:
+ set_rvc_rs1s(rvc_rs1s() | rvc_rs2s());
+ break;
+ case RO_C_AND:
+ set_rvc_rs1s(rvc_rs1s() & rvc_rs2s());
+ break;
+# if JS_CODEGEN_RISCV64
+ case RO_C_SUBW:
+ set_rvc_rs1s(sext32(rvc_rs1s() - rvc_rs2s()));
+ break;
+ case RO_C_ADDW:
+ set_rvc_rs1s(sext32(rvc_rs1s() + rvc_rs2s()));
+ break;
+# endif
+ default:
+ UNSUPPORTED();
+ }
+}
+
+void Simulator::DecodeCIType() {
+ switch (instr_.RvcOpcode()) {
+ case RO_C_NOP_ADDI:
+ if (instr_.RvcRdValue() == 0) // c.nop
+ break;
+ else // c.addi
+ set_rvc_rd(sext_xlen(rvc_rs1() + rvc_imm6()));
+ break;
+# if JS_CODEGEN_RISCV64
+ case RO_C_ADDIW:
+ set_rvc_rd(sext32(rvc_rs1() + rvc_imm6()));
+ break;
+# endif
+ case RO_C_LI:
+ set_rvc_rd(sext_xlen(rvc_imm6()));
+ break;
+ case RO_C_LUI_ADD:
+ if (instr_.RvcRdValue() == 2) {
+ // c.addi16sp
+ int64_t value = getRegister(sp) + rvc_imm6_addi16sp();
+ setRegister(sp, value);
+ } else if (instr_.RvcRdValue() != 0 && instr_.RvcRdValue() != 2) {
+ // c.lui
+ set_rvc_rd(rvc_u_imm6());
+ } else {
+ UNSUPPORTED();
+ }
+ break;
+ case RO_C_SLLI:
+ set_rvc_rd(sext_xlen(rvc_rs1() << rvc_shamt6()));
+ break;
+ case RO_C_FLDSP: {
+ sreg_t addr = getRegister(sp) + rvc_imm6_ldsp();
+ uint64_t val = ReadMem<uint64_t>(addr, instr_.instr());
+ set_rvc_drd(Float64::FromBits(val), false);
+ TraceMemRdDouble(addr, Float64::FromBits(val),
+ getFpuRegister(rvc_frd_reg()));
+ break;
+ }
+# if JS_CODEGEN_RISCV64
+ case RO_C_LWSP: {
+ sreg_t addr = getRegister(sp) + rvc_imm6_lwsp();
+ int64_t val = ReadMem<int32_t>(addr, instr_.instr());
+ set_rvc_rd(sext_xlen(val), false);
+ TraceMemRd(addr, val, getRegister(rvc_rd_reg()));
+ break;
+ }
+ case RO_C_LDSP: {
+ sreg_t addr = getRegister(sp) + rvc_imm6_ldsp();
+ int64_t val = ReadMem<int64_t>(addr, instr_.instr());
+ set_rvc_rd(sext_xlen(val), false);
+ TraceMemRd(addr, val, getRegister(rvc_rd_reg()));
+ break;
+ }
+# elif JS_CODEGEN_RISCV32
+ case RO_C_FLWSP: {
+ sreg_t addr = getRegister(sp) + rvc_imm6_ldsp();
+ uint32_t val = ReadMem<uint32_t>(addr, instr_.instr());
+ set_rvc_frd(Float32::FromBits(val), false);
+ TraceMemRdFloat(addr, Float32::FromBits(val),
+ getFpuRegister(rvc_frd_reg()));
+ break;
+ }
+ case RO_C_LWSP: {
+ sreg_t addr = getRegister(sp) + rvc_imm6_lwsp();
+ int32_t val = ReadMem<int32_t>(addr, instr_.instr());
+ set_rvc_rd(sext_xlen(val), false);
+ TraceMemRd(addr, val, getRegister(rvc_rd_reg()));
+ break;
+ }
+# endif
+ default:
+ UNSUPPORTED();
+ }
+}
+
+void Simulator::DecodeCIWType() {
+ switch (instr_.RvcOpcode()) {
+ case RO_C_ADDI4SPN: {
+ set_rvc_rs2s(getRegister(sp) + rvc_imm8_addi4spn());
+ break;
+ default:
+ UNSUPPORTED();
+ }
+ }
+}
+
+void Simulator::DecodeCSSType() {
+ switch (instr_.RvcOpcode()) {
+ case RO_C_FSDSP: {
+ sreg_t addr = getRegister(sp) + rvc_imm6_sdsp();
+ WriteMem<Float64>(addr, getFpuRegisterFloat64(rvc_rs2_reg()),
+ instr_.instr());
+ break;
+ }
+# if JS_CODEGEN_RISCV32
+ case RO_C_FSWSP: {
+ sreg_t addr = getRegister(sp) + rvc_imm6_sdsp();
+ WriteMem<Float32>(addr, getFpuRegisterFloat32(rvc_rs2_reg()),
+ instr_.instr());
+ break;
+ }
+# endif
+ case RO_C_SWSP: {
+ sreg_t addr = getRegister(sp) + rvc_imm6_swsp();
+ WriteMem<int32_t>(addr, (int32_t)rvc_rs2(), instr_.instr());
+ break;
+ }
+# if JS_CODEGEN_RISCV64
+ case RO_C_SDSP: {
+ sreg_t addr = getRegister(sp) + rvc_imm6_sdsp();
+ WriteMem<int64_t>(addr, (int64_t)rvc_rs2(), instr_.instr());
+ break;
+ }
+# endif
+ default:
+ UNSUPPORTED();
+ }
+}
+
+void Simulator::DecodeCLType() {
+ switch (instr_.RvcOpcode()) {
+ case RO_C_LW: {
+ sreg_t addr = rvc_rs1s() + rvc_imm5_w();
+ int64_t val = ReadMem<int32_t>(addr, instr_.instr());
+ set_rvc_rs2s(sext_xlen(val), false);
+ TraceMemRd(addr, val, getRegister(rvc_rs2s_reg()));
+ break;
+ }
+ case RO_C_FLD: {
+ sreg_t addr = rvc_rs1s() + rvc_imm5_d();
+ uint64_t val = ReadMem<uint64_t>(addr, instr_.instr());
+ set_rvc_drs2s(Float64::FromBits(val), false);
+ break;
+ }
+# if JS_CODEGEN_RISCV64
+ case RO_C_LD: {
+ sreg_t addr = rvc_rs1s() + rvc_imm5_d();
+ int64_t val = ReadMem<int64_t>(addr, instr_.instr());
+ set_rvc_rs2s(sext_xlen(val), false);
+ TraceMemRd(addr, val, getRegister(rvc_rs2s_reg()));
+ break;
+ }
+# elif JS_CODEGEN_RISCV32
+ case RO_C_FLW: {
+ sreg_t addr = rvc_rs1s() + rvc_imm5_d();
+ uint32_t val = ReadMem<uint32_t>(addr, instr_.instr());
+ set_rvc_frs2s(Float32::FromBits(val), false);
+ break;
+ }
+# endif
+ default:
+ UNSUPPORTED();
+ }
+}
+
+void Simulator::DecodeCSType() {
+ switch (instr_.RvcOpcode()) {
+ case RO_C_SW: {
+ sreg_t addr = rvc_rs1s() + rvc_imm5_w();
+ WriteMem<int32_t>(addr, (int32_t)rvc_rs2s(), instr_.instr());
+ break;
+ }
+# if JS_CODEGEN_RISCV64
+ case RO_C_SD: {
+ sreg_t addr = rvc_rs1s() + rvc_imm5_d();
+ WriteMem<int64_t>(addr, (int64_t)rvc_rs2s(), instr_.instr());
+ break;
+ }
+# endif
+ case RO_C_FSD: {
+ sreg_t addr = rvc_rs1s() + rvc_imm5_d();
+ WriteMem<double>(addr, static_cast<double>(rvc_drs2s()), instr_.instr());
+ break;
+ }
+ default:
+ UNSUPPORTED();
+ }
+}
+
+void Simulator::DecodeCJType() {
+ switch (instr_.RvcOpcode()) {
+ case RO_C_J: {
+ set_pc(get_pc() + instr_.RvcImm11CJValue());
+ break;
+ }
+ default:
+ UNSUPPORTED();
+ }
+}
+
+void Simulator::DecodeCBType() {
+ switch (instr_.RvcOpcode()) {
+ case RO_C_BNEZ:
+ if (rvc_rs1() != 0) {
+ sreg_t next_pc = get_pc() + rvc_imm8_b();
+ set_pc(next_pc);
+ }
+ break;
+ case RO_C_BEQZ:
+ if (rvc_rs1() == 0) {
+ sreg_t next_pc = get_pc() + rvc_imm8_b();
+ set_pc(next_pc);
+ }
+ break;
+ case RO_C_MISC_ALU:
+ if (instr_.RvcFunct2BValue() == 0b00) { // c.srli
+ set_rvc_rs1s(sext_xlen(sext_xlen(rvc_rs1s()) >> rvc_shamt6()));
+ } else if (instr_.RvcFunct2BValue() == 0b01) { // c.srai
+ require(rvc_shamt6() < xlen);
+ set_rvc_rs1s(sext_xlen(sext_xlen(rvc_rs1s()) >> rvc_shamt6()));
+ } else if (instr_.RvcFunct2BValue() == 0b10) { // c.andi
+ set_rvc_rs1s(rvc_imm6() & rvc_rs1s());
+ } else {
+ UNSUPPORTED();
+ }
+ break;
+ default:
+ UNSUPPORTED();
+ }
+}
+
+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.
+ intptr_t s0_val = getRegister(Simulator::Register::fp);
+ intptr_t s1_val = getRegister(Simulator::Register::s1);
+ intptr_t s2_val = getRegister(Simulator::Register::s2);
+ intptr_t s3_val = getRegister(Simulator::Register::s3);
+ intptr_t s4_val = getRegister(Simulator::Register::s4);
+ intptr_t s5_val = getRegister(Simulator::Register::s5);
+ intptr_t s6_val = getRegister(Simulator::Register::s6);
+ intptr_t s7_val = getRegister(Simulator::Register::s7);
+ intptr_t s8_val = getRegister(Simulator::Register::s8);
+ intptr_t s9_val = getRegister(Simulator::Register::s9);
+ intptr_t s10_val = getRegister(Simulator::Register::s10);
+ intptr_t s11_val = getRegister(Simulator::Register::s11);
+ intptr_t gp_val = getRegister(Simulator::Register::gp);
+ intptr_t sp_val = getRegister(Simulator::Register::sp);
+
+ // Set up the callee-saved registers with a known value. To be able to check
+ // that they are preserved properly across JS execution. If this value is
+ // small int, it should be SMI.
+ intptr_t callee_saved_value = icount_;
+ setRegister(Simulator::Register::fp, callee_saved_value);
+ setRegister(Simulator::Register::s1, callee_saved_value);
+ setRegister(Simulator::Register::s2, callee_saved_value);
+ setRegister(Simulator::Register::s3, callee_saved_value);
+ setRegister(Simulator::Register::s4, callee_saved_value);
+ setRegister(Simulator::Register::s5, callee_saved_value);
+ setRegister(Simulator::Register::s6, callee_saved_value);
+ setRegister(Simulator::Register::s7, callee_saved_value);
+ setRegister(Simulator::Register::s8, callee_saved_value);
+ setRegister(Simulator::Register::s9, callee_saved_value);
+ setRegister(Simulator::Register::s10, callee_saved_value);
+ setRegister(Simulator::Register::s11, callee_saved_value);
+ setRegister(Simulator::Register::gp, 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(Simulator::Register::fp));
+ MOZ_ASSERT(callee_saved_value == getRegister(Simulator::Register::s1));
+ MOZ_ASSERT(callee_saved_value == getRegister(Simulator::Register::s2));
+ MOZ_ASSERT(callee_saved_value == getRegister(Simulator::Register::s3));
+ MOZ_ASSERT(callee_saved_value == getRegister(Simulator::Register::s4));
+ MOZ_ASSERT(callee_saved_value == getRegister(Simulator::Register::s5));
+ MOZ_ASSERT(callee_saved_value == getRegister(Simulator::Register::s6));
+ MOZ_ASSERT(callee_saved_value == getRegister(Simulator::Register::s7));
+ MOZ_ASSERT(callee_saved_value == getRegister(Simulator::Register::s8));
+ MOZ_ASSERT(callee_saved_value == getRegister(Simulator::Register::s9));
+ MOZ_ASSERT(callee_saved_value == getRegister(Simulator::Register::s10));
+ MOZ_ASSERT(callee_saved_value == getRegister(Simulator::Register::s11));
+ MOZ_ASSERT(callee_saved_value == getRegister(Simulator::Register::gp));
+
+ // Restore callee-saved registers with the original value.
+ setRegister(Simulator::Register::fp, s0_val);
+ setRegister(Simulator::Register::s1, s1_val);
+ setRegister(Simulator::Register::s2, s2_val);
+ setRegister(Simulator::Register::s3, s3_val);
+ setRegister(Simulator::Register::s4, s4_val);
+ setRegister(Simulator::Register::s5, s5_val);
+ setRegister(Simulator::Register::s6, s6_val);
+ setRegister(Simulator::Register::s7, s7_val);
+ setRegister(Simulator::Register::s8, s8_val);
+ setRegister(Simulator::Register::s9, s9_val);
+ setRegister(Simulator::Register::s10, s10_val);
+ setRegister(Simulator::Register::s11, s11_val);
+ setRegister(Simulator::Register::gp, gp_val);
+ setRegister(Simulator::Register::sp, sp_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_; }
+
+#endif // JS_SIMULATOR_RISCV64