/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
//       copyright notice, this list of conditions and the following
//       disclaimer in the documentation and/or other materials provided
//       with the distribution.
//     * Neither the name of Google Inc. nor the names of its
//       contributors may be used to endorse or promote products derived
//       from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include "jit/mips32/Simulator-mips32.h"

#include "mozilla/Casting.h"
#include "mozilla/FloatingPoint.h"
#include "mozilla/Likely.h"
#include "mozilla/MathAlgorithms.h"

#include <float.h>

#include "jit/AtomicOperations.h"
#include "jit/mips32/Assembler-mips32.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)

namespace js {
namespace jit {

static const Instr kCallRedirInstr =
    op_special | MAX_BREAK_CODE << FunctionBits | ff_break;

// Utils functions.
static bool HaveSameSign(int32_t a, int32_t b) { return ((a ^ b) >= 0); }

static uint32_t GetFCSRConditionBit(uint32_t cc) {
  if (cc == 0) {
    return 23;
  } else {
    return 24 + cc;
  }
}

static const int32_t kRegisterskMaxValue = 0x7fffffff;
static const int32_t kRegisterskMinValue = 0x80000000;

// -----------------------------------------------------------------------------
// MIPS assembly various constants.

class SimInstruction {
 public:
  enum {
    kInstrSize = 4,
    // On MIPS PC cannot actually be directly accessed. We behave as if PC was
    // always the value of the current instruction being executed.
    kPCReadOffset = 0
  };

  // Get the raw instruction bits.
  inline Instr instructionBits() const {
    return *reinterpret_cast<const Instr*>(this);
  }

  // Set the raw instruction bits to value.
  inline void setInstructionBits(Instr value) {
    *reinterpret_cast<Instr*>(this) = value;
  }

  // Read one particular bit out of the instruction bits.
  inline int bit(int nr) const { return (instructionBits() >> nr) & 1; }

  // Read a bit field out of the instruction bits.
  inline int bits(int hi, int lo) const {
    return (instructionBits() >> lo) & ((2 << (hi - lo)) - 1);
  }

  // Instruction type.
  enum Type { kRegisterType, kImmediateType, kJumpType, kUnsupported = -1 };

  // Get the encoding type of the instruction.
  Type instructionType() const;

  // Accessors for the different named fields used in the MIPS encoding.
  inline Opcode opcodeValue() const {
    return static_cast<Opcode>(bits(OpcodeShift + OpcodeBits - 1, OpcodeShift));
  }

  inline int rsValue() const {
    MOZ_ASSERT(instructionType() == kRegisterType ||
               instructionType() == kImmediateType);
    return bits(RSShift + RSBits - 1, RSShift);
  }

  inline int rtValue() const {
    MOZ_ASSERT(instructionType() == kRegisterType ||
               instructionType() == kImmediateType);
    return bits(RTShift + RTBits - 1, RTShift);
  }

  inline int rdValue() const {
    MOZ_ASSERT(instructionType() == kRegisterType);
    return bits(RDShift + RDBits - 1, RDShift);
  }

  inline int saValue() const {
    MOZ_ASSERT(instructionType() == kRegisterType);
    return bits(SAShift + SABits - 1, SAShift);
  }

  inline int functionValue() const {
    MOZ_ASSERT(instructionType() == kRegisterType ||
               instructionType() == kImmediateType);
    return bits(FunctionShift + FunctionBits - 1, FunctionShift);
  }

  inline int fdValue() const { return bits(FDShift + FDBits - 1, FDShift); }

  inline int fsValue() const { return bits(FSShift + FSBits - 1, FSShift); }

  inline int ftValue() const { return bits(FTShift + FTBits - 1, FTShift); }

  inline int frValue() const { return bits(FRShift + FRBits - 1, FRShift); }

  // Float Compare condition code instruction bits.
  inline int fcccValue() const {
    return bits(FCccShift + FCccBits - 1, FCccShift);
  }

  // Float Branch condition code instruction bits.
  inline int fbccValue() const {
    return bits(FBccShift + FBccBits - 1, FBccShift);
  }

  // Float Branch true/false instruction bit.
  inline int fbtrueValue() const {
    return bits(FBtrueShift + FBtrueBits - 1, FBtrueShift);
  }

  // Return the fields at their original place in the instruction encoding.
  inline Opcode opcodeFieldRaw() const {
    return static_cast<Opcode>(instructionBits() & OpcodeMask);
  }

  inline int rsFieldRaw() const {
    MOZ_ASSERT(instructionType() == kRegisterType ||
               instructionType() == kImmediateType);
    return instructionBits() & RSMask;
  }

  // Same as above function, but safe to call within instructionType().
  inline int rsFieldRawNoAssert() const { return instructionBits() & RSMask; }

  inline int rtFieldRaw() const {
    MOZ_ASSERT(instructionType() == kRegisterType ||
               instructionType() == kImmediateType);
    return instructionBits() & RTMask;
  }

  inline int rdFieldRaw() const {
    MOZ_ASSERT(instructionType() == kRegisterType);
    return instructionBits() & RDMask;
  }

  inline int saFieldRaw() const {
    MOZ_ASSERT(instructionType() == kRegisterType);
    return instructionBits() & SAMask;
  }

  inline int functionFieldRaw() const {
    return instructionBits() & FunctionMask;
  }

  // Get the secondary field according to the opcode.
  inline int secondaryValue() const {
    Opcode op = opcodeFieldRaw();
    switch (op) {
      case op_special:
      case op_special2:
        return functionValue();
      case op_cop1:
        return rsValue();
      case op_regimm:
        return rtValue();
      default:
        return ff_null;
    }
  }

  inline int32_t imm16Value() const {
    MOZ_ASSERT(instructionType() == kImmediateType);
    return bits(Imm16Shift + Imm16Bits - 1, Imm16Shift);
  }

  inline int32_t imm26Value() const {
    MOZ_ASSERT(instructionType() == kJumpType);
    return bits(Imm26Shift + Imm26Bits - 1, Imm26Shift);
  }

  // Say if the instruction should not be used in a branch delay slot.
  bool isForbiddenInBranchDelay() const;
  // Say if the instruction 'links'. e.g. jal, bal.
  bool isLinkingInstruction() const;
  // Say if the instruction is a debugger break/trap.
  bool isTrap() const;

 private:
  SimInstruction() = delete;
  SimInstruction(const SimInstruction& other) = delete;
  void operator=(const SimInstruction& other) = delete;
};

bool SimInstruction::isForbiddenInBranchDelay() const {
  const int op = opcodeFieldRaw();
  switch (op) {
    case op_j:
    case op_jal:
    case op_beq:
    case op_bne:
    case op_blez:
    case op_bgtz:
    case op_beql:
    case op_bnel:
    case op_blezl:
    case op_bgtzl:
      return true;
    case op_regimm:
      switch (rtFieldRaw()) {
        case rt_bltz:
        case rt_bgez:
        case rt_bltzal:
        case rt_bgezal:
          return true;
        default:
          return false;
      };
      break;
    case op_special:
      switch (functionFieldRaw()) {
        case ff_jr:
        case ff_jalr:
          return true;
        default:
          return false;
      };
      break;
    default:
      return false;
  }
}

bool SimInstruction::isLinkingInstruction() const {
  const int op = opcodeFieldRaw();
  switch (op) {
    case op_jal:
      return true;
    case op_regimm:
      switch (rtFieldRaw()) {
        case rt_bgezal:
        case rt_bltzal:
          return true;
        default:
          return false;
      };
    case op_special:
      switch (functionFieldRaw()) {
        case ff_jalr:
          return true;
        default:
          return false;
      };
    default:
      return false;
  };
}

bool SimInstruction::isTrap() const {
  if (opcodeFieldRaw() != op_special) {
    return false;
  } else {
    switch (functionFieldRaw()) {
      case ff_break:
        return instructionBits() != kCallRedirInstr;
      case ff_tge:
      case ff_tgeu:
      case ff_tlt:
      case ff_tltu:
      case ff_teq:
      case ff_tne:
        return bits(15, 6) != kWasmTrapCode;
      default:
        return false;
    };
  }
}

SimInstruction::Type SimInstruction::instructionType() const {
  switch (opcodeFieldRaw()) {
    case op_special:
      switch (functionFieldRaw()) {
        case ff_jr:
        case ff_jalr:
        case ff_break:
        case ff_sll:
        case ff_srl:
        case ff_sra:
        case ff_sllv:
        case ff_srlv:
        case ff_srav:
        case ff_mfhi:
        case ff_mflo:
        case ff_mult:
        case ff_multu:
        case ff_div:
        case ff_divu:
        case ff_add:
        case ff_addu:
        case ff_sub:
        case ff_subu:
        case ff_and:
        case ff_or:
        case ff_xor:
        case ff_nor:
        case ff_slt:
        case ff_sltu:
        case ff_tge:
        case ff_tgeu:
        case ff_tlt:
        case ff_tltu:
        case ff_teq:
        case ff_tne:
        case ff_movz:
        case ff_movn:
        case ff_movci:
        case ff_sync:
          return kRegisterType;
        default:
          return kUnsupported;
      };
      break;
    case op_special2:
      switch (functionFieldRaw()) {
        case ff_mul:
        case ff_madd:
        case ff_maddu:
        case ff_clz:
          return kRegisterType;
        default:
          return kUnsupported;
      };
      break;
    case op_special3:
      switch (functionFieldRaw()) {
        case ff_ins:
        case ff_ext:
        case ff_bshfl:
          return kRegisterType;
        default:
          return kUnsupported;
      };
      break;
    case op_cop1:  // Coprocessor instructions.
      switch (rsFieldRawNoAssert()) {
        case rs_bc1:  // Branch on coprocessor condition.
          return kImmediateType;
        default:
          return kRegisterType;
      };
      break;
    case op_cop1x:
      return kRegisterType;
      // 16 bits Immediate type instructions. e.g.: addi dest, src, imm16.
    case op_regimm:
    case op_beq:
    case op_bne:
    case op_blez:
    case op_bgtz:
    case op_addi:
    case op_addiu:
    case op_slti:
    case op_sltiu:
    case op_andi:
    case op_ori:
    case op_xori:
    case op_lui:
    case op_beql:
    case op_bnel:
    case op_blezl:
    case op_bgtzl:
    case op_lb:
    case op_lh:
    case op_lwl:
    case op_lw:
    case op_lbu:
    case op_lhu:
    case op_lwr:
    case op_sb:
    case op_sh:
    case op_swl:
    case op_sw:
    case op_swr:
    case op_lwc1:
    case op_ldc1:
    case op_swc1:
    case op_sdc1:
    case op_ll:
    case op_sc:
      return kImmediateType;
      // 26 bits immediate type instructions. e.g.: j imm26.
    case op_j:
    case op_jal:
      return kJumpType;
    default:
      return kUnsupported;
  }
  return kUnsupported;
}

// C/C++ argument slots size.
const int kCArgSlotCount = 4;
const int kCArgsSlotsSize = kCArgSlotCount * SimInstruction::kInstrSize;
const int kBranchReturnOffset = 2 * SimInstruction::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:
  AutoLockSimulatorCache() : Base(SimulatorProcess::singleton_->cacheLock_) {}
};

mozilla::Atomic<size_t, mozilla::ReleaseAcquire>
    SimulatorProcess::ICacheCheckingDisableCount(
        1);  // Checking is disabled by default.
mozilla::Atomic<bool, mozilla::ReleaseAcquire>
    SimulatorProcess::cacheInvalidatedBySignalHandler_(false);
SimulatorProcess* SimulatorProcess::singleton_ = nullptr;

int Simulator::StopSimAt = -1;

Simulator* Simulator::Create(JSContext* cx) {
  Simulator* sim = js_new<Simulator>();
  if (!sim) return nullptr;

  if (!sim->init()) {
    js_delete(sim);
    return nullptr;
  }

  char* stopAtStr = getenv("MIPS_SIM_STOP_AT");
  int64_t stopAt;
  if (stopAtStr && sscanf(stopAtStr, "%lld", &stopAt) == 1) {
    fprintf(stderr, "\nStopping simulation at icount %lld\n", stopAt);
    Simulator::StopSimAt = stopAt;
  }

  return sim;
}

void Simulator::Destroy(Simulator* sim) { js_delete(sim); }

// The MipsDebugger class is used by the simulator while debugging simulated
// code.
class MipsDebugger {
 public:
  explicit MipsDebugger(Simulator* sim) : sim_(sim) {}

  void stop(SimInstruction* instr);
  void debug();
  // Print all registers with a nice formatting.
  void printAllRegs();
  void printAllRegsIncludingFPU();

 private:
  // We set the breakpoint code to 0xfffff to easily recognize it.
  static const Instr kBreakpointInstr = op_special | ff_break | 0xfffff << 6;
  static const Instr kNopInstr = op_special | ff_sll;

  Simulator* sim_;

  int32_t getRegisterValue(int regnum);
  int32_t getFPURegisterValueInt(int regnum);
  int64_t getFPURegisterValueLong(int regnum);
  float getFPURegisterValueFloat(int regnum);
  double getFPURegisterValueDouble(int regnum);
  bool getValue(const char* desc, int32_t* value);

  // Set or delete a breakpoint. Returns true if successful.
  bool setBreakpoint(SimInstruction* breakpc);
  bool deleteBreakpoint(SimInstruction* breakpc);

  // Undo and redo all breakpoints. This is needed to bracket disassembly and
  // execution to skip past breakpoints when run from the debugger.
  void undoBreakpoints();
  void redoBreakpoints();
};

static void UNSUPPORTED() {
  printf("Unsupported instruction.\n");
  MOZ_CRASH();
}

void MipsDebugger::stop(SimInstruction* instr) {
  // Get the stop code.
  uint32_t code = instr->bits(25, 6);
  // Retrieve the encoded address, which comes just after this stop.
  char* msg =
      *reinterpret_cast<char**>(sim_->get_pc() + SimInstruction::kInstrSize);
  // Update this stop description.
  if (!sim_->watchedStops_[code].desc_) {
    sim_->watchedStops_[code].desc_ = msg;
  }
  // Print the stop message and code if it is not the default code.
  if (code != kMaxStopCode) {
    printf("Simulator hit stop %u: %s\n", code, msg);
  } else {
    printf("Simulator hit %s\n", msg);
  }
  sim_->set_pc(sim_->get_pc() + 2 * SimInstruction::kInstrSize);
  debug();
}

int32_t MipsDebugger::getRegisterValue(int regnum) {
  if (regnum == kPCRegister) return sim_->get_pc();
  return sim_->getRegister(regnum);
}

int32_t MipsDebugger::getFPURegisterValueInt(int regnum) {
  return sim_->getFpuRegister(regnum);
}

int64_t MipsDebugger::getFPURegisterValueLong(int regnum) {
  return sim_->getFpuRegisterLong(regnum);
}

float MipsDebugger::getFPURegisterValueFloat(int regnum) {
  return sim_->getFpuRegisterFloat(regnum);
}

double MipsDebugger::getFPURegisterValueDouble(int regnum) {
  return sim_->getFpuRegisterDouble(regnum);
}

bool MipsDebugger::getValue(const char* desc, int32_t* value) {
  Register reg = Register::FromName(desc);
  if (reg != InvalidReg) {
    *value = getRegisterValue(reg.code());
    return true;
  }

  if (strncmp(desc, "0x", 2) == 0) {
    return sscanf(desc, "%x", reinterpret_cast<uint32_t*>(value)) == 1;
  }
  return sscanf(desc, "%i", value) == 1;
}

bool MipsDebugger::setBreakpoint(SimInstruction* breakpc) {
  // Check if a breakpoint can be set. If not return without any side-effects.
  if (sim_->break_pc_ != nullptr) return false;

  // Set the breakpoint.
  sim_->break_pc_ = breakpc;
  sim_->break_instr_ = breakpc->instructionBits();
  // Not setting the breakpoint instruction in the code itself. It will be set
  // when the debugger shell continues.
  return true;
}

bool MipsDebugger::deleteBreakpoint(SimInstruction* breakpc) {
  if (sim_->break_pc_ != nullptr)
    sim_->break_pc_->setInstructionBits(sim_->break_instr_);

  sim_->break_pc_ = nullptr;
  sim_->break_instr_ = 0;
  return true;
}

void MipsDebugger::undoBreakpoints() {
  if (sim_->break_pc_) sim_->break_pc_->setInstructionBits(sim_->break_instr_);
}

void MipsDebugger::redoBreakpoints() {
  if (sim_->break_pc_) sim_->break_pc_->setInstructionBits(kBreakpointInstr);
}

void MipsDebugger::printAllRegs() {
  int32_t value;
  for (uint32_t i = 0; i < Registers::Total; i++) {
    value = getRegisterValue(i);
    printf("%3s: 0x%08x %10d   ", Registers::GetName(i), value, value);

    if (i % 2) printf("\n");
  }
  printf("\n");

  value = getRegisterValue(Simulator::LO);
  printf(" LO: 0x%08x %10d   ", value, value);
  value = getRegisterValue(Simulator::HI);
  printf(" HI: 0x%08x %10d\n", value, value);
  value = getRegisterValue(Simulator::pc);
  printf(" pc: 0x%08x\n", value);
}

void MipsDebugger::printAllRegsIncludingFPU() {
  printAllRegs();

  printf("\n\n");
  // f0, f1, f2, ... f31.
  for (uint32_t i = 0; i < FloatRegisters::RegisterIdLimit; i++) {
    if (i & 0x1) {
      printf("%3s: 0x%08x\tflt: %-8.4g\n", FloatRegisters::GetName(i),
             getFPURegisterValueInt(i), getFPURegisterValueFloat(i));
    } else {
      printf("%3s: 0x%08x\tflt: %-8.4g\tdbl: %-16.4g\n",
             FloatRegisters::GetName(i), getFPURegisterValueInt(i),
             getFPURegisterValueFloat(i), getFPURegisterValueDouble(i));
    }
  }
}

static char* ReadLine(const char* prompt) {
  char* result = nullptr;
  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.
      if (result) js_delete(result);
      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 = (char*)js_malloc(len + 1);
      if (!result) return nullptr;
    } else {
      // Allocate a new result with enough room for the new addition.
      int new_len = offset + len + 1;
      char* new_result = (char*)js_malloc(new_len);
      if (!new_result) return nullptr;
      // Copy the existing input into the new array and set the new
      // array as the result.
      memcpy(new_result, result, offset * sizeof(char));
      js_free(result);
      result = new_result;
    }
    // Copy the newly read line into the result.
    memcpy(result + offset, lineBuf, len * sizeof(char));
    offset += len;
  }

  MOZ_ASSERT(result);
  result[offset] = '\0';
  return result;
}

static void DisassembleInstruction(uint32_t pc) {
  uint8_t* bytes = reinterpret_cast<uint8_t*>(pc);
  char hexbytes[256];
  sprintf(hexbytes, "0x%x 0x%x 0x%x 0x%x", bytes[0], bytes[1], bytes[2],
          bytes[3]);
  char llvmcmd[1024];
  sprintf(llvmcmd,
          "bash -c \"echo -n '%p'; echo '%s' | "
          "llvm-mc -disassemble -arch=mipsel -mcpu=mips32r2 | "
          "grep -v pure_instructions | grep -v .text\"",
          static_cast<void*>(bytes), hexbytes);
  if (system(llvmcmd)) printf("Cannot disassemble instruction.\n");
}

void MipsDebugger::debug() {
  intptr_t lastPC = -1;
  bool done = false;

#define COMMAND_SIZE 63
#define ARG_SIZE 255

#define STR(a) #a
#define XSTR(a) STR(a)

  char cmd[COMMAND_SIZE + 1];
  char arg1[ARG_SIZE + 1];
  char arg2[ARG_SIZE + 1];
  char* argv[3] = {cmd, arg1, arg2};

  // Make sure to have a proper terminating character if reaching the limit.
  cmd[COMMAND_SIZE] = 0;
  arg1[ARG_SIZE] = 0;
  arg2[ARG_SIZE] = 0;

  // Undo all set breakpoints while running in the debugger shell. This will
  // make them invisible to all commands.
  undoBreakpoints();

  while (!done && (sim_->get_pc() != Simulator::end_sim_pc)) {
    if (lastPC != sim_->get_pc()) {
      DisassembleInstruction(sim_->get_pc());
      lastPC = sim_->get_pc();
    }
    char* line = ReadLine("sim> ");
    if (line == nullptr) {
      break;
    } else {
      char* last_input = sim_->lastDebuggerInput();
      if (strcmp(line, "\n") == 0 && last_input != nullptr) {
        line = last_input;
      } else {
        // Ownership is transferred to sim_;
        sim_->setLastDebuggerInput(line);
      }
      // Use sscanf to parse the individual parts of the command line. At the
      // moment no command expects more than two parameters.
      int argc = sscanf(line,
                              "%" XSTR(COMMAND_SIZE) "s "
                              "%" XSTR(ARG_SIZE) "s "
                              "%" XSTR(ARG_SIZE) "s",
                              cmd, arg1, arg2);
      if ((strcmp(cmd, "si") == 0) || (strcmp(cmd, "stepi") == 0)) {
        SimInstruction* instr =
            reinterpret_cast<SimInstruction*>(sim_->get_pc());
        if (!instr->isTrap()) {
          sim_->instructionDecode(
              reinterpret_cast<SimInstruction*>(sim_->get_pc()));
        } else {
          // Allow si to jump over generated breakpoints.
          printf("/!\\ Jumping over generated breakpoint.\n");
          sim_->set_pc(sim_->get_pc() + SimInstruction::kInstrSize);
        }
      } else if ((strcmp(cmd, "c") == 0) || (strcmp(cmd, "cont") == 0)) {
        // Execute the one instruction we broke at with breakpoints disabled.
        sim_->instructionDecode(
            reinterpret_cast<SimInstruction*>(sim_->get_pc()));
        // Leave the debugger shell.
        done = true;
      } else if ((strcmp(cmd, "p") == 0) || (strcmp(cmd, "print") == 0)) {
        if (argc == 2) {
          int32_t value;
          if (strcmp(arg1, "all") == 0) {
            printAllRegs();
          } else if (strcmp(arg1, "allf") == 0) {
            printAllRegsIncludingFPU();
          } else {
            Register reg = Register::FromName(arg1);
            FloatRegisters::Code fCode = FloatRegister::FromName(arg1);
            if (reg != InvalidReg) {
              value = getRegisterValue(reg.code());
              printf("%s: 0x%08x %d \n", arg1, value, value);
            } else if (fCode != FloatRegisters::Invalid) {
              if (fCode & 0x1) {
                printf("%3s: 0x%08x\tflt: %-8.4g\n",
                       FloatRegisters::GetName(fCode),
                       getFPURegisterValueInt(fCode),
                       getFPURegisterValueFloat(fCode));
              } else {
                printf("%3s: 0x%08x\tflt: %-8.4g\tdbl: %-16.4g\n",
                       FloatRegisters::GetName(fCode),
                       getFPURegisterValueInt(fCode),
                       getFPURegisterValueFloat(fCode),
                       getFPURegisterValueDouble(fCode));
              }
            } else {
              printf("%s unrecognized\n", arg1);
            }
          }
        } else {
          printf("print <register> or print <fpu register> single\n");
        }
      } else if (strcmp(cmd, "stack") == 0 || strcmp(cmd, "mem") == 0) {
        int32_t* cur = nullptr;
        int32_t* end = nullptr;
        int next_arg = 1;

        if (strcmp(cmd, "stack") == 0) {
          cur = reinterpret_cast<int32_t*>(sim_->getRegister(Simulator::sp));
        } else {  // Command "mem".
          int32_t value;
          if (!getValue(arg1, &value)) {
            printf("%s unrecognized\n", arg1);
            continue;
          }
          cur = reinterpret_cast<int32_t*>(value);
          next_arg++;
        }

        int32_t words;
        if (argc == next_arg) {
          words = 10;
        } else {
          if (!getValue(argv[next_arg], &words)) {
            words = 10;
          }
        }
        end = cur + words;

        while (cur < end) {
          printf("  %p:  0x%08x %10d", cur, *cur, *cur);
          printf("\n");
          cur++;
        }

      } else if ((strcmp(cmd, "disasm") == 0) || (strcmp(cmd, "dpc") == 0) ||
                 (strcmp(cmd, "di") == 0)) {
        uint8_t* cur = nullptr;
        uint8_t* end = nullptr;

        if (argc == 1) {
          cur = reinterpret_cast<uint8_t*>(sim_->get_pc());
          end = cur + (10 * SimInstruction::kInstrSize);
        } else if (argc == 2) {
          Register reg = Register::FromName(arg1);
          if (reg != InvalidReg || strncmp(arg1, "0x", 2) == 0) {
            // The argument is an address or a register name.
            int32_t value;
            if (getValue(arg1, &value)) {
              cur = reinterpret_cast<uint8_t*>(value);
              // Disassemble 10 instructions at <arg1>.
              end = cur + (10 * SimInstruction::kInstrSize);
            }
          } else {
            // The argument is the number of instructions.
            int32_t value;
            if (getValue(arg1, &value)) {
              cur = reinterpret_cast<uint8_t*>(sim_->get_pc());
              // Disassemble <arg1> instructions.
              end = cur + (value * SimInstruction::kInstrSize);
            }
          }
        } else {
          int32_t value1;
          int32_t value2;
          if (getValue(arg1, &value1) && getValue(arg2, &value2)) {
            cur = reinterpret_cast<uint8_t*>(value1);
            end = cur + (value2 * SimInstruction::kInstrSize);
          }
        }

        while (cur < end) {
          DisassembleInstruction(uint32_t(cur));
          cur += SimInstruction::kInstrSize;
        }
      } else if (strcmp(cmd, "gdb") == 0) {
        printf("relinquishing control to gdb\n");
        asm("int $3");
        printf("regaining control from gdb\n");
      } else if (strcmp(cmd, "break") == 0) {
        if (argc == 2) {
          int32_t value;
          if (getValue(arg1, &value)) {
            if (!setBreakpoint(reinterpret_cast<SimInstruction*>(value)))
              printf("setting breakpoint failed\n");
          } else {
            printf("%s unrecognized\n", arg1);
          }
        } else {
          printf("break <address>\n");
        }
      } else if (strcmp(cmd, "del") == 0) {
        if (!deleteBreakpoint(nullptr)) {
          printf("deleting breakpoint failed\n");
        }
      } else if (strcmp(cmd, "flags") == 0) {
        printf("No flags on MIPS !\n");
      } else if (strcmp(cmd, "stop") == 0) {
        int32_t value;
        intptr_t stop_pc = sim_->get_pc() - 2 * SimInstruction::kInstrSize;
        SimInstruction* stop_instr = reinterpret_cast<SimInstruction*>(stop_pc);
        SimInstruction* msg_address = reinterpret_cast<SimInstruction*>(
            stop_pc + SimInstruction::kInstrSize);
        if ((argc == 2) && (strcmp(arg1, "unstop") == 0)) {
          // Remove the current stop.
          if (sim_->isStopInstruction(stop_instr)) {
            stop_instr->setInstructionBits(kNopInstr);
            msg_address->setInstructionBits(kNopInstr);
          } else {
            printf("Not at debugger stop.\n");
          }
        } else if (argc == 3) {
          // Print information about all/the specified breakpoint(s).
          if (strcmp(arg1, "info") == 0) {
            if (strcmp(arg2, "all") == 0) {
              printf("Stop information:\n");
              for (uint32_t i = kMaxWatchpointCode + 1; i <= kMaxStopCode;
                   i++) {
                sim_->printStopInfo(i);
              }
            } else if (getValue(arg2, &value)) {
              sim_->printStopInfo(value);
            } else {
              printf("Unrecognized argument.\n");
            }
          } else if (strcmp(arg1, "enable") == 0) {
            // Enable all/the specified breakpoint(s).
            if (strcmp(arg2, "all") == 0) {
              for (uint32_t i = kMaxWatchpointCode + 1; i <= kMaxStopCode;
                   i++) {
                sim_->enableStop(i);
              }
            } else if (getValue(arg2, &value)) {
              sim_->enableStop(value);
            } else {
              printf("Unrecognized argument.\n");
            }
          } else if (strcmp(arg1, "disable") == 0) {
            // Disable all/the specified breakpoint(s).
            if (strcmp(arg2, "all") == 0) {
              for (uint32_t i = kMaxWatchpointCode + 1; i <= kMaxStopCode;
                   i++) {
                sim_->disableStop(i);
              }
            } else if (getValue(arg2, &value)) {
              sim_->disableStop(value);
            } else {
              printf("Unrecognized argument.\n");
            }
          }
        } else {
          printf("Wrong usage. Use help command for more information.\n");
        }
      } else if ((strcmp(cmd, "h") == 0) || (strcmp(cmd, "help") == 0)) {
        printf("cont\n");
        printf("  continue execution (alias 'c')\n");
        printf("stepi\n");
        printf("  step one instruction (alias 'si')\n");
        printf("print <register>\n");
        printf("  print register content (alias 'p')\n");
        printf("  use register name 'all' to print all registers\n");
        printf("printobject <register>\n");
        printf("  print an object from a register (alias 'po')\n");
        printf("stack [<words>]\n");
        printf("  dump stack content, default dump 10 words)\n");
        printf("mem <address> [<words>]\n");
        printf("  dump memory content, default dump 10 words)\n");
        printf("flags\n");
        printf("  print flags\n");
        printf("disasm [<instructions>]\n");
        printf("disasm [<address/register>]\n");
        printf("disasm [[<address/register>] <instructions>]\n");
        printf("  disassemble code, default is 10 instructions\n");
        printf("  from pc (alias 'di')\n");
        printf("gdb\n");
        printf("  enter gdb\n");
        printf("break <address>\n");
        printf("  set a break point on the address\n");
        printf("del\n");
        printf("  delete the breakpoint\n");
        printf("stop feature:\n");
        printf("  Description:\n");
        printf("    Stops are debug instructions inserted by\n");
        printf("    the Assembler::stop() function.\n");
        printf("    When hitting a stop, the Simulator will\n");
        printf("    stop and and give control to the Debugger.\n");
        printf("    All stop codes are watched:\n");
        printf("    - They can be enabled / disabled: the Simulator\n");
        printf("       will / won't stop when hitting them.\n");
        printf("    - The Simulator keeps track of how many times they \n");
        printf("      are met. (See the info command.) Going over a\n");
        printf("      disabled stop still increases its counter. \n");
        printf("  Commands:\n");
        printf("    stop info all/<code> : print infos about number <code>\n");
        printf("      or all stop(s).\n");
        printf("    stop enable/disable all/<code> : enables / disables\n");
        printf("      all or number <code> stop(s)\n");
        printf("    stop unstop\n");
        printf("      ignore the stop instruction at the current location\n");
        printf("      from now on\n");
      } else {
        printf("Unknown command: %s\n", cmd);
      }
    }
  }

  // Add all the breakpoints back to stop execution and enter the debugger
  // shell when hit.
  redoBreakpoints();

#undef COMMAND_SIZE
#undef ARG_SIZE

#undef STR
#undef XSTR
}

static bool AllOnOnePage(uintptr_t start, int size) {
  intptr_t start_page = (start & ~CachePage::kPageMask);
  intptr_t end_page = ((start + size) & ~CachePage::kPageMask);
  return start_page == end_page;
}

void Simulator::setLastDebuggerInput(char* input) {
  js_free(lastDebuggerInput_);
  lastDebuggerInput_ = input;
}

static CachePage* GetCachePageLocked(SimulatorProcess::ICacheMap& i_cache,
                                     void* page) {
  SimulatorProcess::ICacheMap::AddPtr p = i_cache.lookupForAdd(page);
  if (p) return p->value();
  AutoEnterOOMUnsafeRegion oomUnsafe;
  CachePage* new_page = js_new<CachePage>();
  if (!new_page || !i_cache.add(p, page, new_page))
    oomUnsafe.crash("Simulator CachePage");
  return new_page;
}

// Flush from start up to and not including start + size.
static void FlushOnePageLocked(SimulatorProcess::ICacheMap& i_cache,
                               intptr_t start, int size) {
  MOZ_ASSERT(size <= CachePage::kPageSize);
  MOZ_ASSERT(AllOnOnePage(start, size - 1));
  MOZ_ASSERT((start & CachePage::kLineMask) == 0);
  MOZ_ASSERT((size & CachePage::kLineMask) == 0);
  void* page = reinterpret_cast<void*>(start & (~CachePage::kPageMask));
  int offset = (start & CachePage::kPageMask);
  CachePage* cache_page = GetCachePageLocked(i_cache, page);
  char* valid_bytemap = cache_page->validityByte(offset);
  memset(valid_bytemap, CachePage::LINE_INVALID, size >> CachePage::kLineShift);
}

static void FlushICacheLocked(SimulatorProcess::ICacheMap& i_cache,
                              void* start_addr, size_t size) {
  intptr_t start = reinterpret_cast<intptr_t>(start_addr);
  int intra_line = (start & CachePage::kLineMask);
  start -= intra_line;
  size += intra_line;
  size = ((size - 1) | CachePage::kLineMask) + 1;
  int offset = (start & CachePage::kPageMask);
  while (!AllOnOnePage(start, size - 1)) {
    int bytes_to_flush = CachePage::kPageSize - offset;
    FlushOnePageLocked(i_cache, start, bytes_to_flush);
    start += bytes_to_flush;
    size -= bytes_to_flush;
    MOZ_ASSERT((start & CachePage::kPageMask) == 0);
    offset = 0;
  }
  if (size != 0) {
    FlushOnePageLocked(i_cache, start, size);
  }
}

/* static */ void SimulatorProcess::checkICacheLocked(SimInstruction* instr) {
  intptr_t address = reinterpret_cast<intptr_t>(instr);
  void* page = reinterpret_cast<void*>(address & (~CachePage::kPageMask));
  void* line = reinterpret_cast<void*>(address & (~CachePage::kLineMask));
  int offset = (address & CachePage::kPageMask);
  CachePage* cache_page = GetCachePageLocked(icache(), page);
  char* cache_valid_byte = cache_page->validityByte(offset);
  bool cache_hit = (*cache_valid_byte == CachePage::LINE_VALID);
  char* cached_line = cache_page->cachedData(offset & ~CachePage::kLineMask);

  // Read all state before considering signal handler effects.
  int cmpret = 0;
  if (cache_hit) {
    // Check that the data in memory matches the contents of the I-cache.
    cmpret = memcmp(reinterpret_cast<void*>(instr),
                    cache_page->cachedData(offset), SimInstruction::kInstrSize);
  }

  // Check for signal handler interruption between reading state and asserting.
  // It is safe for the signal to arrive during the !cache_hit path, since it
  // will be cleared the next time this function is called.
  if (cacheInvalidatedBySignalHandler_) {
    icache().clear();
    cacheInvalidatedBySignalHandler_ = false;
    return;
  }

  if (cache_hit) {
    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 static_cast<uint32_t>(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;
  wasm_interrupt_ = false;
  break_pc_ = nullptr;
  break_instr_ = 0;

  // Set up architecture state.
  // All registers are initialized to zero to start with.
  for (int i = 0; i < Register::kNumSimuRegisters; i++) {
    registers_[i] = 0;
  }
  for (int i = 0; i < Simulator::FPURegister::kNumFPURegisters; i++) {
    FPUregisters_[i] = 0;
  }
  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_ = static_cast<char*>(js_malloc(stackSize));
  if (!stack_) return false;

  // Leave a safety margin of 1MB to prevent overrunning the stack when
  // pushing values (total stack size is 2MB).
  stackLimit_ = reinterpret_cast<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<int32_t>(stack_) + stackSize - 64;

  return true;
}

// When the generated code calls an external reference we need to catch that in
// the simulator.  The external reference will be a function compiled for the
// host architecture.  We need to call that function instead of trying to
// execute it with the simulator.  We do that by redirecting the external
// reference to a swi (software-interrupt) instruction that is handled by
// the simulator.  We write the original destination of the jump just at a known
// offset from the swi instruction so the simulator knows what to call.
class Redirection {
  friend class SimulatorProcess;

  // sim's lock must already be held.
  Redirection(void* nativeFunction, ABIFunctionType type)
      : nativeFunction_(nativeFunction),
        swiInstruction_(kCallRedirInstr),
        type_(type),
        next_(nullptr) {
    next_ = SimulatorProcess::redirection();
    if (!SimulatorProcess::ICacheCheckingDisableCount) {
      FlushICacheLocked(SimulatorProcess::icache(), addressOfSwiInstruction(),
                        SimInstruction::kInstrSize);
    }
    SimulatorProcess::setRedirection(this);
  }

 public:
  void* addressOfSwiInstruction() { return &swiInstruction_; }
  void* nativeFunction() const { return nativeFunction_; }
  ABIFunctionType type() const { return type_; }

  static Redirection* Get(void* nativeFunction, ABIFunctionType type) {
    AutoLockSimulatorCache als;

    Redirection* current = SimulatorProcess::redirection();
    for (; current != nullptr; current = current->next_) {
      if (current->nativeFunction_ == nativeFunction) {
        MOZ_ASSERT(current->type() == type);
        return current;
      }
    }

    AutoEnterOOMUnsafeRegion oomUnsafe;
    Redirection* redir = (Redirection*)js_malloc(sizeof(Redirection));
    if (!redir) {
      oomUnsafe.crash("Simulator redirection");
    }
    new (redir) Redirection(nativeFunction, type);
    return redir;
  }

  static Redirection* FromSwiInstruction(SimInstruction* swiInstruction) {
    uint8_t* addrOfSwi = reinterpret_cast<uint8_t*>(swiInstruction);
    uint8_t* addrOfRedirection =
        addrOfSwi - offsetof(Redirection, swiInstruction_);
    return reinterpret_cast<Redirection*>(addrOfRedirection);
  }

 private:
  void* nativeFunction_;
  uint32_t swiInstruction_;
  ABIFunctionType type_;
  Redirection* next_;
};

Simulator::~Simulator() { js_free(stack_); }

SimulatorProcess::SimulatorProcess()
    : cacheLock_(mutexid::SimulatorCacheLock), redirection_(nullptr) {}

SimulatorProcess::~SimulatorProcess() {
  Redirection* r = redirection_;
  while (r) {
    Redirection* next = r->next_;
    js_delete(r);
    r = next;
  }
}

bool SimulatorProcess::init() {
  if (getenv("MIPS_SIM_ICACHE_CHECKS")) ICacheCheckingDisableCount = 0;

  return icache_.init();
}

/* 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, int32_t value) {
  MOZ_ASSERT((reg >= 0) && (reg < Register::kNumSimuRegisters));
  if (reg == pc) {
    pc_modified_ = true;
  }

  // Zero register always holds 0.
  registers_[reg] = (reg == 0) ? 0 : value;
}

void Simulator::setFpuRegister(int fpureg, int32_t value) {
  MOZ_ASSERT((fpureg >= 0) &&
             (fpureg < Simulator::FPURegister::kNumFPURegisters));
  FPUregisters_[fpureg] = value;
}

void Simulator::setFpuRegisterFloat(int fpureg, float value) {
  MOZ_ASSERT((fpureg >= 0) &&
             (fpureg < Simulator::FPURegister::kNumFPURegisters));
  *mozilla::BitwiseCast<float*>(&FPUregisters_[fpureg]) = value;
}

void Simulator::setFpuRegisterDouble(int fpureg, double value) {
  MOZ_ASSERT((fpureg >= 0) &&
             (fpureg < Simulator::FPURegister::kNumFPURegisters) &&
             ((fpureg % 2) == 0));
  *mozilla::BitwiseCast<double*>(&FPUregisters_[fpureg]) = value;
}

// Get the register from the architecture state. This function does handle
// the special case of accessing the PC register.
int32_t Simulator::getRegister(int reg) const {
  MOZ_ASSERT((reg >= 0) && (reg < Register::kNumSimuRegisters));
  if (reg == 0) return 0;
  return registers_[reg] + ((reg == pc) ? SimInstruction::kPCReadOffset : 0);
}

double Simulator::getDoubleFromRegisterPair(int reg) {
  MOZ_ASSERT((reg >= 0) && (reg < Register::kNumSimuRegisters) &&
             ((reg % 2) == 0));

  double dm_val = 0.0;
  // Read the bits from the unsigned integer register_[] array
  // into the double precision floating point value and return it.
  memcpy(&dm_val, &registers_[reg], sizeof(dm_val));
  return (dm_val);
}

int32_t Simulator::getFpuRegister(int fpureg) const {
  MOZ_ASSERT((fpureg >= 0) &&
             (fpureg < Simulator::FPURegister::kNumFPURegisters));
  return FPUregisters_[fpureg];
}

int64_t Simulator::getFpuRegisterLong(int fpureg) const {
  MOZ_ASSERT((fpureg >= 0) &&
             (fpureg < Simulator::FPURegister::kNumFPURegisters) &&
             ((fpureg % 2) == 0));
  return *mozilla::BitwiseCast<int64_t*>(
      const_cast<int32_t*>(&FPUregisters_[fpureg]));
}

float Simulator::getFpuRegisterFloat(int fpureg) const {
  MOZ_ASSERT((fpureg >= 0) &&
             (fpureg < Simulator::FPURegister::kNumFPURegisters));
  return *mozilla::BitwiseCast<float*>(
      const_cast<int32_t*>(&FPUregisters_[fpureg]));
}

double Simulator::getFpuRegisterDouble(int fpureg) const {
  MOZ_ASSERT((fpureg >= 0) &&
             (fpureg < Simulator::FPURegister::kNumFPURegisters) &&
             ((fpureg % 2) == 0));
  return *mozilla::BitwiseCast<double*>(
      const_cast<int32_t*>(&FPUregisters_[fpureg]));
}

// Runtime FP routines take up to two double arguments and zero
// or one integer arguments. All are constructed here,
// from a0-a3 or f12 and f14.
void Simulator::getFpArgs(double* x, double* y, int32_t* z) {
  *x = getFpuRegisterDouble(12);
  *y = getFpuRegisterDouble(14);
  *z = getRegister(a2);
}

void Simulator::getFpFromStack(int32_t* stack, double* x) {
  MOZ_ASSERT(stack);
  MOZ_ASSERT(x);
  memcpy(x, stack, sizeof(double));
}

void Simulator::setCallResultDouble(double result) {
  setFpuRegisterDouble(f0, result);
}

void Simulator::setCallResultFloat(float result) {
  setFpuRegisterFloat(f0, result);
}

void Simulator::setCallResult(int64_t res) {
  setRegister(v0, static_cast<int32_t>(res));
  setRegister(v1, static_cast<int32_t>(res >> 32));
}

// Helper functions for setting and testing the FCSR register's bits.
void Simulator::setFCSRBit(uint32_t cc, bool value) {
  if (value)
    FCSR_ |= (1 << cc);
  else
    FCSR_ &= ~(1 << cc);
}

bool Simulator::testFCSRBit(uint32_t cc) { return FCSR_ & (1 << cc); }

// Sets the rounding error codes in FCSR based on the result of the rounding.
// Returns true if the operation was invalid.
bool Simulator::setFCSRRoundError(double original, double rounded) {
  bool ret = false;

  setFCSRBit(kFCSRInexactCauseBit, false);
  setFCSRBit(kFCSRUnderflowCauseBit, false);
  setFCSRBit(kFCSROverflowCauseBit, false);
  setFCSRBit(kFCSRInvalidOpCauseBit, false);

  if (!std::isfinite(original) || !std::isfinite(rounded)) {
    setFCSRBit(kFCSRInvalidOpFlagBit, true);
    setFCSRBit(kFCSRInvalidOpCauseBit, true);
    ret = true;
  }

  if (original != rounded) {
    setFCSRBit(kFCSRInexactFlagBit, true);
    setFCSRBit(kFCSRInexactCauseBit, true);
  }

  if (rounded < DBL_MIN && rounded > -DBL_MIN && rounded != 0) {
    setFCSRBit(kFCSRUnderflowFlagBit, true);
    setFCSRBit(kFCSRUnderflowCauseBit, true);
    ret = true;
  }

  if (rounded > INT_MAX || rounded < INT_MIN) {
    setFCSRBit(kFCSROverflowFlagBit, true);
    setFCSRBit(kFCSROverflowCauseBit, true);
    // The reference is not really clear but it seems this is required:
    setFCSRBit(kFCSRInvalidOpFlagBit, true);
    setFCSRBit(kFCSRInvalidOpCauseBit, true);
    ret = true;
  }

  return ret;
}

// Raw access to the PC register.
void Simulator::set_pc(int32_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.
int32_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;
}

// The signal handler only redirects the PC to the interrupt stub when the PC is
// in function code. However, this guard is racy for the simulator since the
// signal handler samples PC in the middle of simulating an instruction and thus
// the current PC may have advanced once since the signal handler's guard. So we
// re-check here.
void Simulator::handleWasmInterrupt() {
  if (!wasm::CodeExists) return;

  void* pc = (void*)get_pc();
  void* fp = (void*)getRegister(Register::fp);

  JitActivation* activation = TlsContext.get()->activation()->asJit();
  const wasm::CodeSegment* segment = wasm::LookupCodeSegment(pc);
  if (!segment || !segment->isModule() || !segment->containsCodePC(pc)) return;

  if (!activation->startWasmInterrupt(registerState())) return;

  set_pc(int32_t(segment->asModule()->interruptCode()));
}

// WebAssembly memories contain an extra region of guard pages (see
// WasmArrayRawBuffer comment). The guard pages catch out-of-bounds accesses
// using a signal handler that redirects PC to a stub that safely reports an
// error. However, if the handler is hit by the simulator, the PC is in C++ code
// and cannot be redirected. Therefore, we must avoid hitting the handler by
// redirecting in the simulator before the real handler would have been hit.
bool Simulator::handleWasmFault(int32_t addr, unsigned numBytes) {
  if (!wasm::CodeExists) return false;

  JSContext* cx = TlsContext.get();
  if (!cx->activation() || !cx->activation()->isJit()) return false;
  JitActivation* act = cx->activation()->asJit();

  void* pc = reinterpret_cast<void*>(get_pc());
  uint8_t* fp = reinterpret_cast<uint8_t*>(getRegister(Register::fp));

  const wasm::CodeSegment* segment = wasm::LookupCodeSegment(pc);
  if (!segment || !segment->isModule()) return false;
  const wasm::ModuleSegment* moduleSegment = segment->asModule();

  wasm::Instance* instance =
      wasm::LookupFaultingInstance(*moduleSegment, pc, fp);
  if (!instance) return false;

  MOZ_RELEASE_ASSERT(&instance->code() == &moduleSegment->code());

  if (!instance->memoryAccessInGuardRegion((uint8_t*)addr, numBytes))
    return false;

  LLBit_ = false;

  const wasm::MemoryAccess* memoryAccess =
      instance->code().lookupMemoryAccess(pc);
  if (!memoryAccess) {
    MOZ_ALWAYS_TRUE(act->startWasmInterrupt(registerState()));
    if (!instance->code().containsCodePC(pc))
      MOZ_CRASH("Cannot map PC to trap handler");
    set_pc(int32_t(moduleSegment->outOfBoundsCode()));
    return true;
  }

  MOZ_ASSERT(memoryAccess->hasTrapOutOfLineCode());
  set_pc(int32_t(memoryAccess->trapOutOfLineCode(moduleSegment->base())));
  return true;
}

bool Simulator::handleWasmTrapFault() {
  if (!wasm::CodeExists) return false;

  JSContext* cx = TlsContext.get();
  if (!cx->activation() || !cx->activation()->isJit()) return false;
  JitActivation* act = cx->activation()->asJit();

  void* pc = reinterpret_cast<void*>(get_pc());

  const wasm::CodeSegment* segment = wasm::LookupCodeSegment(pc);
  if (!segment || !segment->isModule()) return false;
  const wasm::ModuleSegment* moduleSegment = segment->asModule();

  wasm::Trap trap;
  wasm::BytecodeOffset bytecode;
  if (!moduleSegment->code().lookupTrap(pc, &trap, &bytecode)) return false;

  act->startWasmTrap(trap, bytecode.offset, registerState());
  set_pc(int32_t(moduleSegment->trapCode()));
  return true;
}

// MIPS memory instructions (except lwl/r and swl/r) trap on unaligned memory
// access enabling the OS to handle them via trap-and-emulate.
// Note that simulator runs have the runtime system running directly on the host
// system and only generated code is executed in the simulator.
// Since the host is typically IA32 it will not trap on unaligned memory access.
// We assume that that executing correct generated code will not produce
// unaligned memory access, so we explicitly check for address alignment and
// trap. Note that trapping does not occur when executing wasm code, which
// requires that unaligned memory access provides correct result.
int Simulator::readW(uint32_t addr, SimInstruction* instr) {
  if (handleWasmFault(addr, 4)) return -1;

  if ((addr & kPointerAlignmentMask) == 0 ||
      wasm::InCompiledCode(reinterpret_cast<void*>(get_pc()))) {
    intptr_t* ptr = reinterpret_cast<intptr_t*>(addr);
    return *ptr;
  }
  printf("Unaligned read at 0x%08x, pc=0x%08" PRIxPTR "\n", addr,
         reinterpret_cast<intptr_t>(instr));
  MOZ_CRASH();
  return 0;
}

void Simulator::writeW(uint32_t addr, int value, SimInstruction* instr) {
  if (handleWasmFault(addr, 4)) return;

  if ((addr & kPointerAlignmentMask) == 0 ||
      wasm::InCompiledCode(reinterpret_cast<void*>(get_pc()))) {
    intptr_t* ptr = reinterpret_cast<intptr_t*>(addr);
    LLBit_ = false;
    *ptr = value;
    return;
  }
  printf("Unaligned write at 0x%08x, pc=0x%08" PRIxPTR "\n", addr,
         reinterpret_cast<intptr_t>(instr));
  MOZ_CRASH();
}

double Simulator::readD(uint32_t addr, SimInstruction* instr) {
  if (handleWasmFault(addr, 8)) return NAN;

  if ((addr & kDoubleAlignmentMask) == 0 ||
      wasm::InCompiledCode(reinterpret_cast<void*>(get_pc()))) {
    double* ptr = reinterpret_cast<double*>(addr);
    return *ptr;
  }
  printf("Unaligned (double) read at 0x%08x, pc=0x%08" PRIxPTR "\n", addr,
         reinterpret_cast<intptr_t>(instr));
  MOZ_CRASH();
  return 0;
}

void Simulator::writeD(uint32_t addr, double value, SimInstruction* instr) {
  if (handleWasmFault(addr, 8)) return;

  if ((addr & kDoubleAlignmentMask) == 0 ||
      wasm::InCompiledCode(reinterpret_cast<void*>(get_pc()))) {
    double* ptr = reinterpret_cast<double*>(addr);
    LLBit_ = false;
    *ptr = value;
    return;
  }
  printf("Unaligned (double) write at 0x%08x, pc=0x%08" PRIxPTR "\n", addr,
         reinterpret_cast<intptr_t>(instr));
  MOZ_CRASH();
}

uint16_t Simulator::readHU(uint32_t addr, SimInstruction* instr) {
  if (handleWasmFault(addr, 2)) return 0xffff;

  if ((addr & 1) == 0 ||
      wasm::InCompiledCode(reinterpret_cast<void*>(get_pc()))) {
    uint16_t* ptr = reinterpret_cast<uint16_t*>(addr);
    return *ptr;
  }
  printf("Unaligned unsigned halfword read at 0x%08x, pc=0x%08" PRIxPTR "\n",
         addr, reinterpret_cast<intptr_t>(instr));
  MOZ_CRASH();
  return 0;
}

int16_t Simulator::readH(uint32_t addr, SimInstruction* instr) {
  if (handleWasmFault(addr, 2)) return -1;

  if ((addr & 1) == 0 ||
      wasm::InCompiledCode(reinterpret_cast<void*>(get_pc()))) {
    int16_t* ptr = reinterpret_cast<int16_t*>(addr);
    return *ptr;
  }
  printf("Unaligned signed halfword read at 0x%08x, pc=0x%08" PRIxPTR "\n",
         addr, reinterpret_cast<intptr_t>(instr));
  MOZ_CRASH();
  return 0;
}

void Simulator::writeH(uint32_t addr, uint16_t value, SimInstruction* instr) {
  if (handleWasmFault(addr, 2)) return;

  if ((addr & 1) == 0 ||
      wasm::InCompiledCode(reinterpret_cast<void*>(get_pc()))) {
    uint16_t* ptr = reinterpret_cast<uint16_t*>(addr);
    LLBit_ = false;
    *ptr = value;
    return;
  }
  printf("Unaligned unsigned halfword write at 0x%08x, pc=0x%08" PRIxPTR "\n",
         addr, reinterpret_cast<intptr_t>(instr));
  MOZ_CRASH();
}

void Simulator::writeH(uint32_t addr, int16_t value, SimInstruction* instr) {
  if (handleWasmFault(addr, 2)) return;

  if ((addr & 1) == 0 ||
      wasm::InCompiledCode(reinterpret_cast<void*>(get_pc()))) {
    int16_t* ptr = reinterpret_cast<int16_t*>(addr);
    LLBit_ = false;
    *ptr = value;
    return;
  }
  printf("Unaligned halfword write at 0x%08x, pc=0x%08" PRIxPTR "\n", addr,
         reinterpret_cast<intptr_t>(instr));
  MOZ_CRASH();
}

uint32_t Simulator::readBU(uint32_t addr) {
  if (handleWasmFault(addr, 1)) return 0xff;

  uint8_t* ptr = reinterpret_cast<uint8_t*>(addr);
  return *ptr;
}

int32_t Simulator::readB(uint32_t addr) {
  if (handleWasmFault(addr, 1)) return -1;

  int8_t* ptr = reinterpret_cast<int8_t*>(addr);
  return *ptr;
}

void Simulator::writeB(uint32_t addr, uint8_t value) {
  if (handleWasmFault(addr, 1)) return;

  uint8_t* ptr = reinterpret_cast<uint8_t*>(addr);
  LLBit_ = false;
  *ptr = value;
}

void Simulator::writeB(uint32_t addr, int8_t value) {
  if (handleWasmFault(addr, 1)) return;

  int8_t* ptr = reinterpret_cast<int8_t*>(addr);
  LLBit_ = false;
  *ptr = value;
}

int Simulator::loadLinkedW(uint32_t addr, SimInstruction* instr) {
  if ((addr & kPointerAlignmentMask) == 0) {
    if (handleWasmFault(addr, 1)) 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 read at 0x%08x, pc=0x%08" PRIxPTR "\n", addr,
         reinterpret_cast<intptr_t>(instr));
  MOZ_CRASH();
  return 0;
}

int Simulator::storeConditionalW(uint32_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%08x, pc=0x%08" PRIxPTR ", expected: 0x%08x\n",
           addr, reinterpret_cast<intptr_t>(instr), LLAddr_);
    MOZ_CRASH();
  }

  if ((addr & kPointerAlignmentMask) == 0) {
    SharedMem<int32_t*> ptr =
        SharedMem<int32_t*>::shared(reinterpret_cast<int32_t*>(addr));

    if (!LLBit_) {
      return 0;
    }

    LLBit_ = false;
    LLAddr_ = 0;
    int32_t expected = lastLLValue_;
    int32_t old =
        AtomicOperations::compareExchangeSeqCst(ptr, expected, int32_t(value));
    return (old == expected) ? 1 : 0;
  }
  printf("Unaligned SC at 0x%08x, pc=0x%08" PRIxPTR "\n", addr,
         reinterpret_cast<intptr_t>(instr));
  MOZ_CRASH();
  return 0;
}

uintptr_t Simulator::stackLimit() const { return stackLimit_; }

uintptr_t* Simulator::addressOfStackLimit() { return &stackLimit_; }

bool Simulator::overRecursed(uintptr_t newsp) const {
  if (newsp == 0) newsp = getRegister(sp);
  return newsp <= stackLimit();
}

bool Simulator::overRecursedWithExtra(uint32_t extra) const {
  uintptr_t newsp = getRegister(sp) - extra;
  return newsp <= stackLimit();
}

// Unsupported instructions use format to print an error and stop execution.
void Simulator::format(SimInstruction* instr, const char* format) {
  printf("Simulator found unsupported instruction:\n 0x%08" PRIxPTR ": %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)(int32_t arg0);
typedef int64_t (*Prototype_General2)(int32_t arg0, int32_t arg1);
typedef int64_t (*Prototype_General3)(int32_t arg0, int32_t arg1, int32_t arg2);
typedef int64_t (*Prototype_General4)(int32_t arg0, int32_t arg1, int32_t arg2,
                                      int32_t arg3);
typedef int64_t (*Prototype_General5)(int32_t arg0, int32_t arg1, int32_t arg2,
                                      int32_t arg3, int32_t arg4);
typedef int64_t (*Prototype_General6)(int32_t arg0, int32_t arg1, int32_t arg2,
                                      int32_t arg3, int32_t arg4, int32_t arg5);
typedef int64_t (*Prototype_General7)(int32_t arg0, int32_t arg1, int32_t arg2,
                                      int32_t arg3, int32_t arg4, int32_t arg5,
                                      int32_t arg6);
typedef int64_t (*Prototype_General8)(int32_t arg0, int32_t arg1, int32_t arg2,
                                      int32_t arg3, int32_t arg4, int32_t arg5,
                                      int32_t arg6, int32_t arg7);
typedef int64_t (*Prototype_GeneralGeneralGeneralInt64)(int32_t arg0,
                                                        int32_t arg1,
                                                        int32_t arg2,
                                                        int64_t arg3);
typedef int64_t (*Prototype_GeneralGeneralInt64Int64)(int32_t arg0,
                                                      int32_t arg1,
                                                      int64_t arg2,
                                                      int64_t arg3);

typedef double (*Prototype_Double_None)();
typedef double (*Prototype_Double_Double)(double arg0);
typedef double (*Prototype_Double_Int)(int32_t arg0);
typedef int32_t (*Prototype_Int_Double)(double arg0);
typedef int64_t (*Prototype_Int64_Double)(double arg0);
typedef int32_t (*Prototype_Int_DoubleIntInt)(double arg0, int32_t arg1,
                                              int32_t arg2);
typedef int32_t (*Prototype_Int_IntDoubleIntInt)(int32_t arg0, double arg1,
                                                 int32_t arg2, int32_t arg3);
typedef float (*Prototype_Float32_Float32)(float arg0);
typedef float (*Prototype_Float32_Float32Float32)(float arg0, float arg1);
typedef float (*Prototype_Float32_IntInt)(int arg0, int arg1);

typedef double (*Prototype_DoubleInt)(double arg0, int32_t arg1);
typedef double (*Prototype_Double_IntInt)(int32_t arg0, int32_t arg1);
typedef double (*Prototype_Double_IntDouble)(int32_t arg0, double arg1);
typedef double (*Prototype_Double_DoubleDouble)(double arg0, double arg1);
typedef int32_t (*Prototype_Int_IntDouble)(int32_t 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);

static int64_t MakeInt64(int32_t first, int32_t second) {
  // Little-endian order.
  return ((int64_t)second << 32) | (uint32_t)first;
}

// Software interrupt instructions are used by the simulator to call into C++.
void Simulator::softwareInterrupt(SimInstruction* instr) {
  int32_t func = instr->functionFieldRaw();
  uint32_t code = (func == ff_break) ? instr->bits(25, 6) : -1;

  // We first check if we met a call_rt_redirected.
  if (instr->instructionBits() == kCallRedirInstr) {
#if !defined(USES_O32_ABI)
    MOZ_CRASH("Only O32 ABI supported.");
#else
    Redirection* redirection = Redirection::FromSwiInstruction(instr);
    int32_t arg0 = getRegister(a0);
    int32_t arg1 = getRegister(a1);
    int32_t arg2 = getRegister(a2);
    int32_t arg3 = getRegister(a3);

    int32_t* stack_pointer = reinterpret_cast<int32_t*>(getRegister(sp));
    // Args 4 and 5 are on the stack after the reserved space for args 0..3.
    int32_t arg4 = stack_pointer[4];
    int32_t arg5 = stack_pointer[5];

    // This is dodgy but it works because the C entry stubs are never moved.
    // See comment in codegen-arm.cc and bug 1242173.
    int32_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();
    }

    switch (redirection->type()) {
      case Args_General0: {
        Prototype_General0 target =
            reinterpret_cast<Prototype_General0>(external);
        int64_t result = target();
        setCallResult(result);
        break;
      }
      case Args_General1: {
        Prototype_General1 target =
            reinterpret_cast<Prototype_General1>(external);
        int64_t result = target(arg0);
        setCallResult(result);
        break;
      }
      case Args_General2: {
        Prototype_General2 target =
            reinterpret_cast<Prototype_General2>(external);
        int64_t result = target(arg0, arg1);
        setCallResult(result);
        break;
      }
      case Args_General3: {
        Prototype_General3 target =
            reinterpret_cast<Prototype_General3>(external);
        int64_t result = target(arg0, arg1, arg2);
        setCallResult(result);
        break;
      }
      case Args_General4: {
        Prototype_General4 target =
            reinterpret_cast<Prototype_General4>(external);
        int64_t result = target(arg0, arg1, arg2, arg3);
        setCallResult(result);
        break;
      }
      case Args_General5: {
        Prototype_General5 target =
            reinterpret_cast<Prototype_General5>(external);
        int64_t result = target(arg0, arg1, arg2, arg3, arg4);
        setCallResult(result);
        break;
      }
      case Args_General6: {
        Prototype_General6 target =
            reinterpret_cast<Prototype_General6>(external);
        int64_t result = target(arg0, arg1, arg2, arg3, arg4, arg5);
        setCallResult(result);
        break;
      }
      case Args_General7: {
        Prototype_General7 target =
            reinterpret_cast<Prototype_General7>(external);
        int32_t arg6 = stack_pointer[6];
        int64_t result = target(arg0, arg1, arg2, arg3, arg4, arg5, arg6);
        setCallResult(result);
        break;
      }
      case Args_General8: {
        Prototype_General8 target =
            reinterpret_cast<Prototype_General8>(external);
        int32_t arg6 = stack_pointer[6];
        int32_t arg7 = stack_pointer[7];
        int64_t result = target(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
        setCallResult(result);
        break;
      }
      case Args_Double_None: {
        Prototype_Double_None target =
            reinterpret_cast<Prototype_Double_None>(external);
        double dresult = target();
        setCallResultDouble(dresult);
        break;
      }
      case Args_Int_Double: {
        double dval0, dval1;
        int32_t ival;
        getFpArgs(&dval0, &dval1, &ival);
        Prototype_Int_Double target =
            reinterpret_cast<Prototype_Int_Double>(external);
        int32_t res = target(dval0);
        setRegister(v0, res);
        break;
      }
      case Args_Int_GeneralGeneralGeneralInt64: {
        Prototype_GeneralGeneralGeneralInt64 target =
            reinterpret_cast<Prototype_GeneralGeneralGeneralInt64>(external);
        // The int64 arg is not split across register and stack
        int64_t result = target(arg0, arg1, arg2, MakeInt64(arg4, arg5));
        setCallResult(result);
        break;
      }
      case Args_Int_GeneralGeneralInt64Int64: {
        Prototype_GeneralGeneralInt64Int64 target =
            reinterpret_cast<Prototype_GeneralGeneralInt64Int64>(external);
        int64_t result =
            target(arg0, arg1, MakeInt64(arg2, arg3), MakeInt64(arg4, arg5));
        setCallResult(result);
        break;
      }
      case Args_Int64_Double: {
        double dval0, dval1;
        int32_t ival;
        getFpArgs(&dval0, &dval1, &ival);
        Prototype_Int64_Double target =
            reinterpret_cast<Prototype_Int64_Double>(external);
        int64_t result = target(dval0);
        setCallResult(result);
        break;
      }
      case Args_Int_DoubleIntInt: {
        double dval = getFpuRegisterDouble(12);
        Prototype_Int_DoubleIntInt target =
            reinterpret_cast<Prototype_Int_DoubleIntInt>(external);
        int32_t res = target(dval, arg2, arg3);
        setRegister(v0, res);
        break;
      }
      case Args_Int_IntDoubleIntInt: {
        double dval = getDoubleFromRegisterPair(a2);
        Prototype_Int_IntDoubleIntInt target =
            reinterpret_cast<Prototype_Int_IntDoubleIntInt>(external);
        int32_t res = target(arg0, dval, arg4, arg5);
        setRegister(v0, res);
        break;
      }
      case Args_Double_Double: {
        double dval0, dval1;
        int32_t ival;
        getFpArgs(&dval0, &dval1, &ival);
        Prototype_Double_Double target =
            reinterpret_cast<Prototype_Double_Double>(external);
        double dresult = target(dval0);
        setCallResultDouble(dresult);
        break;
      }
      case Args_Float32_Float32: {
        float fval0;
        fval0 = getFpuRegisterFloat(12);
        Prototype_Float32_Float32 target =
            reinterpret_cast<Prototype_Float32_Float32>(external);
        float fresult = target(fval0);
        setCallResultFloat(fresult);
        break;
      }
      case Args_Float32_Float32Float32: {
        float fval0;
        float fval1;
        fval0 = getFpuRegisterFloat(12);
        fval1 = getFpuRegisterFloat(14);
        Prototype_Float32_Float32Float32 target =
            reinterpret_cast<Prototype_Float32_Float32Float32>(external);
        float fresult = target(fval0, fval1);
        setCallResultFloat(fresult);
        break;
      }
      case Args_Float32_IntInt: {
        Prototype_Float32_IntInt target =
            reinterpret_cast<Prototype_Float32_IntInt>(external);
        float fresult = target(arg0, arg1);
        setCallResultFloat(fresult);
        break;
      }
      case Args_Double_Int: {
        Prototype_Double_Int target =
            reinterpret_cast<Prototype_Double_Int>(external);
        double dresult = target(arg0);
        setCallResultDouble(dresult);
        break;
      }
      case Args_Double_IntInt: {
        Prototype_Double_IntInt target =
            reinterpret_cast<Prototype_Double_IntInt>(external);
        double dresult = target(arg0, arg1);
        setCallResultDouble(dresult);
        break;
      }
      case Args_Double_DoubleInt: {
        double dval0, dval1;
        int32_t ival;
        getFpArgs(&dval0, &dval1, &ival);
        Prototype_DoubleInt target =
            reinterpret_cast<Prototype_DoubleInt>(external);
        double dresult = target(dval0, ival);
        setCallResultDouble(dresult);
        break;
      }
      case Args_Double_DoubleDouble: {
        double dval0, dval1;
        int32_t ival;
        getFpArgs(&dval0, &dval1, &ival);
        Prototype_Double_DoubleDouble target =
            reinterpret_cast<Prototype_Double_DoubleDouble>(external);
        double dresult = target(dval0, dval1);
        setCallResultDouble(dresult);
        break;
      }
      case Args_Double_IntDouble: {
        int32_t ival = getRegister(a0);
        double dval0 = getDoubleFromRegisterPair(a2);
        Prototype_Double_IntDouble target =
            reinterpret_cast<Prototype_Double_IntDouble>(external);
        double dresult = target(ival, dval0);
        setCallResultDouble(dresult);
        break;
      }
      case Args_Int_IntDouble: {
        int32_t ival = getRegister(a0);
        double dval0 = getDoubleFromRegisterPair(a2);
        Prototype_Int_IntDouble target =
            reinterpret_cast<Prototype_Int_IntDouble>(external);
        int32_t result = target(ival, dval0);
        setRegister(v0, result);
        break;
      }
      case Args_Double_DoubleDoubleDouble: {
        double dval0, dval1, dval2;
        int32_t ival;
        getFpArgs(&dval0, &dval1, &ival);
        // the last argument is on stack
        getFpFromStack(stack_pointer + 4, &dval2);
        Prototype_Double_DoubleDoubleDouble target =
            reinterpret_cast<Prototype_Double_DoubleDoubleDouble>(external);
        double dresult = target(dval0, dval1, dval2);
        setCallResultDouble(dresult);
        break;
      }
      case Args_Double_DoubleDoubleDoubleDouble: {
        double dval0, dval1, dval2, dval3;
        int32_t ival;
        getFpArgs(&dval0, &dval1, &ival);
        // the two last arguments are on stack
        getFpFromStack(stack_pointer + 4, &dval2);
        getFpFromStack(stack_pointer + 6, &dval3);
        Prototype_Double_DoubleDoubleDoubleDouble target =
            reinterpret_cast<Prototype_Double_DoubleDoubleDoubleDouble>(
                external);
        double dresult = target(dval0, dval1, dval2, dval3);
        setCallResultDouble(dresult);
        break;
      }
      default:
        MOZ_CRASH("call");
    }

    setRegister(ra, saved_ra);
    set_pc(getRegister(ra));
#endif
  } else if (func == ff_break && code <= kMaxStopCode) {
    if (isWatchpoint(code)) {
      printWatchpoint(code);
    } else {
      increaseStopCounter(code);
      handleStop(code, instr);
    }
  } else {
    switch (func) {
      case ff_tge:
      case ff_tgeu:
      case ff_tlt:
      case ff_tltu:
      case ff_teq:
      case ff_tne:
        if (instr->bits(15, 6) == kWasmTrapCode && handleWasmTrapFault())
          return;
    };
    // All remaining break_ codes, and all traps are handled here.
    MipsDebugger dbg(this);
    dbg.debug();
  }
}

// Stop helper functions.
bool Simulator::isWatchpoint(uint32_t code) {
  return (code <= kMaxWatchpointCode);
}

void Simulator::printWatchpoint(uint32_t code) {
  MipsDebugger dbg(this);
  ++break_count_;
  printf(
      "\n---- break %d marker: %3d  (instr count: %8d) ----------"
      "----------------------------------",
      code, break_count_, icount_);
  dbg.printAllRegs();  // Print registers and continue running.
}

void Simulator::handleStop(uint32_t code, SimInstruction* instr) {
  // Stop if it is enabled, otherwise go on jumping over the stop
  // and the message address.
  if (isEnabledStop(code)) {
    MipsDebugger dbg(this);
    dbg.stop(instr);
  } else {
    set_pc(get_pc() + 2 * SimInstruction::kInstrSize);
  }
}

bool Simulator::isStopInstruction(SimInstruction* instr) {
  int32_t func = instr->functionFieldRaw();
  uint32_t code = static_cast<uint32_t>(instr->bits(25, 6));
  return (func == ff_break) && code > kMaxWatchpointCode &&
         code <= kMaxStopCode;
}

bool Simulator::isEnabledStop(uint32_t code) {
  MOZ_ASSERT(code <= kMaxStopCode);
  MOZ_ASSERT(code > kMaxWatchpointCode);
  return !(watchedStops_[code].count_ & kStopDisabledBit);
}

void Simulator::enableStop(uint32_t code) {
  if (!isEnabledStop(code)) watchedStops_[code].count_ &= ~kStopDisabledBit;
}

void Simulator::disableStop(uint32_t code) {
  if (isEnabledStop(code)) watchedStops_[code].count_ |= kStopDisabledBit;
}

void Simulator::increaseStopCounter(uint32_t code) {
  MOZ_ASSERT(code <= kMaxStopCode);
  if ((watchedStops_[code].count_ & ~(1 << 31)) == 0x7fffffff) {
    printf(
        "Stop counter for code %i has overflowed.\n"
        "Enabling this code and reseting the counter to 0.\n",
        code);
    watchedStops_[code].count_ = 0;
    enableStop(code);
  } else {
    watchedStops_[code].count_++;
  }
}

// Print a stop status.
void Simulator::printStopInfo(uint32_t code) {
  if (code <= kMaxWatchpointCode) {
    printf("That is a watchpoint, not a stop.\n");
    return;
  } else if (code > kMaxStopCode) {
    printf("Code too large, only %u stops can be used\n", kMaxStopCode + 1);
    return;
  }
  const char* state = isEnabledStop(code) ? "Enabled" : "Disabled";
  int32_t count = watchedStops_[code].count_ & ~kStopDisabledBit;
  // Don't print the state of unused breakpoints.
  if (count != 0) {
    if (watchedStops_[code].desc_) {
      printf("stop %i - 0x%x: \t%s, \tcounter = %i, \t%s\n", code, code, state,
             count, watchedStops_[code].desc_);
    } else {
      printf("stop %i - 0x%x: \t%s, \tcounter = %i\n", code, code, state,
             count);
    }
  }
}

void Simulator::signalExceptions() {
  for (int i = 1; i < kNumExceptions; i++) {
    if (exceptions[i] != 0) MOZ_CRASH("Error: Exception raised.");
  }
}

// Handle execution based on instruction types.
void Simulator::configureTypeRegister(SimInstruction* instr, int32_t& alu_out,
                                      int64_t& i64hilo, uint64_t& u64hilo,
                                      int32_t& next_pc,
                                      int32_t& return_addr_reg,
                                      bool& do_interrupt) {
  // Every local variable declared here needs to be const.
  // This is to make sure that changed values are sent back to
  // decodeTypeRegister correctly.

  // Instruction fields.
  const Opcode op = instr->opcodeFieldRaw();
  const int32_t rs_reg = instr->rsValue();
  const int32_t rs = getRegister(rs_reg);
  const uint32_t rs_u = static_cast<uint32_t>(rs);
  const int32_t rt_reg = instr->rtValue();
  const int32_t rt = getRegister(rt_reg);
  const uint32_t rt_u = static_cast<uint32_t>(rt);
  const int32_t rd_reg = instr->rdValue();
  const uint32_t sa = instr->saValue();

  const int32_t fs_reg = instr->fsValue();

  // ---------- Configuration.
  switch (op) {
    case op_cop1:  // Coprocessor instructions.
      switch (instr->rsFieldRaw()) {
        case rs_bc1:  // Handled in DecodeTypeImmed, should never come here.
          MOZ_CRASH();
          break;
        case rs_cfc1:
          // At the moment only FCSR is supported.
          MOZ_ASSERT(fs_reg == kFCSRRegister);
          alu_out = FCSR_;
          break;
        case rs_mfc1:
          alu_out = getFpuRegister(fs_reg);
          break;
        case rs_mfhc1:
          MOZ_CRASH();
          break;
        case rs_ctc1:
        case rs_mtc1:
        case rs_mthc1:
          // Do the store in the execution step.
          break;
        case rs_s:
        case rs_d:
        case rs_w:
        case rs_l:
        case rs_ps:
          // Do everything in the execution step.
          break;
        default:
          MOZ_CRASH();
      }
      break;
    case op_cop1x:
      break;
    case op_special:
      switch (instr->functionFieldRaw()) {
        case ff_jr:
        case ff_jalr:
          next_pc = getRegister(instr->rsValue());
          return_addr_reg = instr->rdValue();
          break;
        case ff_sll:
          alu_out = rt << sa;
          break;
        case ff_srl:
          if (rs_reg == 0) {
            // Regular logical right shift of a word by a fixed number of
            // bits instruction. RS field is always equal to 0.
            alu_out = rt_u >> sa;
          } else {
            // Logical right-rotate of a word by a fixed number of bits. This
            // is special case of SRL instruction, added in MIPS32 Release 2.
            // RS field is equal to 00001.
            alu_out = (rt_u >> sa) | (rt_u << (32 - sa));
          }
          break;
        case ff_sra:
          alu_out = rt >> sa;
          break;
        case ff_sllv:
          alu_out = rt << rs;
          break;
        case ff_srlv:
          if (sa == 0) {
            // Regular logical right-shift of a word by a variable number of
            // bits instruction. SA field is always equal to 0.
            alu_out = rt_u >> rs;
          } else {
            // Logical right-rotate of a word by a variable number of bits.
            // This is special case od SRLV instruction, added in MIPS32
            // Release 2. SA field is equal to 00001.
            alu_out = (rt_u >> rs_u) | (rt_u << (32 - rs_u));
          }
          break;
        case ff_srav:
          alu_out = rt >> rs;
          break;
        case ff_mfhi:
          alu_out = getRegister(HI);
          break;
        case ff_mflo:
          alu_out = getRegister(LO);
          break;
        case ff_mult:
          i64hilo = static_cast<int64_t>(rs) * static_cast<int64_t>(rt);
          break;
        case ff_multu:
          u64hilo = static_cast<uint64_t>(rs_u) * static_cast<uint64_t>(rt_u);
          break;
        case ff_add:
          if (HaveSameSign(rs, rt)) {
            if (rs > 0) {
              exceptions[kIntegerOverflow] = rs > (kRegisterskMaxValue - rt);
            } else if (rs < 0) {
              exceptions[kIntegerUnderflow] = rs < (kRegisterskMinValue - rt);
            }
          }
          alu_out = rs + rt;
          break;
        case ff_addu:
          alu_out = rs + rt;
          break;
        case ff_sub:
          if (!HaveSameSign(rs, rt)) {
            if (rs > 0) {
              exceptions[kIntegerOverflow] = rs > (kRegisterskMaxValue + rt);
            } else if (rs < 0) {
              exceptions[kIntegerUnderflow] = rs < (kRegisterskMinValue + rt);
            }
          }
          alu_out = rs - rt;
          break;
        case ff_subu:
          alu_out = rs - rt;
          break;
        case ff_and:
          alu_out = rs & rt;
          break;
        case ff_or:
          alu_out = rs | rt;
          break;
        case ff_xor:
          alu_out = rs ^ rt;
          break;
        case ff_nor:
          alu_out = ~(rs | rt);
          break;
        case ff_slt:
          alu_out = rs < rt ? 1 : 0;
          break;
        case ff_sltu:
          alu_out = rs_u < rt_u ? 1 : 0;
          break;
          // Break and trap instructions.
        case ff_break:
          do_interrupt = true;
          break;
        case ff_tge:
          do_interrupt = rs >= rt;
          break;
        case ff_tgeu:
          do_interrupt = rs_u >= rt_u;
          break;
        case ff_tlt:
          do_interrupt = rs < rt;
          break;
        case ff_tltu:
          do_interrupt = rs_u < rt_u;
          break;
        case ff_teq:
          do_interrupt = rs == rt;
          break;
        case ff_tne:
          do_interrupt = rs != rt;
          break;
        case ff_movn:
        case ff_movz:
        case ff_movci:
        case ff_sync:
          // No action taken on decode.
          break;
        case ff_div:
        case ff_divu:
          // div and divu never raise exceptions.
          break;
        default:
          MOZ_CRASH();
      }
      break;
    case op_special2:
      switch (instr->functionFieldRaw()) {
        case ff_mul:
          alu_out = rs_u * rt_u;  // Only the lower 32 bits are kept.
          break;
        case ff_mult:
          i64hilo = static_cast<int64_t>(rs) * static_cast<int64_t>(rt);
          break;
        case ff_multu:
          u64hilo = static_cast<uint64_t>(rs_u) * static_cast<uint64_t>(rt_u);
          break;
        case ff_madd:
          i64hilo += static_cast<int64_t>(rs) * static_cast<int64_t>(rt);
          break;
        case ff_maddu:
          u64hilo += static_cast<uint64_t>(rs_u) * static_cast<uint64_t>(rt_u);
          break;
        case ff_clz:
          alu_out = rs_u ? __builtin_clz(rs_u) : 32;
          break;
        default:
          MOZ_CRASH();
      }
      break;
    case op_special3:
      switch (instr->functionFieldRaw()) {
        case ff_ins: {  // Mips32r2 instruction.
          // Interpret rd field as 5-bit msb of insert.
          uint16_t msb = rd_reg;
          // Interpret sa field as 5-bit lsb of insert.
          uint16_t lsb = sa;
          uint16_t size = msb - lsb + 1;
          uint32_t mask = (1 << size) - 1;
          alu_out = (rt_u & ~(mask << lsb)) | ((rs_u & mask) << lsb);
          break;
        }
        case ff_ext: {  // Mips32r2 instruction.
          // Interpret rd field as 5-bit msb of extract.
          uint16_t msb = rd_reg;
          // Interpret sa field as 5-bit lsb of extract.
          uint16_t lsb = sa;
          uint16_t size = msb + 1;
          uint32_t mask = (1 << size) - 1;
          alu_out = (rs_u & (mask << lsb)) >> lsb;
          break;
        }
        case ff_bshfl: {  // Mips32r2 instruction.
          if (16 == sa)   // seb
            alu_out = I32(I8(rt));
          else if (24 == sa)  // seh
            alu_out = I32(I16(rt));
          else {
            MOZ_CRASH();
          }
          break;
        }
        default:
          MOZ_CRASH();
      }
      break;
    default:
      MOZ_CRASH();
  }
}

void Simulator::decodeTypeRegister(SimInstruction* instr) {
  // Instruction fields.
  const Opcode op = instr->opcodeFieldRaw();
  const int32_t rs_reg = instr->rsValue();
  const int32_t rs = getRegister(rs_reg);
  const uint32_t rs_u = static_cast<uint32_t>(rs);
  const int32_t rt_reg = instr->rtValue();
  const int32_t rt = getRegister(rt_reg);
  const uint32_t rt_u = static_cast<uint32_t>(rt);
  const int32_t rd_reg = instr->rdValue();

  const int32_t fr_reg = instr->frValue();
  const int32_t fs_reg = instr->fsValue();
  const int32_t ft_reg = instr->ftValue();
  const int32_t fd_reg = instr->fdValue();
  int64_t i64hilo = 0;
  uint64_t u64hilo = 0;

  // ALU output.
  // It should not be used as is. Instructions using it should always
  // initialize it first.
  int32_t alu_out = 0x12345678;

  // For break and trap instructions.
  bool do_interrupt = false;

  // For jr and jalr.
  // Get current pc.
  int32_t current_pc = get_pc();
  // Next pc
  int32_t next_pc = 0;
  int32_t return_addr_reg = 31;

  // Set up the variables if needed before executing the instruction.
  configureTypeRegister(instr, alu_out, i64hilo, u64hilo, next_pc,
                        return_addr_reg, do_interrupt);

  // ---------- Raise exceptions triggered.
  signalExceptions();

  // ---------- Execution.
  switch (op) {
    case op_cop1:
      switch (instr->rsFieldRaw()) {
        case rs_bc1:  // Branch on coprocessor condition.
          MOZ_CRASH();
          break;
        case rs_cfc1:
        case rs_mfc1:
          setRegister(rt_reg, alu_out);
          break;
        case rs_mfhc1:
          MOZ_CRASH();
          break;
        case rs_ctc1:
          // At the moment only FCSR is supported.
          MOZ_ASSERT(fs_reg == kFCSRRegister);
          FCSR_ = registers_[rt_reg];
          break;
        case rs_mtc1:
          FPUregisters_[fs_reg] = registers_[rt_reg];
          break;
        case rs_mthc1:
          MOZ_CRASH();
          break;
        case rs_s:
          float f, ft_value, fs_value;
          uint32_t cc, fcsr_cc;
          fs_value = getFpuRegisterFloat(fs_reg);
          ft_value = getFpuRegisterFloat(ft_reg);
          cc = instr->fcccValue();
          fcsr_cc = GetFCSRConditionBit(cc);
          switch (instr->functionFieldRaw()) {
            case ff_add_fmt:
              setFpuRegisterFloat(fd_reg, fs_value + ft_value);
              break;
            case ff_sub_fmt:
              setFpuRegisterFloat(fd_reg, fs_value - ft_value);
              break;
            case ff_mul_fmt:
              setFpuRegisterFloat(fd_reg, fs_value * ft_value);
              break;
            case ff_div_fmt:
              setFpuRegisterFloat(fd_reg, fs_value / ft_value);
              break;
            case ff_abs_fmt:
              setFpuRegisterFloat(fd_reg, fabsf(fs_value));
              break;
            case ff_mov_fmt:
              setFpuRegisterFloat(fd_reg, fs_value);
              break;
            case ff_neg_fmt:
              setFpuRegisterFloat(fd_reg, -fs_value);
              break;
            case ff_sqrt_fmt:
              setFpuRegisterFloat(fd_reg, sqrtf(fs_value));
              break;
            case ff_c_un_fmt:
              setFCSRBit(fcsr_cc,
                         mozilla::IsNaN(fs_value) || mozilla::IsNaN(ft_value));
              break;
            case ff_c_eq_fmt:
              setFCSRBit(fcsr_cc, (fs_value == ft_value));
              break;
            case ff_c_ueq_fmt:
              setFCSRBit(fcsr_cc,
                         (fs_value == ft_value) || (mozilla::IsNaN(fs_value) ||
                                                    mozilla::IsNaN(ft_value)));
              break;
            case ff_c_olt_fmt:
              setFCSRBit(fcsr_cc, (fs_value < ft_value));
              break;
            case ff_c_ult_fmt:
              setFCSRBit(fcsr_cc,
                         (fs_value < ft_value) || (mozilla::IsNaN(fs_value) ||
                                                   mozilla::IsNaN(ft_value)));
              break;
            case ff_c_ole_fmt:
              setFCSRBit(fcsr_cc, (fs_value <= ft_value));
              break;
            case ff_c_ule_fmt:
              setFCSRBit(fcsr_cc,
                         (fs_value <= ft_value) || (mozilla::IsNaN(fs_value) ||
                                                    mozilla::IsNaN(ft_value)));
              break;
            case ff_cvt_d_fmt:
              f = getFpuRegisterFloat(fs_reg);
              setFpuRegisterDouble(fd_reg, static_cast<double>(f));
              break;
            case ff_cvt_w_fmt:  // Convert float to word.
              // Rounding modes are not yet supported.
              MOZ_ASSERT((FCSR_ & 3) == 0);
              // In rounding mode 0 it should behave like ROUND.
              MOZ_FALLTHROUGH;
            case ff_round_w_fmt: {  // Round double to word (round half to
                                    // even).
              float rounded = std::floor(fs_value + 0.5);
              int32_t result = static_cast<int32_t>(rounded);
              if ((result & 1) != 0 && result - fs_value == 0.5) {
                // If the number is halfway between two integers,
                // round to the even one.
                result--;
              }
              setFpuRegister(fd_reg, result);
              if (setFCSRRoundError(fs_value, rounded)) {
                setFpuRegister(fd_reg, kFPUInvalidResult);
              }
              break;
            }
            case ff_trunc_w_fmt: {  // Truncate float to word (round towards 0).
              float rounded = truncf(fs_value);
              int32_t result = static_cast<int32_t>(rounded);
              setFpuRegister(fd_reg, result);
              if (setFCSRRoundError(fs_value, rounded)) {
                setFpuRegister(fd_reg, kFPUInvalidResult);
              }
              break;
            }
            case ff_floor_w_fmt: {  // Round float to word towards negative
                                    // infinity.
              float rounded = std::floor(fs_value);
              int32_t result = static_cast<int32_t>(rounded);
              setFpuRegister(fd_reg, result);
              if (setFCSRRoundError(fs_value, rounded)) {
                setFpuRegister(fd_reg, kFPUInvalidResult);
              }
              break;
            }
            case ff_ceil_w_fmt: {  // Round double to word towards positive
                                   // infinity.
              float rounded = std::ceil(fs_value);
              int32_t result = static_cast<int32_t>(rounded);
              setFpuRegister(fd_reg, result);
              if (setFCSRRoundError(fs_value, rounded)) {
                setFpuRegister(fd_reg, kFPUInvalidResult);
              }
              break;
            }
            case ff_cvt_l_fmt:
            case ff_round_l_fmt:
            case ff_trunc_l_fmt:
            case ff_floor_l_fmt:
            case ff_ceil_l_fmt:
            case ff_cvt_ps_s:
            case ff_c_f_fmt:
              MOZ_CRASH();
              break;
            case ff_movf_fmt:
              // location of cc field in MOVF is equal to float branch
              // instructions
              cc = instr->fbccValue();
              fcsr_cc = GetFCSRConditionBit(cc);
              if (testFCSRBit(fcsr_cc)) {
                setFpuRegisterFloat(fd_reg, getFpuRegisterFloat(fs_reg));
              }
              break;
            case ff_movz_fmt:
              if (rt == 0) {
                setFpuRegisterFloat(fd_reg, getFpuRegisterFloat(fs_reg));
              }
              break;
            case ff_movn_fmt:
              if (rt != 0) {
                setFpuRegisterFloat(fd_reg, getFpuRegisterFloat(fs_reg));
              }
              break;
            default:
              MOZ_CRASH();
          }
          break;
        case rs_d:
          double dt_value, ds_value;
          ds_value = getFpuRegisterDouble(fs_reg);
          cc = instr->fcccValue();
          fcsr_cc = GetFCSRConditionBit(cc);
          switch (instr->functionFieldRaw()) {
            case ff_add_fmt:
              dt_value = getFpuRegisterDouble(ft_reg);
              setFpuRegisterDouble(fd_reg, ds_value + dt_value);
              break;
            case ff_sub_fmt:
              dt_value = getFpuRegisterDouble(ft_reg);
              setFpuRegisterDouble(fd_reg, ds_value - dt_value);
              break;
            case ff_mul_fmt:
              dt_value = getFpuRegisterDouble(ft_reg);
              setFpuRegisterDouble(fd_reg, ds_value * dt_value);
              break;
            case ff_div_fmt:
              dt_value = getFpuRegisterDouble(ft_reg);
              setFpuRegisterDouble(fd_reg, ds_value / dt_value);
              break;
            case ff_abs_fmt:
              setFpuRegisterDouble(fd_reg, fabs(ds_value));
              break;
            case ff_mov_fmt:
              setFpuRegisterDouble(fd_reg, ds_value);
              break;
            case ff_neg_fmt:
              setFpuRegisterDouble(fd_reg, -ds_value);
              break;
            case ff_sqrt_fmt:
              setFpuRegisterDouble(fd_reg, sqrt(ds_value));
              break;
            case ff_c_un_fmt:
              dt_value = getFpuRegisterDouble(ft_reg);
              setFCSRBit(fcsr_cc,
                         mozilla::IsNaN(ds_value) || mozilla::IsNaN(dt_value));
              break;
            case ff_c_eq_fmt:
              dt_value = getFpuRegisterDouble(ft_reg);
              setFCSRBit(fcsr_cc, (ds_value == dt_value));
              break;
            case ff_c_ueq_fmt:
              dt_value = getFpuRegisterDouble(ft_reg);
              setFCSRBit(fcsr_cc,
                         (ds_value == dt_value) || (mozilla::IsNaN(ds_value) ||
                                                    mozilla::IsNaN(dt_value)));
              break;
            case ff_c_olt_fmt:
              dt_value = getFpuRegisterDouble(ft_reg);
              setFCSRBit(fcsr_cc, (ds_value < dt_value));
              break;
            case ff_c_ult_fmt:
              dt_value = getFpuRegisterDouble(ft_reg);
              setFCSRBit(fcsr_cc,
                         (ds_value < dt_value) || (mozilla::IsNaN(ds_value) ||
                                                   mozilla::IsNaN(dt_value)));
              break;
            case ff_c_ole_fmt:
              dt_value = getFpuRegisterDouble(ft_reg);
              setFCSRBit(fcsr_cc, (ds_value <= dt_value));
              break;
            case ff_c_ule_fmt:
              dt_value = getFpuRegisterDouble(ft_reg);
              setFCSRBit(fcsr_cc,
                         (ds_value <= dt_value) || (mozilla::IsNaN(ds_value) ||
                                                    mozilla::IsNaN(dt_value)));
              break;
            case ff_cvt_w_fmt:  // Convert double to word.
              // Rounding modes are not yet supported.
              MOZ_ASSERT((FCSR_ & 3) == 0);
              // In rounding mode 0 it should behave like ROUND.
              MOZ_FALLTHROUGH;
            case ff_round_w_fmt: {  // Round double to word (round half to
                                    // even).
              double rounded = std::floor(ds_value + 0.5);
              int32_t result = static_cast<int32_t>(rounded);
              if ((result & 1) != 0 && result - ds_value == 0.5) {
                // If the number is halfway between two integers,
                // round to the even one.
                result--;
              }
              setFpuRegister(fd_reg, result);
              if (setFCSRRoundError(ds_value, rounded)) {
                setFpuRegister(fd_reg, kFPUInvalidResult);
              }
              break;
            }
            case ff_trunc_w_fmt: {  // Truncate double to word (round towards
                                    // 0).
              double rounded = trunc(ds_value);
              int32_t result = static_cast<int32_t>(rounded);
              setFpuRegister(fd_reg, result);
              if (setFCSRRoundError(ds_value, rounded)) {
                setFpuRegister(fd_reg, kFPUInvalidResult);
              }
              break;
            }
            case ff_floor_w_fmt: {  // Round double to word towards negative
                                    // infinity.
              double rounded = std::floor(ds_value);
              int32_t result = static_cast<int32_t>(rounded);
              setFpuRegister(fd_reg, result);
              if (setFCSRRoundError(ds_value, rounded)) {
                setFpuRegister(fd_reg, kFPUInvalidResult);
              }
              break;
            }
            case ff_ceil_w_fmt: {  // Round double to word towards positive
                                   // infinity.
              double rounded = std::ceil(ds_value);
              int32_t result = static_cast<int32_t>(rounded);
              setFpuRegister(fd_reg, result);
              if (setFCSRRoundError(ds_value, rounded)) {
                setFpuRegister(fd_reg, kFPUInvalidResult);
              }
              break;
            }
            case ff_cvt_s_fmt:  // Convert double to float (single).
              setFpuRegisterFloat(fd_reg, static_cast<float>(ds_value));
              break;
            case ff_cvt_l_fmt:
            case ff_trunc_l_fmt:
            case ff_round_l_fmt:
            case ff_floor_l_fmt:
            case ff_ceil_l_fmt:
            case ff_c_f_fmt:
              MOZ_CRASH();
              break;
            case ff_movf_fmt:
              // location of cc field in MOVF is equal to float branch
              // instructions
              cc = instr->fbccValue();
              fcsr_cc = GetFCSRConditionBit(cc);
              if (testFCSRBit(fcsr_cc)) {
                setFpuRegisterDouble(fd_reg, getFpuRegisterDouble(fs_reg));
              }
              break;
            case ff_movz_fmt:
              if (rt == 0) {
                setFpuRegisterDouble(fd_reg, getFpuRegisterDouble(fs_reg));
              }
              break;
            case ff_movn_fmt:
              if (rt != 0) {
                setFpuRegisterDouble(fd_reg, getFpuRegisterDouble(fs_reg));
              }
              break;
            default:
              MOZ_CRASH();
          }
          break;
        case rs_w:
          switch (instr->functionFieldRaw()) {
            case ff_cvt_s_fmt:  // Convert word to float (single).
              alu_out = getFpuRegister(fs_reg);
              setFpuRegisterFloat(fd_reg, static_cast<float>(alu_out));
              break;
            case ff_cvt_d_fmt:  // Convert word to double.
              alu_out = getFpuRegister(fs_reg);
              setFpuRegisterDouble(fd_reg, static_cast<double>(alu_out));
              break;
            default:
              MOZ_CRASH();
          }
          break;
        case rs_l:
          switch (instr->functionFieldRaw()) {
            case ff_cvt_d_fmt:
            case ff_cvt_s_fmt:
              MOZ_CRASH();
              break;
            default:
              MOZ_CRASH();
          }
          break;
        case rs_ps:
          break;
        default:
          MOZ_CRASH();
      }
      break;
    case op_cop1x:
      switch (instr->functionFieldRaw()) {
        case ff_madd_s:
          float fr, ft, fs;
          fr = getFpuRegisterFloat(fr_reg);
          fs = getFpuRegisterFloat(fs_reg);
          ft = getFpuRegisterFloat(ft_reg);
          setFpuRegisterFloat(fd_reg, fs * ft + fr);
          break;
        case ff_madd_d:
          double dr, dt, ds;
          dr = getFpuRegisterDouble(fr_reg);
          ds = getFpuRegisterDouble(fs_reg);
          dt = getFpuRegisterDouble(ft_reg);
          setFpuRegisterDouble(fd_reg, ds * dt + dr);
          break;
        default:
          MOZ_CRASH();
      }
      break;
    case op_special:
      switch (instr->functionFieldRaw()) {
        case ff_jr: {
          SimInstruction* branch_delay_instr =
              reinterpret_cast<SimInstruction*>(current_pc +
                                                SimInstruction::kInstrSize);
          branchDelayInstructionDecode(branch_delay_instr);
          set_pc(next_pc);
          pc_modified_ = true;
          break;
        }
        case ff_jalr: {
          SimInstruction* branch_delay_instr =
              reinterpret_cast<SimInstruction*>(current_pc +
                                                SimInstruction::kInstrSize);
          setRegister(return_addr_reg,
                      current_pc + 2 * SimInstruction::kInstrSize);
          branchDelayInstructionDecode(branch_delay_instr);
          set_pc(next_pc);
          pc_modified_ = true;
          break;
        }
        // Instructions using HI and LO registers.
        case ff_mult:
          setRegister(LO, static_cast<int32_t>(i64hilo & 0xffffffff));
          setRegister(HI, static_cast<int32_t>(i64hilo >> 32));
          break;
        case ff_multu:
          setRegister(LO, static_cast<int32_t>(u64hilo & 0xffffffff));
          setRegister(HI, static_cast<int32_t>(u64hilo >> 32));
          break;
        case ff_div:
          // Divide by zero and overflow was not checked in the configuration
          // step - div and divu do not raise exceptions. On division by 0
          // the result will be UNPREDICTABLE. On overflow (INT_MIN/-1),
          // return INT_MIN which is what the hardware does.
          if (rs == INT_MIN && rt == -1) {
            setRegister(LO, INT_MIN);
            setRegister(HI, 0);
          } else if (rt != 0) {
            setRegister(LO, rs / rt);
            setRegister(HI, rs % rt);
          }
          break;
        case ff_divu:
          if (rt_u != 0) {
            setRegister(LO, rs_u / rt_u);
            setRegister(HI, rs_u % rt_u);
          }
          break;
          // Break and trap instructions.
        case ff_break:
        case ff_tge:
        case ff_tgeu:
        case ff_tlt:
        case ff_tltu:
        case ff_teq:
        case ff_tne:
          if (do_interrupt) {
            softwareInterrupt(instr);
          }
          break;
        case ff_sync:
          switch (instr->bits(10, 6)) {
            case 0x0:
            case 0x4:
            case 0x10:
            case 0x11:
            case 0x12:
            case 0x13:
              AtomicOperations::fenceSeqCst();
              break;
            default:
              MOZ_CRASH();
          }
          break;
          // Conditional moves.
        case ff_movn:
          if (rt) setRegister(rd_reg, rs);
          break;
        case ff_movci: {
          uint32_t cc = instr->fbccValue();
          uint32_t fcsr_cc = GetFCSRConditionBit(cc);
          if (instr->bit(16)) {  // Read Tf bit.
            if (testFCSRBit(fcsr_cc)) setRegister(rd_reg, rs);
          } else {
            if (!testFCSRBit(fcsr_cc)) setRegister(rd_reg, rs);
          }
          break;
        }
        case ff_movz:
          if (!rt) setRegister(rd_reg, rs);
          break;
        default:  // For other special opcodes we do the default operation.
          setRegister(rd_reg, alu_out);
      }
      break;
    case op_special2:
      switch (instr->functionFieldRaw()) {
        case ff_mul:
          setRegister(rd_reg, alu_out);
          // HI and LO are UNPREDICTABLE after the operation.
          setRegister(LO, Unpredictable);
          setRegister(HI, Unpredictable);
          break;
        case ff_madd:
          setRegister(
              LO, getRegister(LO) + static_cast<int32_t>(i64hilo & 0xffffffff));
          setRegister(HI,
                      getRegister(HI) + static_cast<int32_t>(i64hilo >> 32));
          break;
        case ff_maddu:
          setRegister(
              LO, getRegister(LO) + static_cast<int32_t>(u64hilo & 0xffffffff));
          setRegister(HI,
                      getRegister(HI) + static_cast<int32_t>(u64hilo >> 32));
          break;
        default:  // For other special2 opcodes we do the default operation.
          setRegister(rd_reg, alu_out);
      }
      break;
    case op_special3:
      switch (instr->functionFieldRaw()) {
        case ff_ins:
          // Ins instr leaves result in Rt, rather than Rd.
          setRegister(rt_reg, alu_out);
          break;
        case ff_ext:
          // Ext instr leaves result in Rt, rather than Rd.
          setRegister(rt_reg, alu_out);
          break;
        case ff_bshfl:
          setRegister(rd_reg, alu_out);
          break;
        default:
          MOZ_CRASH();
      }
      break;
      // Unimplemented opcodes raised an error in the configuration step before,
      // so we can use the default here to set the destination register in
      // common cases.
    default:
      setRegister(rd_reg, alu_out);
  }
}

// Type 2: instructions using a 16 bytes immediate. (e.g. addi, beq).
void Simulator::decodeTypeImmediate(SimInstruction* instr) {
  // Instruction fields.
  Opcode op = instr->opcodeFieldRaw();
  int32_t rs = getRegister(instr->rsValue());
  uint32_t rs_u = static_cast<uint32_t>(rs);
  int32_t rt_reg = instr->rtValue();  // Destination register.
  int32_t rt = getRegister(rt_reg);
  int16_t imm16 = instr->imm16Value();

  int32_t ft_reg = instr->ftValue();  // Destination register.

  // Zero extended immediate.
  uint32_t oe_imm16 = 0xffff & imm16;
  // Sign extended immediate.
  int32_t se_imm16 = imm16;

  // Get current pc.
  int32_t current_pc = get_pc();
  // Next pc.
  int32_t next_pc = bad_ra;

  // Used for conditional branch instructions.
  bool do_branch = false;
  bool execute_branch_delay_instruction = false;

  // Used for arithmetic instructions.
  int32_t alu_out = 0;
  // Floating point.
  double fp_out = 0.0;
  uint32_t cc, cc_value, fcsr_cc;

  // Used for memory instructions.
  uint32_t addr = 0x0;
  // Value to be written in memory.
  uint32_t mem_value = 0x0;

  // ---------- Configuration (and execution for op_regimm).
  switch (op) {
      // ------------- op_cop1. Coprocessor instructions.
    case op_cop1:
      switch (instr->rsFieldRaw()) {
        case rs_bc1:  // Branch on coprocessor condition.
          cc = instr->fbccValue();
          fcsr_cc = GetFCSRConditionBit(cc);
          cc_value = testFCSRBit(fcsr_cc);
          do_branch = (instr->fbtrueValue()) ? cc_value : !cc_value;
          execute_branch_delay_instruction = true;
          // Set next_pc.
          if (do_branch) {
            next_pc = current_pc + (imm16 << 2) + SimInstruction::kInstrSize;
          } else {
            next_pc = current_pc + kBranchReturnOffset;
          }
          break;
        default:
          MOZ_CRASH();
      }
      break;
      // ------------- op_regimm class.
    case op_regimm:
      switch (instr->rtFieldRaw()) {
        case rt_bltz:
          do_branch = (rs < 0);
          break;
        case rt_bltzal:
          do_branch = rs < 0;
          break;
        case rt_bgez:
          do_branch = rs >= 0;
          break;
        case rt_bgezal:
          do_branch = rs >= 0;
          break;
        default:
          MOZ_CRASH();
      }
      switch (instr->rtFieldRaw()) {
        case rt_bltz:
        case rt_bltzal:
        case rt_bgez:
        case rt_bgezal:
          // Branch instructions common part.
          execute_branch_delay_instruction = true;
          // Set next_pc.
          if (do_branch) {
            next_pc = current_pc + (imm16 << 2) + SimInstruction::kInstrSize;
            if (instr->isLinkingInstruction()) {
              setRegister(31, current_pc + kBranchReturnOffset);
            }
          } else {
            next_pc = current_pc + kBranchReturnOffset;
          }
          break;
        default:
          break;
      }
      break;  // case op_regimm.
      // ------------- Branch instructions.
      // When comparing to zero, the encoding of rt field is always 0, so we
      // don't need to replace rt with zero.
    case op_beq:
      do_branch = (rs == rt);
      break;
    case op_bne:
      do_branch = rs != rt;
      break;
    case op_blez:
      do_branch = rs <= 0;
      break;
    case op_bgtz:
      do_branch = rs > 0;
      break;
      // ------------- Arithmetic instructions.
    case op_addi:
      if (HaveSameSign(rs, se_imm16)) {
        if (rs > 0) {
          exceptions[kIntegerOverflow] = rs > (kRegisterskMaxValue - se_imm16);
        } else if (rs < 0) {
          exceptions[kIntegerUnderflow] = rs < (kRegisterskMinValue - se_imm16);
        }
      }
      alu_out = rs + se_imm16;
      break;
    case op_addiu:
      alu_out = rs + se_imm16;
      break;
    case op_slti:
      alu_out = (rs < se_imm16) ? 1 : 0;
      break;
    case op_sltiu:
      alu_out = (rs_u < static_cast<uint32_t>(se_imm16)) ? 1 : 0;
      break;
    case op_andi:
      alu_out = rs & oe_imm16;
      break;
    case op_ori:
      alu_out = rs | oe_imm16;
      break;
    case op_xori:
      alu_out = rs ^ oe_imm16;
      break;
    case op_lui:
      alu_out = (oe_imm16 << 16);
      break;
      // ------------- Memory instructions.
    case op_lb:
      addr = rs + se_imm16;
      alu_out = readB(addr);
      break;
    case op_lh:
      addr = rs + se_imm16;
      alu_out = readH(addr, instr);
      break;
    case op_lwl: {
      // al_offset is offset of the effective address within an aligned word.
      uint8_t al_offset = (rs + se_imm16) & kPointerAlignmentMask;
      uint8_t byte_shift = kPointerAlignmentMask - al_offset;
      uint32_t mask = (1 << byte_shift * 8) - 1;
      addr = rs + se_imm16 - al_offset;
      alu_out = readW(addr, instr);
      alu_out <<= byte_shift * 8;
      alu_out |= rt & mask;
      break;
    }
    case op_lw:
      addr = rs + se_imm16;
      alu_out = readW(addr, instr);
      break;
    case op_lbu:
      addr = rs + se_imm16;
      alu_out = readBU(addr);
      break;
    case op_lhu:
      addr = rs + se_imm16;
      alu_out = readHU(addr, instr);
      break;
    case op_lwr: {
      // al_offset is offset of the effective address within an aligned word.
      uint8_t al_offset = (rs + se_imm16) & kPointerAlignmentMask;
      uint8_t byte_shift = kPointerAlignmentMask - al_offset;
      uint32_t mask = al_offset ? (~0 << (byte_shift + 1) * 8) : 0;
      addr = rs + se_imm16 - al_offset;
      alu_out = readW(addr, instr);
      alu_out = static_cast<uint32_t>(alu_out) >> al_offset * 8;
      alu_out |= rt & mask;
      break;
    }
    case op_sb:
      addr = rs + se_imm16;
      break;
    case op_sh:
      addr = rs + se_imm16;
      break;
    case op_swl: {
      uint8_t al_offset = (rs + se_imm16) & kPointerAlignmentMask;
      uint8_t byte_shift = kPointerAlignmentMask - al_offset;
      uint32_t mask = byte_shift ? (~0 << (al_offset + 1) * 8) : 0;
      addr = rs + se_imm16 - al_offset;
      mem_value = readW(addr, instr) & mask;
      mem_value |= static_cast<uint32_t>(rt) >> byte_shift * 8;
      break;
    }
    case op_sw:
      addr = rs + se_imm16;
      break;
    case op_swr: {
      uint8_t al_offset = (rs + se_imm16) & kPointerAlignmentMask;
      uint32_t mask = (1 << al_offset * 8) - 1;
      addr = rs + se_imm16 - al_offset;
      mem_value = readW(addr, instr);
      mem_value = (rt << al_offset * 8) | (mem_value & mask);
      break;
    }
    case op_lwc1:
      addr = rs + se_imm16;
      alu_out = readW(addr, instr);
      break;
    case op_ldc1:
      addr = rs + se_imm16;
      fp_out = readD(addr, instr);
      break;
    case op_swc1:
    case op_sdc1:
      addr = rs + se_imm16;
      break;
    case op_ll:
      addr = rs + se_imm16;
      alu_out = loadLinkedW(addr, instr);
      break;
    case op_sc:
      addr = rs + se_imm16;
      alu_out = storeConditionalW(addr, rt, instr);
      break;
    default:
      MOZ_CRASH();
  }

  // ---------- Raise exceptions triggered.
  signalExceptions();

  // ---------- Execution.
  switch (op) {
      // ------------- Branch instructions.
    case op_beq:
    case op_bne:
    case op_blez:
    case op_bgtz:
      // Branch instructions common part.
      execute_branch_delay_instruction = true;
      // Set next_pc.
      if (do_branch) {
        next_pc = current_pc + (imm16 << 2) + SimInstruction::kInstrSize;
        if (instr->isLinkingInstruction()) {
          setRegister(31, current_pc + 2 * SimInstruction::kInstrSize);
        }
      } else {
        next_pc = current_pc + 2 * SimInstruction::kInstrSize;
      }
      break;
      // ------------- Arithmetic instructions.
    case op_addi:
    case op_addiu:
    case op_slti:
    case op_sltiu:
    case op_andi:
    case op_ori:
    case op_xori:
    case op_lui:
      setRegister(rt_reg, alu_out);
      break;
      // ------------- Memory instructions.
    case op_lb:
    case op_lh:
    case op_lwl:
    case op_lw:
    case op_lbu:
    case op_lhu:
    case op_lwr:
    case op_ll:
    case op_sc:
      setRegister(rt_reg, alu_out);
      break;
    case op_sb:
      writeB(addr, static_cast<int8_t>(rt));
      break;
    case op_sh:
      writeH(addr, static_cast<uint16_t>(rt), instr);
      break;
    case op_swl:
      writeW(addr, mem_value, instr);
      break;
    case op_sw:
      writeW(addr, rt, instr);
      break;
    case op_swr:
      writeW(addr, mem_value, instr);
      break;
    case op_lwc1:
      setFpuRegister(ft_reg, alu_out);
      break;
    case op_ldc1:
      setFpuRegisterDouble(ft_reg, fp_out);
      break;
    case op_swc1:
      addr = rs + se_imm16;
      writeW(addr, getFpuRegister(ft_reg), instr);
      break;
    case op_sdc1:
      addr = rs + se_imm16;
      writeD(addr, getFpuRegisterDouble(ft_reg), instr);
      break;
    default:
      break;
  }

  if (execute_branch_delay_instruction) {
    // Execute branch delay slot
    // We don't check for end_sim_pc. First it should not be met as the current
    // pc is valid. Secondly a jump should always execute its branch delay slot.
    SimInstruction* branch_delay_instr = reinterpret_cast<SimInstruction*>(
        current_pc + SimInstruction::kInstrSize);
    branchDelayInstructionDecode(branch_delay_instr);
  }

  // If needed update pc after the branch delay execution.
  if (next_pc != bad_ra) set_pc(next_pc);
}

// Type 3: instructions using a 26 bytes immediate. (e.g. j, jal).
void Simulator::decodeTypeJump(SimInstruction* instr) {
  // Get current pc.
  int32_t current_pc = get_pc();
  // Get unchanged bits of pc.
  int32_t pc_high_bits = current_pc & 0xf0000000;
  // Next pc.
  int32_t next_pc = pc_high_bits | (instr->imm26Value() << 2);

  // Execute branch delay slot.
  // We don't check for end_sim_pc. First it should not be met as the current pc
  // is valid. Secondly a jump should always execute its branch delay slot.
  SimInstruction* branch_delay_instr = reinterpret_cast<SimInstruction*>(
      current_pc + SimInstruction::kInstrSize);
  branchDelayInstructionDecode(branch_delay_instr);

  // Update pc and ra if necessary.
  // Do this after the branch delay execution.
  if (instr->isLinkingInstruction())
    setRegister(31, current_pc + 2 * SimInstruction::kInstrSize);
  set_pc(next_pc);
  pc_modified_ = true;
}

// Executes the current instruction.
void Simulator::instructionDecode(SimInstruction* instr) {
  if (!SimulatorProcess::ICacheCheckingDisableCount) {
    AutoLockSimulatorCache als;
    SimulatorProcess::checkICacheLocked(instr);
  }
  pc_modified_ = false;

  switch (instr->instructionType()) {
    case SimInstruction::kRegisterType:
      decodeTypeRegister(instr);
      break;
    case SimInstruction::kImmediateType:
      decodeTypeImmediate(instr);
      break;
    case SimInstruction::kJumpType:
      decodeTypeJump(instr);
      break;
    default:
      UNSUPPORTED();
  }
  if (!pc_modified_)
    setRegister(pc,
                reinterpret_cast<int32_t>(instr) + SimInstruction::kInstrSize);
}

void Simulator::branchDelayInstructionDecode(SimInstruction* instr) {
  if (instr->instructionBits() == NopInst) {
    // Short-cut generic nop instructions. They are always valid and they
    // never change the simulator state.
    return;
  }

  if (instr->isForbiddenInBranchDelay()) {
    MOZ_CRASH("Eror:Unexpected opcode in a branch delay slot.");
  }
  instructionDecode(instr);
}

static void FakeInterruptHandler() {
  JSContext* cx = TlsContext.get();
  uint8_t* pc = cx->simulator()->get_pc_as<uint8_t*>();

  const wasm::ModuleSegment* ms = nullptr;
  if (!wasm::InInterruptibleCode(cx, pc, &ms)) return;

  cx->simulator()->trigger_wasm_interrupt();
}

template <bool enableStopSimAt>
void Simulator::execute() {
  // 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.
  int program_counter = get_pc();

  while (program_counter != end_sim_pc) {
    if (enableStopSimAt && (icount_ == Simulator::StopSimAt)) {
      MipsDebugger dbg(this);
      dbg.debug();
    } else {
      if (MOZ_UNLIKELY(JitOptions.simulatorAlwaysInterrupt))
        FakeInterruptHandler();
      SimInstruction* instr =
          reinterpret_cast<SimInstruction*>(program_counter);
      instructionDecode(instr);
      icount_++;

      if (MOZ_UNLIKELY(wasm_interrupt_)) {
        handleWasmInterrupt();
        wasm_interrupt_ = false;
      }
    }
    program_counter = get_pc();
  }
}

void Simulator::callInternal(uint8_t* entry) {
  // Prepare to execute the code at entry.
  setRegister(pc, reinterpret_cast<int32_t>(entry));
  // Put down marker for end of simulation. The simulator will stop simulation
  // when the PC reaches this value. By saving the "end simulation" value into
  // the LR the simulation stops when returning to this call point.
  setRegister(ra, end_sim_pc);

  // Remember the values of callee-saved registers.
  // The code below assumes that r9 is not used as sb (static base) in
  // simulator code and therefore is regarded as a callee-saved register.
  int32_t s0_val = getRegister(s0);
  int32_t s1_val = getRegister(s1);
  int32_t s2_val = getRegister(s2);
  int32_t s3_val = getRegister(s3);
  int32_t s4_val = getRegister(s4);
  int32_t s5_val = getRegister(s5);
  int32_t s6_val = getRegister(s6);
  int32_t s7_val = getRegister(s7);
  int32_t gp_val = getRegister(gp);
  int32_t sp_val = getRegister(sp);
  int32_t fp_val = getRegister(fp);

  // Set up the callee-saved registers with a known value. To be able to check
  // that they are preserved properly across JS execution.
  int32_t callee_saved_value = icount_;
  setRegister(s0, callee_saved_value);
  setRegister(s1, callee_saved_value);
  setRegister(s2, callee_saved_value);
  setRegister(s3, callee_saved_value);
  setRegister(s4, callee_saved_value);
  setRegister(s5, callee_saved_value);
  setRegister(s6, callee_saved_value);
  setRegister(s7, callee_saved_value);
  setRegister(gp, callee_saved_value);
  setRegister(fp, callee_saved_value);

  // Start the simulation.
  if (Simulator::StopSimAt != -1)
    execute<true>();
  else
    execute<false>();

  // Check that the callee-saved registers have been preserved.
  MOZ_ASSERT(callee_saved_value == getRegister(s0));
  MOZ_ASSERT(callee_saved_value == getRegister(s1));
  MOZ_ASSERT(callee_saved_value == getRegister(s2));
  MOZ_ASSERT(callee_saved_value == getRegister(s3));
  MOZ_ASSERT(callee_saved_value == getRegister(s4));
  MOZ_ASSERT(callee_saved_value == getRegister(s5));
  MOZ_ASSERT(callee_saved_value == getRegister(s6));
  MOZ_ASSERT(callee_saved_value == getRegister(s7));
  MOZ_ASSERT(callee_saved_value == getRegister(gp));
  MOZ_ASSERT(callee_saved_value == getRegister(fp));

  // Restore callee-saved registers with the original value.
  setRegister(s0, s0_val);
  setRegister(s1, s1_val);
  setRegister(s2, s2_val);
  setRegister(s3, s3_val);
  setRegister(s4, s4_val);
  setRegister(s5, s5_val);
  setRegister(s6, s6_val);
  setRegister(s7, s7_val);
  setRegister(gp, gp_val);
  setRegister(sp, sp_val);
  setRegister(fp, fp_val);
}

int32_t Simulator::call(uint8_t* entry, int argument_count, ...) {
  va_list parameters;
  va_start(parameters, argument_count);

  int original_stack = getRegister(sp);
  // Compute position of stack on entry to generated code.
  int entry_stack = original_stack;
  if (argument_count > kCArgSlotCount)
    entry_stack = entry_stack - argument_count * sizeof(int32_t);
  else
    entry_stack = entry_stack - kCArgsSlotsSize;

  entry_stack &= ~(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, int32_t));
    else
      stack_argument[i] = va_arg(parameters, int32_t);
  }

  va_end(parameters);
  setRegister(sp, entry_stack);

  callInternal(entry);

  // Pop stack passed arguments.
  MOZ_ASSERT(entry_stack == getRegister(sp));
  setRegister(sp, original_stack);

  int32_t result = getRegister(v0);
  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_; }

uintptr_t* JSContext::addressOfSimulatorStackLimit() {
  return simulator_->addressOfStackLimit();
}
