#include "jit/mips64/Simulator-mips64.h"
#include "mozilla/Casting.h"
#include "mozilla/FloatingPoint.h"
#include "mozilla/IntegerPrintfMacros.h"
#include "mozilla/Likely.h"
#include "mozilla/MathAlgorithms.h"
#include <float.h>
#include <limits>
#include "jit/AtomicOperations.h"
#include "jit/mips64/Assembler-mips64.h"
#include "js/UniquePtr.h"
#include "js/Utility.h"
#include "threading/LockGuard.h"
#include "vm/Runtime.h"
#include "wasm/WasmInstance.h"
#include "wasm/WasmSignalHandlers.h"
#define I8(v) static_cast<int8_t>(v)
#define I16(v) static_cast<int16_t>(v)
#define U16(v) static_cast<uint16_t>(v)
#define I32(v) static_cast<int32_t>(v)
#define U32(v) static_cast<uint32_t>(v)
#define I64(v) static_cast<int64_t>(v)
#define U64(v) static_cast<uint64_t>(v)
#define I128(v) static_cast<__int128_t>(v)
#define U128(v) static_cast<__uint128_t>(v)
#define I32_CHECK(v) \
({ \
MOZ_ASSERT(I64(I32(v)) == I64(v)); \
I32((v)); \
})
namespace js {
namespace jit {
static const Instr kCallRedirInstr =
op_special | MAX_BREAK_CODE << FunctionBits | ff_break;
static uint32_t GetFCSRConditionBit(uint32_t cc) {
if (cc == 0) {
return 23;
}
return 24 + cc;
}
class SimInstruction {
public:
enum {
kInstrSize = 4,
kPCReadOffset = 0
};
inline Instr instructionBits() const {
return *reinterpret_cast<const Instr*>(this);
}
inline void setInstructionBits(Instr value) {
*reinterpret_cast<Instr*>(this) = value;
}
inline int bit(int nr) const { return (instructionBits() >> nr) & 1; }
inline int bits(int hi, int lo) const {
return (instructionBits() >> lo) & ((2 << (hi - lo)) - 1);
}
enum Type { kRegisterType, kImmediateType, kJumpType, kUnsupported = -1 };
Type instructionType() const;
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); }
inline int fcccValue() const {
return bits(FCccShift + FCccBits - 1, FCccShift);
}
inline int fbccValue() const {
return bits(FBccShift + FBccBits - 1, FBccShift);
}
inline int fbtrueValue() const {
return bits(FBtrueShift + FBtrueBits - 1, FBtrueShift);
}
inline Opcode opcodeFieldRaw() const {
return static_cast<Opcode>(instructionBits() & OpcodeMask);
}
inline int rsFieldRaw() const {
MOZ_ASSERT(instructionType() == kRegisterType ||
instructionType() == kImmediateType);
return instructionBits() & RSMask;
}
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;
}
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);
}
bool isForbiddenInBranchDelay() const;
bool isLinkingInstruction() const;
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_sync:
case ff_break:
case ff_sll:
case ff_dsll:
case ff_dsll32:
case ff_srl:
case ff_dsrl:
case ff_dsrl32:
case ff_sra:
case ff_dsra:
case ff_dsra32:
case ff_sllv:
case ff_dsllv:
case ff_srlv:
case ff_dsrlv:
case ff_srav:
case ff_dsrav:
case ff_mfhi:
case ff_mflo:
case ff_mult:
case ff_dmult:
case ff_multu:
case ff_dmultu:
case ff_div:
case ff_ddiv:
case ff_divu:
case ff_ddivu:
case ff_add:
case ff_dadd:
case ff_addu:
case ff_daddu:
case ff_sub:
case ff_dsub:
case ff_subu:
case ff_dsubu:
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:
return kRegisterType;
default:
return kUnsupported;
};
break;
case op_special2:
switch (functionFieldRaw()) {
case ff_mul:
case ff_clz:
case ff_dclz:
return kRegisterType;
default:
return kUnsupported;
};
break;
case op_special3:
switch (functionFieldRaw()) {
case ff_ins:
case ff_dins:
case ff_dinsm:
case ff_dinsu:
case ff_ext:
case ff_dext:
case ff_dextm:
case ff_dextu:
case ff_bshfl:
return kRegisterType;
default:
return kUnsupported;
};
break;
case op_cop1: switch (rsFieldRawNoAssert()) {
case rs_bc1: return kImmediateType;
default:
return kRegisterType;
};
break;
case op_cop1x:
return kRegisterType;
case op_regimm:
case op_beq:
case op_bne:
case op_blez:
case op_bgtz:
case op_addi:
case op_daddi:
case op_addiu:
case op_daddiu:
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_lbu:
case op_lh:
case op_lhu:
case op_lw:
case op_lwu:
case op_lwl:
case op_lwr:
case op_ll:
case op_lld:
case op_ld:
case op_ldl:
case op_ldr:
case op_sb:
case op_sh:
case op_sw:
case op_swl:
case op_swr:
case op_sc:
case op_scd:
case op_sd:
case op_sdl:
case op_sdr:
case op_lwc1:
case op_ldc1:
case op_swc1:
case op_sdc1:
return kImmediateType;
case op_j:
case op_jal:
return kJumpType;
default:
return kUnsupported;
};
return kUnsupported;
}
const int kCArgSlotCount = 0;
const int kCArgsSlotsSize = kCArgSlotCount * sizeof(uintptr_t);
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; 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]; static const int kValidityMapSize = kPageSize >> kLineShift;
char validity_map_[kValidityMapSize]; };
class AutoLockSimulatorCache : public LockGuard<Mutex> {
using Base = LockGuard<Mutex>;
public:
explicit AutoLockSimulatorCache()
: Base(SimulatorProcess::singleton_->cacheLock_) {}
};
mozilla::Atomic<size_t, mozilla::ReleaseAcquire>
SimulatorProcess::ICacheCheckingDisableCount(
1); SimulatorProcess* SimulatorProcess::singleton_ = nullptr;
int64_t Simulator::StopSimAt = -1;
Simulator* Simulator::Create() {
auto sim = MakeUnique<Simulator>();
if (!sim) {
return nullptr;
}
if (!sim->init()) {
return nullptr;
}
int64_t stopAt;
char* stopAtStr = getenv("MIPS_SIM_STOP_AT");
if (stopAtStr && sscanf(stopAtStr, "%" PRIi64, &stopAt) == 1) {
fprintf(stderr, "\nStopping simulation at icount %" PRIi64 "\n", stopAt);
Simulator::StopSimAt = stopAt;
}
return sim.release();
}
void Simulator::Destroy(Simulator* sim) { js_delete(sim); }
class MipsDebugger {
public:
explicit MipsDebugger(Simulator* sim) : sim_(sim) {}
void stop(SimInstruction* instr);
void debug();
void printAllRegs();
void printAllRegsIncludingFPU();
private:
static const Instr kBreakpointInstr = op_special | ff_break | 0xfffff << 6;
static const Instr kNopInstr = op_special | ff_sll;
Simulator* sim_;
int64_t getRegisterValue(int regnum);
int64_t getFPURegisterValueLong(int regnum);
float getFPURegisterValueFloat(int regnum);
double getFPURegisterValueDouble(int regnum);
bool getValue(const char* desc, int64_t* value);
bool setBreakpoint(SimInstruction* breakpc);
bool deleteBreakpoint(SimInstruction* breakpc);
void undoBreakpoints();
void redoBreakpoints();
};
static void UNSUPPORTED() {
printf("Unsupported instruction.\n");
MOZ_CRASH();
}
void MipsDebugger::stop(SimInstruction* instr) {
uint32_t code = instr->bits(25, 6);
char* msg =
*reinterpret_cast<char**>(sim_->get_pc() + SimInstruction::kInstrSize);
if (!sim_->watchedStops_[code].desc_) {
sim_->watchedStops_[code].desc_ = msg;
}
if (code != kMaxStopCode) {
printf("Simulator hit stop %u: %s\n", code, msg);
} else {
printf("Simulator hit %s\n", msg);
}
sim_->set_pc(sim_->get_pc() + 2 * SimInstruction::kInstrSize);
debug();
}
int64_t MipsDebugger::getRegisterValue(int regnum) {
if (regnum == kPCRegister) {
return sim_->get_pc();
}
return sim_->getRegister(regnum);
}
int64_t MipsDebugger::getFPURegisterValueLong(int regnum) {
return sim_->getFpuRegister(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, int64_t* value) {
Register reg = Register::FromName(desc);
if (reg != InvalidReg) {
*value = getRegisterValue(reg.code());
return true;
}
if (strncmp(desc, "0x", 2) == 0) {
return sscanf(desc, "%" PRIu64, reinterpret_cast<uint64_t*>(value)) == 1;
}
return sscanf(desc, "%" PRIi64, value) == 1;
}
bool MipsDebugger::setBreakpoint(SimInstruction* breakpc) {
if (sim_->break_pc_ != nullptr) {
return false;
}
sim_->break_pc_ = breakpc;
sim_->break_instr_ = breakpc->instructionBits();
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() {
int64_t value;
for (uint32_t i = 0; i < Registers::Total; i++) {
value = getRegisterValue(i);
printf("%3s: 0x%016" PRIx64 " %20" PRIi64 " ", Registers::GetName(i),
value, value);
if (i % 2) {
printf("\n");
}
}
printf("\n");
value = getRegisterValue(Simulator::LO);
printf(" LO: 0x%016" PRIx64 " %20" PRIi64 " ", value, value);
value = getRegisterValue(Simulator::HI);
printf(" HI: 0x%016" PRIx64 " %20" PRIi64 "\n", value, value);
value = getRegisterValue(Simulator::pc);
printf(" pc: 0x%016" PRIx64 "\n", value);
}
void MipsDebugger::printAllRegsIncludingFPU() {
printAllRegs();
printf("\n\n");
for (uint32_t i = 0; i < FloatRegisters::TotalPhys; i++) {
printf("%3s: 0x%016" PRIi64 "\tflt: %-8.4g\tdbl: %-16.4g\n",
FloatRegisters::GetName(i), getFPURegisterValueLong(i),
getFPURegisterValueFloat(i), getFPURegisterValueDouble(i));
}
}
static char* ReadLine(const char* prompt) {
UniqueChars result;
char lineBuf[256];
int offset = 0;
bool keepGoing = true;
fprintf(stdout, "%s", prompt);
fflush(stdout);
while (keepGoing) {
if (fgets(lineBuf, sizeof(lineBuf), stdin) == nullptr) {
return nullptr;
}
int len = strlen(lineBuf);
if (len > 0 && lineBuf[len - 1] == '\n') {
keepGoing = false;
}
if (!result) {
result.reset(js_pod_malloc<char>(len + 1));
if (!result) {
return nullptr;
}
} else {
int new_len = offset + len + 1;
char* new_result = js_pod_malloc<char>(new_len);
if (!new_result) {
return nullptr;
}
memcpy(new_result, result.get(), offset * sizeof(char));
result.reset(new_result);
}
memcpy(result.get() + offset, lineBuf, len * sizeof(char));
offset += len;
}
MOZ_ASSERT(result);
result[offset] = '\0';
return result.release();
}
static void DisassembleInstruction(uint64_t pc) {
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=mips64el -mcpu=mips64r2 | "
"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};
cmd[COMMAND_SIZE] = 0;
arg1[ARG_SIZE] = 0;
arg2[ARG_SIZE] = 0;
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 {
sim_->setLastDebuggerInput(line);
}
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 {
printf("/!\\ Jumping over generated breakpoint.\n");
sim_->set_pc(sim_->get_pc() + SimInstruction::kInstrSize);
}
} else if ((strcmp(cmd, "c") == 0) || (strcmp(cmd, "cont") == 0)) {
sim_->instructionDecode(
reinterpret_cast<SimInstruction*>(sim_->get_pc()));
done = true;
} else if ((strcmp(cmd, "p") == 0) || (strcmp(cmd, "print") == 0)) {
if (argc == 2) {
int64_t value;
if (strcmp(arg1, "all") == 0) {
printAllRegs();
} else if (strcmp(arg1, "allf") == 0) {
printAllRegsIncludingFPU();
} else {
Register reg = Register::FromName(arg1);
FloatRegisters::Encoding fReg = FloatRegisters::FromName(arg1);
if (reg != InvalidReg) {
value = getRegisterValue(reg.code());
printf("%s: 0x%016" PRIi64 " %20" PRIi64 " \n", arg1, value,
value);
} else if (fReg != FloatRegisters::Invalid) {
printf("%3s: 0x%016" PRIi64 "\tflt: %-8.4g\tdbl: %-16.4g\n",
FloatRegisters::GetName(fReg),
getFPURegisterValueLong(fReg),
getFPURegisterValueFloat(fReg),
getFPURegisterValueDouble(fReg));
} else {
printf("%s unrecognized\n", arg1);
}
}
} else {
printf("print <register> or print <fpu register> single\n");
}
} else if (strcmp(cmd, "stack") == 0 || strcmp(cmd, "mem") == 0) {
int64_t* cur = nullptr;
int64_t* end = nullptr;
int next_arg = 1;
if (strcmp(cmd, "stack") == 0) {
cur = reinterpret_cast<int64_t*>(sim_->getRegister(Simulator::sp));
} else { int64_t value;
if (!getValue(arg1, &value)) {
printf("%s unrecognized\n", arg1);
continue;
}
cur = reinterpret_cast<int64_t*>(value);
next_arg++;
}
int64_t words;
if (argc == next_arg) {
words = 10;
} else {
if (!getValue(argv[next_arg], &words)) {
words = 10;
}
}
end = cur + words;
while (cur < end) {
printf(" %p: 0x%016" PRIx64 " %20" PRIi64, cur, *cur, *cur);
printf("\n");
cur++;
}
} else if ((strcmp(cmd, "disasm") == 0) || (strcmp(cmd, "dpc") == 0) ||
(strcmp(cmd, "di") == 0)) {
uint8_t* cur = nullptr;
uint8_t* end = nullptr;
if (argc == 1) {
cur = reinterpret_cast<uint8_t*>(sim_->get_pc());
end = cur + (10 * SimInstruction::kInstrSize);
} else if (argc == 2) {
Register reg = Register::FromName(arg1);
if (reg != InvalidReg || strncmp(arg1, "0x", 2) == 0) {
int64_t value;
if (getValue(arg1, &value)) {
cur = reinterpret_cast<uint8_t*>(value);
end = cur + (10 * SimInstruction::kInstrSize);
}
} else {
int64_t value;
if (getValue(arg1, &value)) {
cur = reinterpret_cast<uint8_t*>(sim_->get_pc());
end = cur + (value * SimInstruction::kInstrSize);
}
}
} else {
int64_t value1;
int64_t value2;
if (getValue(arg1, &value1) && getValue(arg2, &value2)) {
cur = reinterpret_cast<uint8_t*>(value1);
end = cur + (value2 * SimInstruction::kInstrSize);
}
}
while (cur < end) {
DisassembleInstruction(uint64_t(cur));
cur += SimInstruction::kInstrSize;
}
} else if (strcmp(cmd, "gdb") == 0) {
printf("relinquishing control to gdb\n");
asm("int $3");
printf("regaining control from gdb\n");
} else if (strcmp(cmd, "break") == 0) {
if (argc == 2) {
int64_t value;
if (getValue(arg1, &value)) {
if (!setBreakpoint(reinterpret_cast<SimInstruction*>(value))) {
printf("setting breakpoint failed\n");
}
} else {
printf("%s unrecognized\n", arg1);
}
} else {
printf("break <address>\n");
}
} else if (strcmp(cmd, "del") == 0) {
if (!deleteBreakpoint(nullptr)) {
printf("deleting breakpoint failed\n");
}
} else if (strcmp(cmd, "flags") == 0) {
printf("No flags on MIPS !\n");
} else if (strcmp(cmd, "stop") == 0) {
int64_t value;
intptr_t stop_pc = sim_->get_pc() - 2 * SimInstruction::kInstrSize;
SimInstruction* stop_instr = reinterpret_cast<SimInstruction*>(stop_pc);
SimInstruction* msg_address = reinterpret_cast<SimInstruction*>(
stop_pc + SimInstruction::kInstrSize);
if ((argc == 2) && (strcmp(arg1, "unstop") == 0)) {
if (sim_->isStopInstruction(stop_instr)) {
stop_instr->setInstructionBits(kNopInstr);
msg_address->setInstructionBits(kNopInstr);
} else {
printf("Not at debugger stop.\n");
}
} else if (argc == 3) {
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) {
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) {
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);
}
}
}
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;
}
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);
}
}
void SimulatorProcess::checkICacheLocked(SimInstruction* instr) {
intptr_t address = reinterpret_cast<intptr_t>(instr);
void* page = reinterpret_cast<void*>(address & (~CachePage::kPageMask));
void* line = reinterpret_cast<void*>(address & (~CachePage::kLineMask));
int offset = (address & CachePage::kPageMask);
CachePage* cache_page = GetCachePageLocked(icache(), page);
char* cache_valid_byte = cache_page->validityByte(offset);
bool cache_hit = (*cache_valid_byte == CachePage::LINE_VALID);
char* cached_line = cache_page->cachedData(offset & ~CachePage::kLineMask);
if (cache_hit) {
int cmpret =
memcmp(reinterpret_cast<void*>(instr), cache_page->cachedData(offset),
SimInstruction::kInstrSize);
MOZ_ASSERT(cmpret == 0);
} else {
memcpy(cached_line, line, CachePage::kLineLength);
*cache_valid_byte = CachePage::LINE_VALID;
}
}
HashNumber SimulatorProcess::ICacheHasher::hash(const Lookup& l) {
return U32(reinterpret_cast<uintptr_t>(l)) >> 2;
}
bool SimulatorProcess::ICacheHasher::match(const Key& k, const Lookup& l) {
MOZ_ASSERT((reinterpret_cast<intptr_t>(k) & CachePage::kPageMask) == 0);
MOZ_ASSERT((reinterpret_cast<intptr_t>(l) & CachePage::kPageMask) == 0);
return k == l;
}
void SimulatorProcess::FlushICache(void* start_addr, size_t size) {
if (!ICacheCheckingDisableCount) {
AutoLockSimulatorCache als;
js::jit::FlushICacheLocked(icache(), start_addr, size);
}
}
Simulator::Simulator() {
stack_ = nullptr;
stackLimit_ = 0;
pc_modified_ = false;
icount_ = 0;
break_count_ = 0;
break_pc_ = nullptr;
break_instr_ = 0;
single_stepping_ = false;
single_step_callback_ = nullptr;
single_step_callback_arg_ = nullptr;
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;
registers_[pc] = bad_ra;
registers_[ra] = bad_ra;
for (int i = 0; i < kNumExceptions; i++) {
exceptions[i] = 0;
}
lastDebuggerInput_ = nullptr;
}
bool Simulator::init() {
static const size_t stackSize = 2 * 1024 * 1024;
stack_ = js_pod_malloc<char>(stackSize);
if (!stack_) {
return false;
}
stackLimit_ = reinterpret_cast<uintptr_t>(stack_) + 1024 * 1024;
registers_[sp] = reinterpret_cast<int64_t>(stack_) + stackSize - 64;
return true;
}
class Redirection {
friend class SimulatorProcess;
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 = js_pod_malloc<Redirection>(1);
if (!redir) {
oomUnsafe.crash("Simulator redirection");
}
new (redir) Redirection(nativeFunction, type);
return redir;
}
static Redirection* FromSwiInstruction(SimInstruction* swiInstruction) {
uint8_t* addrOfSwi = reinterpret_cast<uint8_t*>(swiInstruction);
uint8_t* addrOfRedirection =
addrOfSwi - offsetof(Redirection, swiInstruction_);
return reinterpret_cast<Redirection*>(addrOfRedirection);
}
private:
void* nativeFunction_;
uint32_t swiInstruction_;
ABIFunctionType type_;
Redirection* next_;
};
Simulator::~Simulator() { js_free(stack_); }
SimulatorProcess::SimulatorProcess()
: cacheLock_(mutexid::SimulatorCacheLock), redirection_(nullptr) {
if (getenv("MIPS_SIM_ICACHE_CHECKS")) {
ICacheCheckingDisableCount = 0;
}
}
SimulatorProcess::~SimulatorProcess() {
Redirection* r = redirection_;
while (r) {
Redirection* next = r->next_;
js_delete(r);
r = next;
}
}
void* Simulator::RedirectNativeFunction(void* nativeFunction,
ABIFunctionType type) {
Redirection* redirection = Redirection::Get(nativeFunction, type);
return redirection->addressOfSwiInstruction();
}
Simulator* Simulator::Current() {
JSContext* cx = TlsContext.get();
MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
return cx->simulator();
}
void Simulator::setRegister(int reg, int64_t value) {
MOZ_ASSERT((reg >= 0) && (reg < Register::kNumSimuRegisters));
if (reg == pc) {
pc_modified_ = true;
}
registers_[reg] = (reg == 0) ? 0 : value;
}
void Simulator::setFpuRegister(int fpureg, int64_t value) {
MOZ_ASSERT((fpureg >= 0) &&
(fpureg < Simulator::FPURegister::kNumFPURegisters));
FPUregisters_[fpureg] = value;
}
void Simulator::setFpuRegisterLo(int fpureg, int32_t value) {
MOZ_ASSERT((fpureg >= 0) &&
(fpureg < Simulator::FPURegister::kNumFPURegisters));
*mozilla::BitwiseCast<int32_t*>(&FPUregisters_[fpureg]) = value;
}
void Simulator::setFpuRegisterHi(int fpureg, int32_t value) {
MOZ_ASSERT((fpureg >= 0) &&
(fpureg < Simulator::FPURegister::kNumFPURegisters));
*((mozilla::BitwiseCast<int32_t*>(&FPUregisters_[fpureg])) + 1) = value;
}
void Simulator::setFpuRegisterFloat(int fpureg, float value) {
MOZ_ASSERT((fpureg >= 0) &&
(fpureg < Simulator::FPURegister::kNumFPURegisters));
*mozilla::BitwiseCast<float*>(&FPUregisters_[fpureg]) = value;
}
void Simulator::setFpuRegisterDouble(int fpureg, double value) {
MOZ_ASSERT((fpureg >= 0) &&
(fpureg < Simulator::FPURegister::kNumFPURegisters));
*mozilla::BitwiseCast<double*>(&FPUregisters_[fpureg]) = value;
}
int64_t Simulator::getRegister(int reg) const {
MOZ_ASSERT((reg >= 0) && (reg < Register::kNumSimuRegisters));
if (reg == 0) {
return 0;
}
return registers_[reg] + ((reg == pc) ? SimInstruction::kPCReadOffset : 0);
}
int64_t Simulator::getFpuRegister(int fpureg) const {
MOZ_ASSERT((fpureg >= 0) &&
(fpureg < Simulator::FPURegister::kNumFPURegisters));
return FPUregisters_[fpureg];
}
int32_t Simulator::getFpuRegisterLo(int fpureg) const {
MOZ_ASSERT((fpureg >= 0) &&
(fpureg < Simulator::FPURegister::kNumFPURegisters));
return *mozilla::BitwiseCast<int32_t*>(&FPUregisters_[fpureg]);
}
int32_t Simulator::getFpuRegisterHi(int fpureg) const {
MOZ_ASSERT((fpureg >= 0) &&
(fpureg < Simulator::FPURegister::kNumFPURegisters));
return *((mozilla::BitwiseCast<int32_t*>(&FPUregisters_[fpureg])) + 1);
}
float Simulator::getFpuRegisterFloat(int fpureg) const {
MOZ_ASSERT((fpureg >= 0) &&
(fpureg < Simulator::FPURegister::kNumFPURegisters));
return *mozilla::BitwiseCast<float*>(&FPUregisters_[fpureg]);
}
double Simulator::getFpuRegisterDouble(int fpureg) const {
MOZ_ASSERT((fpureg >= 0) &&
(fpureg < Simulator::FPURegister::kNumFPURegisters));
return *mozilla::BitwiseCast<double*>(&FPUregisters_[fpureg]);
}
void Simulator::setCallResultDouble(double result) {
setFpuRegisterDouble(f0, result);
}
void Simulator::setCallResultFloat(float result) {
setFpuRegisterFloat(f0, result);
}
void Simulator::setCallResult(int64_t res) { setRegister(v0, res); }
void Simulator::setCallResult(__int128_t res) {
setRegister(v0, I64(res));
setRegister(v1, I64(res >> 64));
}
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); }
template <typename T>
bool Simulator::setFCSRRoundError(double original, double rounded) {
bool ret = false;
setFCSRBit(kFCSRInexactCauseBit, false);
setFCSRBit(kFCSRUnderflowCauseBit, false);
setFCSRBit(kFCSROverflowCauseBit, false);
setFCSRBit(kFCSRInvalidOpCauseBit, false);
if (!std::isfinite(original) || !std::isfinite(rounded)) {
setFCSRBit(kFCSRInvalidOpFlagBit, true);
setFCSRBit(kFCSRInvalidOpCauseBit, true);
ret = true;
}
if (original != rounded) {
setFCSRBit(kFCSRInexactFlagBit, true);
setFCSRBit(kFCSRInexactCauseBit, true);
}
if (rounded < DBL_MIN && rounded > -DBL_MIN && rounded != 0) {
setFCSRBit(kFCSRUnderflowFlagBit, true);
setFCSRBit(kFCSRUnderflowCauseBit, true);
ret = true;
}
if ((long double)rounded > (long double)std::numeric_limits<T>::max() ||
(long double)rounded < (long double)std::numeric_limits<T>::min()) {
setFCSRBit(kFCSROverflowFlagBit, true);
setFCSRBit(kFCSROverflowCauseBit, true);
setFCSRBit(kFCSRInvalidOpFlagBit, true);
setFCSRBit(kFCSRInvalidOpCauseBit, true);
ret = true;
}
return ret;
}
void Simulator::set_pc(int64_t value) {
pc_modified_ = true;
registers_[pc] = value;
}
bool Simulator::has_bad_pc() const {
return ((registers_[pc] == bad_ra) || (registers_[pc] == end_sim_pc));
}
int64_t Simulator::get_pc() const { return registers_[pc]; }
JS::ProfilingFrameIterator::RegisterState Simulator::registerState() {
wasm::RegisterState state;
state.pc = (void*)get_pc();
state.fp = (void*)getRegister(fp);
state.sp = (void*)getRegister(sp);
state.lr = (void*)getRegister(ra);
return state;
}
uint8_t Simulator::readBU(uint64_t addr, SimInstruction* instr) {
if (handleWasmSegFault(addr, 1)) {
return 0xff;
}
uint8_t* ptr = reinterpret_cast<uint8_t*>(addr);
return *ptr;
}
int8_t Simulator::readB(uint64_t addr, SimInstruction* instr) {
if (handleWasmSegFault(addr, 1)) {
return -1;
}
int8_t* ptr = reinterpret_cast<int8_t*>(addr);
return *ptr;
}
void Simulator::writeB(uint64_t addr, uint8_t value, SimInstruction* instr) {
if (handleWasmSegFault(addr, 1)) {
return;
}
uint8_t* ptr = reinterpret_cast<uint8_t*>(addr);
*ptr = value;
}
void Simulator::writeB(uint64_t addr, int8_t value, SimInstruction* instr) {
if (handleWasmSegFault(addr, 1)) {
return;
}
int8_t* ptr = reinterpret_cast<int8_t*>(addr);
*ptr = value;
}
uint16_t Simulator::readHU(uint64_t addr, SimInstruction* instr) {
if (handleWasmSegFault(addr, 2)) {
return 0xffff;
}
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%016" PRIx64
", pc=0x%016" PRIxPTR "\n",
addr, reinterpret_cast<intptr_t>(instr));
MOZ_CRASH();
return 0;
}
int16_t Simulator::readH(uint64_t addr, SimInstruction* instr) {
if (handleWasmSegFault(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%016" PRIx64 ", pc=0x%016" PRIxPTR
"\n",
addr, reinterpret_cast<intptr_t>(instr));
MOZ_CRASH();
return 0;
}
void Simulator::writeH(uint64_t addr, uint16_t value, SimInstruction* instr) {
if (handleWasmSegFault(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%016" PRIx64
", pc=0x%016" PRIxPTR "\n",
addr, reinterpret_cast<intptr_t>(instr));
MOZ_CRASH();
}
void Simulator::writeH(uint64_t addr, int16_t value, SimInstruction* instr) {
if (handleWasmSegFault(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%016" PRIx64 ", pc=0x%016" PRIxPTR "\n",
addr, reinterpret_cast<intptr_t>(instr));
MOZ_CRASH();
}
uint32_t Simulator::readWU(uint64_t addr, SimInstruction* instr) {
if (handleWasmSegFault(addr, 4)) {
return -1;
}
if ((addr & 3) == 0 ||
wasm::InCompiledCode(reinterpret_cast<void*>(get_pc()))) {
uint32_t* ptr = reinterpret_cast<uint32_t*>(addr);
return *ptr;
}
printf("Unaligned read at 0x%016" PRIx64 ", pc=0x%016" PRIxPTR "\n", addr,
reinterpret_cast<intptr_t>(instr));
MOZ_CRASH();
return 0;
}
int32_t Simulator::readW(uint64_t addr, SimInstruction* instr) {
if (handleWasmSegFault(addr, 4)) {
return -1;
}
if ((addr & 3) == 0 ||
wasm::InCompiledCode(reinterpret_cast<void*>(get_pc()))) {
int32_t* ptr = reinterpret_cast<int32_t*>(addr);
return *ptr;
}
printf("Unaligned read at 0x%016" PRIx64 ", pc=0x%016" PRIxPTR "\n", addr,
reinterpret_cast<intptr_t>(instr));
MOZ_CRASH();
return 0;
}
void Simulator::writeW(uint64_t addr, uint32_t value, SimInstruction* instr) {
if (handleWasmSegFault(addr, 4)) {
return;
}
if ((addr & 3) == 0 ||
wasm::InCompiledCode(reinterpret_cast<void*>(get_pc()))) {
uint32_t* ptr = reinterpret_cast<uint32_t*>(addr);
LLBit_ = false;
*ptr = value;
return;
}
printf("Unaligned write at 0x%016" PRIx64 ", pc=0x%016" PRIxPTR "\n", addr,
reinterpret_cast<intptr_t>(instr));
MOZ_CRASH();
}
void Simulator::writeW(uint64_t addr, int32_t value, SimInstruction* instr) {
if (handleWasmSegFault(addr, 4)) {
return;
}
if ((addr & 3) == 0 ||
wasm::InCompiledCode(reinterpret_cast<void*>(get_pc()))) {
int32_t* ptr = reinterpret_cast<int32_t*>(addr);
LLBit_ = false;
*ptr = value;
return;
}
printf("Unaligned write at 0x%016" PRIx64 ", pc=0x%016" PRIxPTR "\n", addr,
reinterpret_cast<intptr_t>(instr));
MOZ_CRASH();
}
int64_t Simulator::readDW(uint64_t addr, SimInstruction* instr) {
if (handleWasmSegFault(addr, 8)) {
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%016" PRIx64 ", pc=0x%016" PRIxPTR "\n", addr,
reinterpret_cast<intptr_t>(instr));
MOZ_CRASH();
return 0;
}
void Simulator::writeDW(uint64_t addr, int64_t value, SimInstruction* instr) {
if (handleWasmSegFault(addr, 8)) {
return;
}
if ((addr & kPointerAlignmentMask) == 0 ||
wasm::InCompiledCode(reinterpret_cast<void*>(get_pc()))) {
int64_t* ptr = reinterpret_cast<int64_t*>(addr);
LLBit_ = false;
*ptr = value;
return;
}
printf("Unaligned write at 0x%016" PRIx64 ", pc=0x%016" PRIxPTR "\n", addr,
reinterpret_cast<intptr_t>(instr));
MOZ_CRASH();
}
double Simulator::readD(uint64_t addr, SimInstruction* instr) {
if (handleWasmSegFault(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%016" PRIx64 ", pc=0x%016" PRIxPTR "\n",
addr, reinterpret_cast<intptr_t>(instr));
MOZ_CRASH();
return 0;
}
void Simulator::writeD(uint64_t addr, double value, SimInstruction* instr) {
if (handleWasmSegFault(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%016" PRIx64 ", pc=0x%016" PRIxPTR "\n",
addr, reinterpret_cast<intptr_t>(instr));
MOZ_CRASH();
}
int Simulator::loadLinkedW(uint64_t addr, SimInstruction* instr) {
if ((addr & 3) == 0) {
if (handleWasmSegFault(addr, 4)) {
return -1;
}
volatile int32_t* ptr = reinterpret_cast<volatile int32_t*>(addr);
int32_t value = *ptr;
lastLLValue_ = value;
LLAddr_ = addr;
LLBit_ = true;
return value;
}
printf("Unaligned write at 0x%016" PRIx64 ", pc=0x%016" PRIxPTR "\n", addr,
reinterpret_cast<intptr_t>(instr));
MOZ_CRASH();
return 0;
}
int Simulator::storeConditionalW(uint64_t addr, int value,
SimInstruction* instr) {
if (addr != LLAddr_) {
printf("SC to bad address: 0x%016" PRIx64 ", pc=0x%016" PRIx64
", expected: 0x%016" PRIx64 "\n",
addr, reinterpret_cast<intptr_t>(instr), LLAddr_);
MOZ_CRASH();
}
if ((addr & 3) == 0) {
SharedMem<int32_t*> ptr =
SharedMem<int32_t*>::shared(reinterpret_cast<int32_t*>(addr));
if (!LLBit_) {
return 0;
}
LLBit_ = false;
LLAddr_ = 0;
int32_t expected = int32_t(lastLLValue_);
int32_t old =
AtomicOperations::compareExchangeSeqCst(ptr, expected, int32_t(value));
return (old == expected) ? 1 : 0;
}
printf("Unaligned SC at 0x%016" PRIx64 ", pc=0x%016" PRIxPTR "\n", addr,
reinterpret_cast<intptr_t>(instr));
MOZ_CRASH();
return 0;
}
int64_t Simulator::loadLinkedD(uint64_t addr, SimInstruction* instr) {
if ((addr & kPointerAlignmentMask) == 0) {
if (handleWasmSegFault(addr, 8)) {
return -1;
}
volatile int64_t* ptr = reinterpret_cast<volatile int64_t*>(addr);
int64_t value = *ptr;
lastLLValue_ = value;
LLAddr_ = addr;
LLBit_ = true;
return value;
}
printf("Unaligned write at 0x%016" PRIx64 ", pc=0x%016" PRIxPTR "\n", addr,
reinterpret_cast<intptr_t>(instr));
MOZ_CRASH();
return 0;
}
int Simulator::storeConditionalD(uint64_t addr, int64_t value,
SimInstruction* instr) {
if (addr != LLAddr_) {
printf("SC to bad address: 0x%016" PRIx64 ", pc=0x%016" PRIx64
", expected: 0x%016" PRIx64 "\n",
addr, reinterpret_cast<intptr_t>(instr), LLAddr_);
MOZ_CRASH();
}
if ((addr & kPointerAlignmentMask) == 0) {
SharedMem<int64_t*> ptr =
SharedMem<int64_t*>::shared(reinterpret_cast<int64_t*>(addr));
if (!LLBit_) {
return 0;
}
LLBit_ = false;
LLAddr_ = 0;
int64_t expected = lastLLValue_;
int64_t old =
AtomicOperations::compareExchangeSeqCst(ptr, expected, int64_t(value));
return (old == expected) ? 1 : 0;
}
printf("Unaligned SC at 0x%016" PRIx64 ", pc=0x%016" PRIxPTR "\n", addr,
reinterpret_cast<intptr_t>(instr));
MOZ_CRASH();
return 0;
}
uintptr_t Simulator::stackLimit() const { return stackLimit_; }
uintptr_t* Simulator::addressOfStackLimit() { return &stackLimit_; }
bool Simulator::overRecursed(uintptr_t newsp) const {
if (newsp == 0) {
newsp = getRegister(sp);
}
return newsp <= stackLimit();
}
bool Simulator::overRecursedWithExtra(uint32_t extra) const {
uintptr_t newsp = getRegister(sp) - extra;
return newsp <= stackLimit();
}
void Simulator::format(SimInstruction* instr, const char* format) {
printf("Simulator found unsupported instruction:\n 0x%016lx: %s\n",
reinterpret_cast<intptr_t>(instr), format);
MOZ_CRASH();
}
typedef int64_t (*Prototype_General0)();
typedef int64_t (*Prototype_General1)(int64_t arg0);
typedef int64_t (*Prototype_General2)(int64_t arg0, int64_t arg1);
typedef int64_t (*Prototype_General3)(int64_t arg0, int64_t arg1, int64_t arg2);
typedef int64_t (*Prototype_General4)(int64_t arg0, int64_t arg1, int64_t arg2,
int64_t arg3);
typedef int64_t (*Prototype_General5)(int64_t arg0, int64_t arg1, int64_t arg2,
int64_t arg3, int64_t arg4);
typedef int64_t (*Prototype_General6)(int64_t arg0, int64_t arg1, int64_t arg2,
int64_t arg3, int64_t arg4, int64_t arg5);
typedef int64_t (*Prototype_General7)(int64_t arg0, int64_t arg1, int64_t arg2,
int64_t arg3, int64_t arg4, int64_t arg5,
int64_t arg6);
typedef int64_t (*Prototype_General8)(int64_t arg0, int64_t arg1, int64_t arg2,
int64_t arg3, int64_t arg4, int64_t arg5,
int64_t arg6, int64_t arg7);
typedef int32_t (*Prototype_Int_GeneralGeneralGeneralInt64)(int64_t arg0,
int64_t arg1,
int64_t arg2,
int64_t arg3);
typedef int32_t (*Prototype_Int_GeneralGeneralInt64Int64)(int64_t arg0,
int64_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)(int64_t arg0);
typedef int64_t (*Prototype_Int_Double)(double arg0);
typedef int64_t (*Prototype_Int_DoubleIntInt)(double arg0, int64_t arg1,
int64_t arg2);
typedef int64_t (*Prototype_Int_IntDoubleIntInt)(int64_t arg0, double arg1,
int64_t arg2, int64_t arg3);
typedef float (*Prototype_Float32_Float32)(float arg0);
typedef float (*Prototype_Float32_Float32Float32)(float arg0, float arg1);
typedef float (*Prototype_Float32_IntInt)(int64_t arg0, int64_t arg1);
typedef double (*Prototype_Double_DoubleInt)(double arg0, int64_t arg1);
typedef double (*Prototype_Double_IntDouble)(int64_t arg0, double arg1);
typedef double (*Prototype_Double_DoubleDouble)(double arg0, double arg1);
typedef int64_t (*Prototype_Int_IntDouble)(int64_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);
void Simulator::softwareInterrupt(SimInstruction* instr) {
int32_t func = instr->functionFieldRaw();
uint32_t code = (func == ff_break) ? instr->bits(25, 6) : -1;
if (instr->instructionBits() == kCallRedirInstr) {
#if !defined(USES_N64_ABI)
MOZ_CRASH("Only N64 ABI supported.");
#else
Redirection* redirection = Redirection::FromSwiInstruction(instr);
int64_t arg0 = getRegister(a0);
int64_t arg1 = getRegister(a1);
int64_t arg2 = getRegister(a2);
int64_t arg3 = getRegister(a3);
int64_t arg4 = getRegister(a4);
int64_t arg5 = getRegister(a5);
int64_t saved_ra = getRegister(ra);
intptr_t external =
reinterpret_cast<intptr_t>(redirection->nativeFunction());
bool stack_aligned = (getRegister(sp) & (ABIStackAlignment - 1)) == 0;
if (!stack_aligned) {
fprintf(stderr, "Runtime call with unaligned stack!\n");
MOZ_CRASH();
}
if (single_stepping_) {
single_step_callback_(single_step_callback_arg_, this, nullptr);
}
switch (redirection->type()) {
case Args_General0: {
Prototype_General0 target =
reinterpret_cast<Prototype_General0>(external);
int64_t result = target();
setCallResult(result);
break;
}
case Args_General1: {
Prototype_General1 target =
reinterpret_cast<Prototype_General1>(external);
int64_t result = target(arg0);
setCallResult(result);
break;
}
case Args_General2: {
Prototype_General2 target =
reinterpret_cast<Prototype_General2>(external);
int64_t result = target(arg0, arg1);
setCallResult(result);
break;
}
case Args_General3: {
Prototype_General3 target =
reinterpret_cast<Prototype_General3>(external);
int64_t result = target(arg0, arg1, arg2);
if (external == intptr_t(&js::wasm::Instance::wake)) {
result = int32_t(result);
}
setCallResult(result);
break;
}
case Args_General4: {
Prototype_General4 target =
reinterpret_cast<Prototype_General4>(external);
int64_t result = target(arg0, arg1, arg2, arg3);
setCallResult(result);
break;
}
case Args_General5: {
Prototype_General5 target =
reinterpret_cast<Prototype_General5>(external);
int64_t result = target(arg0, arg1, arg2, arg3, arg4);
setCallResult(result);
break;
}
case Args_General6: {
Prototype_General6 target =
reinterpret_cast<Prototype_General6>(external);
int64_t result = target(arg0, arg1, arg2, arg3, arg4, arg5);
setCallResult(result);
break;
}
case Args_General7: {
Prototype_General7 target =
reinterpret_cast<Prototype_General7>(external);
int64_t arg6 = getRegister(a6);
int64_t result = target(arg0, arg1, arg2, arg3, arg4, arg5, arg6);
setCallResult(result);
break;
}
case Args_General8: {
Prototype_General8 target =
reinterpret_cast<Prototype_General8>(external);
int64_t arg6 = getRegister(a6);
int64_t arg7 = getRegister(a7);
int64_t result = target(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
setCallResult(result);
break;
}
case Args_Double_None: {
Prototype_Double_None target =
reinterpret_cast<Prototype_Double_None>(external);
double dresult = target();
setCallResultDouble(dresult);
break;
}
case Args_Int_Double: {
double dval0 = getFpuRegisterDouble(12);
Prototype_Int_Double target =
reinterpret_cast<Prototype_Int_Double>(external);
int64_t result = target(dval0);
if (external == intptr_t((int32_t(*)(double))JS::ToInt32)) {
result = int32_t(result);
}
setRegister(v0, result);
break;
}
case Args_Int_GeneralGeneralGeneralInt64: {
Prototype_Int_GeneralGeneralGeneralInt64 target =
reinterpret_cast<Prototype_Int_GeneralGeneralGeneralInt64>(
external);
int64_t result = target(arg0, arg1, arg2, arg3);
if (external == intptr_t(&js::wasm::Instance::wait_i32)) {
result = int32_t(result);
}
setRegister(v0, result);
break;
}
case Args_Int_GeneralGeneralInt64Int64: {
Prototype_Int_GeneralGeneralInt64Int64 target =
reinterpret_cast<Prototype_Int_GeneralGeneralInt64Int64>(external);
int64_t result = target(arg0, arg1, arg2, arg3);
if (external == intptr_t(&js::wasm::Instance::wait_i64)) {
result = int32_t(result);
}
setRegister(v0, result);
break;
}
case Args_Int_DoubleIntInt: {
double dval = getFpuRegisterDouble(12);
Prototype_Int_DoubleIntInt target =
reinterpret_cast<Prototype_Int_DoubleIntInt>(external);
int64_t result = target(dval, arg1, arg2);
setRegister(v0, result);
break;
}
case Args_Int_IntDoubleIntInt: {
double dval = getFpuRegisterDouble(13);
Prototype_Int_IntDoubleIntInt target =
reinterpret_cast<Prototype_Int_IntDoubleIntInt>(external);
int64_t result = target(arg0, dval, arg2, arg3);
setRegister(v0, result);
break;
}
case Args_Double_Double: {
double dval0 = getFpuRegisterDouble(12);
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(13);
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_DoubleInt: {
double dval0 = getFpuRegisterDouble(12);
Prototype_Double_DoubleInt target =
reinterpret_cast<Prototype_Double_DoubleInt>(external);
double dresult = target(dval0, arg1);
setCallResultDouble(dresult);
break;
}
case Args_Double_DoubleDouble: {
double dval0 = getFpuRegisterDouble(12);
double dval1 = getFpuRegisterDouble(13);
Prototype_Double_DoubleDouble target =
reinterpret_cast<Prototype_Double_DoubleDouble>(external);
double dresult = target(dval0, dval1);
setCallResultDouble(dresult);
break;
}
case Args_Double_IntDouble: {
double dval1 = getFpuRegisterDouble(13);
Prototype_Double_IntDouble target =
reinterpret_cast<Prototype_Double_IntDouble>(external);
double dresult = target(arg0, dval1);
setCallResultDouble(dresult);
break;
}
case Args_Int_IntDouble: {
double dval1 = getFpuRegisterDouble(13);
Prototype_Int_IntDouble target =
reinterpret_cast<Prototype_Int_IntDouble>(external);
int64_t result = target(arg0, dval1);
setRegister(v0, result);
break;
}
case Args_Double_DoubleDoubleDouble: {
double dval0 = getFpuRegisterDouble(12);
double dval1 = getFpuRegisterDouble(13);
double dval2 = getFpuRegisterDouble(14);
Prototype_Double_DoubleDoubleDouble target =
reinterpret_cast<Prototype_Double_DoubleDoubleDouble>(external);
double dresult = target(dval0, dval1, dval2);
setCallResultDouble(dresult);
break;
}
case Args_Double_DoubleDoubleDoubleDouble: {
double dval0 = getFpuRegisterDouble(12);
double dval1 = getFpuRegisterDouble(13);
double dval2 = getFpuRegisterDouble(14);
double dval3 = getFpuRegisterDouble(15);
Prototype_Double_DoubleDoubleDoubleDouble target =
reinterpret_cast<Prototype_Double_DoubleDoubleDoubleDouble>(
external);
double dresult = target(dval0, dval1, dval2, dval3);
setCallResultDouble(dresult);
break;
}
default:
MOZ_CRASH("call");
}
if (single_stepping_) {
single_step_callback_(single_step_callback_arg_, this, nullptr);
}
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) {
uint8_t* newPC;
if (wasm::HandleIllegalInstruction(registerState(), &newPC)) {
set_pc(int64_t(newPC));
return;
}
}
};
MipsDebugger dbg(this);
dbg.debug();
}
}
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: %20" PRIi64 " (instr count: %20" PRIi64
") ----\n",
code, break_count_, icount_);
dbg.printAllRegs(); }
void Simulator::handleStop(uint32_t code, SimInstruction* instr) {
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 = U32(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_++;
}
}
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;
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.");
}
}
}
void Simulator::configureTypeRegister(SimInstruction* instr, int64_t& alu_out,
__int128& i128hilo,
unsigned __int128& u128hilo,
int64_t& next_pc,
int32_t& return_addr_reg,
bool& do_interrupt) {
const Opcode op = instr->opcodeFieldRaw();
const int32_t rs_reg = instr->rsValue();
const int64_t rs = getRegister(rs_reg);
const int32_t rt_reg = instr->rtValue();
const int64_t rt = getRegister(rt_reg);
const int32_t rd_reg = instr->rdValue();
const uint32_t sa = instr->saValue();
const int32_t fs_reg = instr->fsValue();
__int128 temp;
switch (op) {
case op_cop1: switch (instr->rsFieldRaw()) {
case rs_bc1: MOZ_CRASH();
break;
case rs_cfc1:
MOZ_ASSERT(fs_reg == kFCSRRegister);
alu_out = FCSR_;
break;
case rs_mfc1:
alu_out = getFpuRegisterLo(fs_reg);
break;
case rs_dmfc1:
alu_out = getFpuRegister(fs_reg);
break;
case rs_mfhc1:
alu_out = getFpuRegisterHi(fs_reg);
break;
case rs_ctc1:
case rs_mtc1:
case rs_dmtc1:
case rs_mthc1:
break;
case rs_s:
case rs_d:
case rs_w:
case rs_l:
case rs_ps:
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 = I64(I32(rt) << sa);
break;
case ff_dsll:
alu_out = rt << sa;
break;
case ff_dsll32:
alu_out = rt << (sa + 32);
break;
case ff_srl:
if (rs_reg == 0) {
alu_out = I64(I32(U32(I32_CHECK(rt)) >> sa));
} else {
alu_out = I64(I32((U32(I32_CHECK(rt)) >> sa) |
(U32(I32_CHECK(rt)) << (32 - sa))));
}
break;
case ff_dsrl:
if (rs_reg == 0) {
alu_out = U64(rt) >> sa;
} else {
alu_out = (U64(rt) >> sa) | (U64(rt) << (64 - sa));
}
break;
case ff_dsrl32:
if (rs_reg == 0) {
alu_out = U64(rt) >> (sa + 32);
} else {
alu_out = (U64(rt) >> (sa + 32)) | (U64(rt) << (64 - (sa + 32)));
}
break;
case ff_sra:
alu_out = I64(I32_CHECK(rt)) >> sa;
break;
case ff_dsra:
alu_out = rt >> sa;
break;
case ff_dsra32:
alu_out = rt >> (sa + 32);
break;
case ff_sllv:
alu_out = I64(I32(rt) << rs);
break;
case ff_dsllv:
alu_out = rt << rs;
break;
case ff_srlv:
if (sa == 0) {
alu_out = I64(I32(U32(I32_CHECK(rt)) >> rs));
} else {
alu_out = I64(I32((U32(I32_CHECK(rt)) >> rs) |
(U32(I32_CHECK(rt)) << (32 - rs))));
}
break;
case ff_dsrlv:
if (sa == 0) {
alu_out = U64(rt) >> rs;
} else {
alu_out = (U64(rt) >> rs) | (U64(rt) << (64 - rs));
}
break;
case ff_srav:
alu_out = I64(I32_CHECK(rt) >> rs);
break;
case ff_dsrav:
alu_out = rt >> rs;
break;
case ff_mfhi:
alu_out = getRegister(HI);
break;
case ff_mflo:
alu_out = getRegister(LO);
break;
case ff_mult:
i128hilo = I64(U32(I32_CHECK(rs))) * I64(U32(I32_CHECK(rt)));
break;
case ff_dmult:
i128hilo = I128(rs) * I128(rt);
break;
case ff_multu:
u128hilo = U64(U32(I32_CHECK(rs))) * U64(U32(I32_CHECK(rt)));
break;
case ff_dmultu:
u128hilo = U128(rs) * U128(rt);
break;
case ff_add:
alu_out = I32_CHECK(rs) + I32_CHECK(rt);
if ((alu_out << 32) != (alu_out << 31)) {
exceptions[kIntegerOverflow] = 1;
}
alu_out = I32(alu_out);
break;
case ff_dadd:
temp = I128(rs) + I128(rt);
if ((temp << 64) != (temp << 63)) {
exceptions[kIntegerOverflow] = 1;
}
alu_out = I64(temp);
break;
case ff_addu:
alu_out = I32(I32_CHECK(rs) + I32_CHECK(rt));
break;
case ff_daddu:
alu_out = rs + rt;
break;
case ff_sub:
alu_out = I32_CHECK(rs) - I32_CHECK(rt);
if ((alu_out << 32) != (alu_out << 31)) {
exceptions[kIntegerUnderflow] = 1;
}
alu_out = I32(alu_out);
break;
case ff_dsub:
temp = I128(rs) - I128(rt);
if ((temp << 64) != (temp << 63)) {
exceptions[kIntegerUnderflow] = 1;
}
alu_out = I64(temp);
break;
case ff_subu:
alu_out = I32(I32_CHECK(rs) - I32_CHECK(rt));
break;
case ff_dsubu:
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 = I64(rs) < I64(rt) ? 1 : 0;
break;
case ff_sltu:
alu_out = U64(rs) < U64(rt) ? 1 : 0;
break;
case ff_sync:
break;
case ff_break:
do_interrupt = true;
break;
case ff_tge:
do_interrupt = rs >= rt;
break;
case ff_tgeu:
do_interrupt = U64(rs) >= U64(rt);
break;
case ff_tlt:
do_interrupt = rs < rt;
break;
case ff_tltu:
do_interrupt = U64(rs) < U64(rt);
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:
break;
case ff_div:
if (I32_CHECK(rs) == INT_MIN && I32_CHECK(rt) == -1) {
i128hilo = U32(INT_MIN);
} else {
uint32_t div = I32_CHECK(rs) / I32_CHECK(rt);
uint32_t mod = I32_CHECK(rs) % I32_CHECK(rt);
i128hilo = (I64(mod) << 32) | div;
}
break;
case ff_ddiv:
if (I64(rs) == INT64_MIN && I64(rt) == -1) {
i128hilo = U64(INT64_MIN);
} else {
uint64_t div = rs / rt;
uint64_t mod = rs % rt;
i128hilo = (I128(mod) << 64) | div;
}
break;
case ff_divu: {
uint32_t div = U32(I32_CHECK(rs)) / U32(I32_CHECK(rt));
uint32_t mod = U32(I32_CHECK(rs)) % U32(I32_CHECK(rt));
i128hilo = (U64(mod) << 32) | div;
} break;
case ff_ddivu:
if (0 == rt) {
i128hilo = (I128(Unpredictable) << 64) | I64(Unpredictable);
} else {
uint64_t div = U64(rs) / U64(rt);
uint64_t mod = U64(rs) % U64(rt);
i128hilo = (I128(mod) << 64) | div;
}
break;
default:
MOZ_CRASH();
};
break;
case op_special2:
switch (instr->functionFieldRaw()) {
case ff_mul:
alu_out = I32(I32_CHECK(rs) *
I32_CHECK(rt)); break;
case ff_clz:
alu_out = U32(I32_CHECK(rs)) ? __builtin_clz(U32(I32_CHECK(rs))) : 32;
break;
case ff_dclz:
alu_out = U64(rs) ? __builtin_clzl(U64(rs)) : 64;
break;
default:
MOZ_CRASH();
};
break;
case op_special3:
switch (instr->functionFieldRaw()) {
case ff_ins: { uint16_t msb = rd_reg;
uint16_t lsb = sa;
uint16_t size = msb - lsb + 1;
uint32_t mask = (1 << size) - 1;
if (lsb > msb) {
alu_out = Unpredictable;
} else {
alu_out = (U32(I32_CHECK(rt)) & ~(mask << lsb)) |
((U32(I32_CHECK(rs)) & mask) << lsb);
}
break;
}
case ff_dins: { uint16_t msb = rd_reg;
uint16_t lsb = sa;
uint16_t size = msb - lsb + 1;
uint64_t mask = (1ul << size) - 1;
if (lsb > msb) {
alu_out = Unpredictable;
} else {
alu_out = (U64(rt) & ~(mask << lsb)) | ((U64(rs) & mask) << lsb);
}
break;
}
case ff_dinsm: { uint16_t msb = rd_reg;
uint16_t lsb = sa;
uint16_t size = msb - lsb + 33;
uint64_t mask = (1ul << size) - 1;
alu_out = (U64(rt) & ~(mask << lsb)) | ((U64(rs) & mask) << lsb);
break;
}
case ff_dinsu: { uint16_t msb = rd_reg;
uint16_t lsb = sa + 32;
uint16_t size = msb - lsb + 33;
uint64_t mask = (1ul << size) - 1;
if (sa > msb) {
alu_out = Unpredictable;
} else {
alu_out = (U64(rt) & ~(mask << lsb)) | ((U64(rs) & mask) << lsb);
}
break;
}
case ff_ext: { uint16_t msb = rd_reg;
uint16_t lsb = sa;
uint16_t size = msb + 1;
uint32_t mask = (1 << size) - 1;
if ((lsb + msb) > 31) {
alu_out = Unpredictable;
} else {
alu_out = (U32(I32_CHECK(rs)) & (mask << lsb)) >> lsb;
}
break;
}
case ff_dext: { uint16_t msb = rd_reg;
uint16_t lsb = sa;
uint16_t size = msb + 1;
uint64_t mask = (1ul << size) - 1;
alu_out = (U64(rs) & (mask << lsb)) >> lsb;
break;
}
case ff_dextm: { uint16_t msb = rd_reg;
uint16_t lsb = sa;
uint16_t size = msb + 33;
uint64_t mask = (1ul << size) - 1;
if ((lsb + msb + 32 + 1) > 64) {
alu_out = Unpredictable;
} else {
alu_out = (U64(rs) & (mask << lsb)) >> lsb;
}
break;
}
case ff_dextu: { uint16_t msb = rd_reg;
uint16_t lsb = sa + 32;
uint16_t size = msb + 1;
uint64_t mask = (1ul << size) - 1;
if ((lsb + msb + 1) > 64) {
alu_out = Unpredictable;
} else {
alu_out = (U64(rs) & (mask << lsb)) >> lsb;
}
break;
}
case ff_bshfl: { if (16 == sa) { alu_out = I64(I8(I32_CHECK(rt)));
} else if (24 == sa) { alu_out = I64(I16(I32_CHECK(rt)));
}
break;
}
default:
MOZ_CRASH();
};
break;
default:
MOZ_CRASH();
};
}
void Simulator::decodeTypeRegister(SimInstruction* instr) {
const Opcode op = instr->opcodeFieldRaw();
const int32_t rs_reg = instr->rsValue();
const int64_t rs = getRegister(rs_reg);
const int32_t rt_reg = instr->rtValue();
const int64_t rt = getRegister(rt_reg);
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();
__int128 i128hilo = 0;
unsigned __int128 u128hilo = 0;
int64_t alu_out = 0x12345678;
bool do_interrupt = false;
int64_t current_pc = get_pc();
int64_t next_pc = 0;
int32_t return_addr_reg = 31;
configureTypeRegister(instr, alu_out, i128hilo, u128hilo, next_pc,
return_addr_reg, do_interrupt);
signalExceptions();
switch (op) {
case op_cop1:
switch (instr->rsFieldRaw()) {
case rs_bc1: MOZ_CRASH();
break;
case rs_cfc1:
setRegister(rt_reg, alu_out);
MOZ_FALLTHROUGH;
case rs_mfc1:
setRegister(rt_reg, alu_out);
break;
case rs_dmfc1:
setRegister(rt_reg, alu_out);
break;
case rs_mfhc1:
setRegister(rt_reg, alu_out);
break;
case rs_ctc1:
MOZ_ASSERT(fs_reg == kFCSRRegister);
FCSR_ = registers_[rt_reg];
break;
case rs_mtc1:
setFpuRegisterLo(fs_reg, registers_[rt_reg]);
break;
case rs_dmtc1:
setFpuRegister(fs_reg, registers_[rt_reg]);
break;
case rs_mthc1:
setFpuRegisterHi(fs_reg, registers_[rt_reg]);
break;
case rs_s:
float f, ft_value, fs_value;
uint32_t cc, fcsr_cc;
int64_t i64;
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: MOZ_ASSERT((FCSR_ & 3) == 0);
MOZ_FALLTHROUGH;
case ff_round_w_fmt: { float rounded = std::floor(fs_value + 0.5);
int32_t result = I32(rounded);
if ((result & 1) != 0 && result - fs_value == 0.5) {
result--;
}
setFpuRegisterLo(fd_reg, result);
if (setFCSRRoundError<int32_t>(fs_value, rounded)) {
setFpuRegisterLo(fd_reg, kFPUInvalidResult);
}
break;
}
case ff_trunc_w_fmt: { float rounded = truncf(fs_value);
int32_t result = I32(rounded);
setFpuRegisterLo(fd_reg, result);
if (setFCSRRoundError<int32_t>(fs_value, rounded)) {
setFpuRegisterLo(fd_reg, kFPUInvalidResult);
}
break;
}
case ff_floor_w_fmt: { float rounded = std::floor(fs_value);
int32_t result = I32(rounded);
setFpuRegisterLo(fd_reg, result);
if (setFCSRRoundError<int32_t>(fs_value, rounded)) {
setFpuRegisterLo(fd_reg, kFPUInvalidResult);
}
break;
}
case ff_ceil_w_fmt: { float rounded = std::ceil(fs_value);
int32_t result = I32(rounded);
setFpuRegisterLo(fd_reg, result);
if (setFCSRRoundError<int32_t>(fs_value, rounded)) {
setFpuRegisterLo(fd_reg, kFPUInvalidResult);
}
break;
}
case ff_cvt_l_fmt: MOZ_ASSERT((FCSR_ & 3) == 0);
MOZ_FALLTHROUGH;
case ff_round_l_fmt: { float rounded = fs_value > 0 ? std::floor(fs_value + 0.5)
: std::ceil(fs_value - 0.5);
i64 = I64(rounded);
setFpuRegister(fd_reg, i64);
if (setFCSRRoundError<int64_t>(fs_value, rounded)) {
setFpuRegister(fd_reg, kFPUInvalidResult64);
}
break;
}
case ff_trunc_l_fmt: { float rounded = truncf(fs_value);
i64 = I64(rounded);
setFpuRegister(fd_reg, i64);
if (setFCSRRoundError<int64_t>(fs_value, rounded)) {
setFpuRegister(fd_reg, kFPUInvalidResult64);
}
break;
}
case ff_floor_l_fmt: { float rounded = std::floor(fs_value);
i64 = I64(rounded);
setFpuRegister(fd_reg, i64);
if (setFCSRRoundError<int64_t>(fs_value, rounded)) {
setFpuRegister(fd_reg, kFPUInvalidResult64);
}
break;
}
case ff_ceil_l_fmt: { float rounded = std::ceil(fs_value);
i64 = I64(rounded);
setFpuRegister(fd_reg, i64);
if (setFCSRRoundError<int64_t>(fs_value, rounded)) {
setFpuRegister(fd_reg, kFPUInvalidResult64);
}
break;
}
case ff_cvt_ps_s:
case ff_c_f_fmt:
MOZ_CRASH();
break;
case ff_movf_fmt:
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);
dt_value = getFpuRegisterDouble(ft_reg);
cc = instr->fcccValue();
fcsr_cc = GetFCSRConditionBit(cc);
switch (instr->functionFieldRaw()) {
case ff_add_fmt:
setFpuRegisterDouble(fd_reg, ds_value + dt_value);
break;
case ff_sub_fmt:
setFpuRegisterDouble(fd_reg, ds_value - dt_value);
break;
case ff_mul_fmt:
setFpuRegisterDouble(fd_reg, ds_value * dt_value);
break;
case ff_div_fmt:
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:
setFCSRBit(fcsr_cc,
mozilla::IsNaN(ds_value) || mozilla::IsNaN(dt_value));
break;
case ff_c_eq_fmt:
setFCSRBit(fcsr_cc, (ds_value == dt_value));
break;
case ff_c_ueq_fmt:
setFCSRBit(fcsr_cc,
(ds_value == dt_value) || (mozilla::IsNaN(ds_value) ||
mozilla::IsNaN(dt_value)));
break;
case ff_c_olt_fmt:
setFCSRBit(fcsr_cc, (ds_value < dt_value));
break;
case ff_c_ult_fmt:
setFCSRBit(fcsr_cc,
(ds_value < dt_value) || (mozilla::IsNaN(ds_value) ||
mozilla::IsNaN(dt_value)));
break;
case ff_c_ole_fmt:
setFCSRBit(fcsr_cc, (ds_value <= dt_value));
break;
case ff_c_ule_fmt:
setFCSRBit(fcsr_cc,
(ds_value <= dt_value) || (mozilla::IsNaN(ds_value) ||
mozilla::IsNaN(dt_value)));
break;
case ff_cvt_w_fmt: MOZ_ASSERT((FCSR_ & 3) == 0);
MOZ_FALLTHROUGH;
case ff_round_w_fmt: { double rounded = std::floor(ds_value + 0.5);
int32_t result = I32(rounded);
if ((result & 1) != 0 && result - ds_value == 0.5) {
result--;
}
setFpuRegisterLo(fd_reg, result);
if (setFCSRRoundError<int32_t>(ds_value, rounded)) {
setFpuRegisterLo(fd_reg, kFPUInvalidResult);
}
break;
}
case ff_trunc_w_fmt: { double rounded = trunc(ds_value);
int32_t result = I32(rounded);
setFpuRegisterLo(fd_reg, result);
if (setFCSRRoundError<int32_t>(ds_value, rounded)) {
setFpuRegisterLo(fd_reg, kFPUInvalidResult);
}
break;
}
case ff_floor_w_fmt: { double rounded = std::floor(ds_value);
int32_t result = I32(rounded);
setFpuRegisterLo(fd_reg, result);
if (setFCSRRoundError<int32_t>(ds_value, rounded)) {
setFpuRegisterLo(fd_reg, kFPUInvalidResult);
}
break;
}
case ff_ceil_w_fmt: { double rounded = std::ceil(ds_value);
int32_t result = I32(rounded);
setFpuRegisterLo(fd_reg, result);
if (setFCSRRoundError<int32_t>(ds_value, rounded)) {
setFpuRegisterLo(fd_reg, kFPUInvalidResult);
}
break;
}
case ff_cvt_s_fmt: setFpuRegisterFloat(fd_reg, static_cast<float>(ds_value));
break;
case ff_cvt_l_fmt: MOZ_ASSERT((FCSR_ & 3) == 0);
MOZ_FALLTHROUGH;
case ff_round_l_fmt: { double rounded = ds_value > 0 ? std::floor(ds_value + 0.5)
: std::ceil(ds_value - 0.5);
i64 = I64(rounded);
setFpuRegister(fd_reg, i64);
if (setFCSRRoundError<int64_t>(ds_value, rounded)) {
setFpuRegister(fd_reg, kFPUInvalidResult64);
}
break;
}
case ff_trunc_l_fmt: { double rounded = trunc(ds_value);
i64 = I64(rounded);
setFpuRegister(fd_reg, i64);
if (setFCSRRoundError<int64_t>(ds_value, rounded)) {
setFpuRegister(fd_reg, kFPUInvalidResult64);
}
break;
}
case ff_floor_l_fmt: { double rounded = std::floor(ds_value);
i64 = I64(rounded);
setFpuRegister(fd_reg, i64);
if (setFCSRRoundError<int64_t>(ds_value, rounded)) {
setFpuRegister(fd_reg, kFPUInvalidResult64);
}
break;
}
case ff_ceil_l_fmt: { double rounded = std::ceil(ds_value);
i64 = I64(rounded);
setFpuRegister(fd_reg, i64);
if (setFCSRRoundError<int64_t>(ds_value, rounded)) {
setFpuRegister(fd_reg, kFPUInvalidResult64);
}
break;
}
case ff_c_f_fmt:
MOZ_CRASH();
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;
case ff_movf_fmt:
cc = instr->fbccValue();
fcsr_cc = GetFCSRConditionBit(cc);
if (testFCSRBit(fcsr_cc)) {
setFpuRegisterDouble(fd_reg, getFpuRegisterDouble(fs_reg));
}
break;
default:
MOZ_CRASH();
}
break;
case rs_w:
switch (instr->functionFieldRaw()) {
case ff_cvt_s_fmt: i64 = getFpuRegisterLo(fs_reg);
setFpuRegisterFloat(fd_reg, static_cast<float>(i64));
break;
case ff_cvt_d_fmt: i64 = getFpuRegisterLo(fs_reg);
setFpuRegisterDouble(fd_reg, static_cast<double>(i64));
break;
default:
MOZ_CRASH();
};
break;
case rs_l:
switch (instr->functionFieldRaw()) {
case ff_cvt_d_fmt: i64 = getFpuRegister(fs_reg);
setFpuRegisterDouble(fd_reg, static_cast<double>(i64));
break;
case ff_cvt_s_fmt:
i64 = getFpuRegister(fs_reg);
setFpuRegisterFloat(fd_reg, static_cast<float>(i64));
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;
}
case ff_mult:
setRegister(LO, I32(i128hilo & 0xffffffff));
setRegister(HI, I32(i128hilo >> 32));
break;
case ff_dmult:
setRegister(LO, I64(i128hilo & 0xfffffffffffffffful));
setRegister(HI, I64(i128hilo >> 64));
break;
case ff_multu:
setRegister(LO, I32(u128hilo & 0xffffffff));
setRegister(HI, I32(u128hilo >> 32));
break;
case ff_dmultu:
setRegister(LO, I64(u128hilo & 0xfffffffffffffffful));
setRegister(HI, I64(u128hilo >> 64));
break;
case ff_div:
case ff_divu:
setRegister(LO, I32(i128hilo & 0xffffffff));
setRegister(HI, I32(i128hilo >> 32));
break;
case ff_ddiv:
case ff_ddivu:
setRegister(LO, I64(i128hilo & 0xfffffffffffffffful));
setRegister(HI, I64(i128hilo >> 64));
break;
case ff_sync:
break;
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_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)) { 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: setRegister(rd_reg, alu_out);
};
break;
case op_special2:
switch (instr->functionFieldRaw()) {
case ff_mul:
setRegister(rd_reg, alu_out);
setRegister(LO, Unpredictable);
setRegister(HI, Unpredictable);
break;
default: setRegister(rd_reg, alu_out);
}
break;
case op_special3:
switch (instr->functionFieldRaw()) {
case ff_ins:
case ff_dins:
case ff_dinsm:
case ff_dinsu:
setRegister(rt_reg, alu_out);
break;
case ff_ext:
case ff_dext:
case ff_dextm:
case ff_dextu:
setRegister(rt_reg, alu_out);
break;
case ff_bshfl:
setRegister(rd_reg, alu_out);
break;
default:
MOZ_CRASH();
};
break;
default:
setRegister(rd_reg, alu_out);
};
}
void Simulator::decodeTypeImmediate(SimInstruction* instr) {
Opcode op = instr->opcodeFieldRaw();
int64_t rs = getRegister(instr->rsValue());
int32_t rt_reg = instr->rtValue(); int64_t rt = getRegister(rt_reg);
int16_t imm16 = instr->imm16Value();
int32_t ft_reg = instr->ftValue();
uint32_t oe_imm16 = 0xffff & imm16;
int32_t se_imm16 = imm16;
int64_t current_pc = get_pc();
int64_t next_pc = bad_ra;
bool do_branch = false;
bool execute_branch_delay_instruction = false;
int64_t alu_out = 0;
double fp_out = 0.0;
uint32_t cc, cc_value, fcsr_cc;
uint64_t addr = 0x0;
uint64_t mem_value = 0x0;
__int128 temp;
switch (op) {
case op_cop1:
switch (instr->rsFieldRaw()) {
case rs_bc1: 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;
if (do_branch) {
next_pc = current_pc + (imm16 << 2) + SimInstruction::kInstrSize;
} else {
next_pc = current_pc + kBranchReturnOffset;
}
break;
default:
MOZ_CRASH();
};
break;
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:
execute_branch_delay_instruction = true;
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_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;
case op_addi:
alu_out = I32_CHECK(rs) + se_imm16;
if ((alu_out << 32) != (alu_out << 31)) {
exceptions[kIntegerOverflow] = 1;
}
alu_out = I32_CHECK(alu_out);
break;
case op_daddi:
temp = alu_out = rs + se_imm16;
if ((temp << 64) != (temp << 63)) {
exceptions[kIntegerOverflow] = 1;
}
alu_out = I64(temp);
break;
case op_addiu:
alu_out = I32(I32_CHECK(rs) + se_imm16);
break;
case op_daddiu:
alu_out = rs + se_imm16;
break;
case op_slti:
alu_out = (rs < se_imm16) ? 1 : 0;
break;
case op_sltiu:
alu_out = (U64(rs) < U64(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 = (se_imm16 << 16);
break;
case op_lbu:
addr = rs + se_imm16;
alu_out = readBU(addr, instr);
break;
case op_lb:
addr = rs + se_imm16;
alu_out = readB(addr, instr);
break;
case op_lhu:
addr = rs + se_imm16;
alu_out = readHU(addr, instr);
break;
case op_lh:
addr = rs + se_imm16;
alu_out = readH(addr, instr);
break;
case op_lwu:
addr = rs + se_imm16;
alu_out = readWU(addr, instr);
break;
case op_lw:
addr = rs + se_imm16;
alu_out = readW(addr, instr);
break;
case op_lwl: {
uint8_t al_offset = (rs + se_imm16) & 3;
uint8_t byte_shift = 3 - 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_lwr: {
uint8_t al_offset = (rs + se_imm16) & 3;
uint8_t byte_shift = 3 - 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 = U32(alu_out) >> al_offset * 8;
alu_out |= rt & mask;
break;
}
case op_ll:
addr = rs + se_imm16;
alu_out = loadLinkedW(addr, instr);
break;
case op_lld:
addr = rs + se_imm16;
alu_out = loadLinkedD(addr, instr);
break;
case op_ld:
addr = rs + se_imm16;
alu_out = readDW(addr, instr);
break;
case op_ldl: {
uint8_t al_offset = (rs + se_imm16) & 7;
uint8_t byte_shift = 7 - al_offset;
uint64_t mask = (1ul << byte_shift * 8) - 1;
addr = rs + se_imm16 - al_offset;
alu_out = readDW(addr, instr);
alu_out <<= byte_shift * 8;
alu_out |= rt & mask;
break;
}
case op_ldr: {
uint8_t al_offset = (rs + se_imm16) & 7;
uint8_t byte_shift = 7 - al_offset;
uint64_t mask = al_offset ? (~0ul << (byte_shift + 1) * 8) : 0;
addr = rs + se_imm16 - al_offset;
alu_out = readDW(addr, instr);
alu_out = U64(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_sw:
addr = rs + se_imm16;
break;
case op_swl: {
uint8_t al_offset = (rs + se_imm16) & 3;
uint8_t byte_shift = 3 - 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 |= U32(rt) >> byte_shift * 8;
break;
}
case op_swr: {
uint8_t al_offset = (rs + se_imm16) & 3;
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_sc:
addr = rs + se_imm16;
break;
case op_scd:
addr = rs + se_imm16;
break;
case op_sd:
addr = rs + se_imm16;
break;
case op_sdl: {
uint8_t al_offset = (rs + se_imm16) & 7;
uint8_t byte_shift = 7 - al_offset;
uint64_t mask = byte_shift ? (~0ul << (al_offset + 1) * 8) : 0;
addr = rs + se_imm16 - al_offset;
mem_value = readW(addr, instr) & mask;
mem_value |= U64(rt) >> byte_shift * 8;
break;
}
case op_sdr: {
uint8_t al_offset = (rs + se_imm16) & 7;
uint64_t mask = (1ul << 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;
default:
MOZ_CRASH();
};
signalExceptions();
switch (op) {
case op_beq:
case op_bne:
case op_blez:
case op_bgtz:
execute_branch_delay_instruction = true;
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;
case op_addi:
case op_daddi:
case op_addiu:
case op_daddiu:
case op_slti:
case op_sltiu:
case op_andi:
case op_ori:
case op_xori:
case op_lui:
setRegister(rt_reg, alu_out);
break;
case op_lbu:
case op_lb:
case op_lhu:
case op_lh:
case op_lwu:
case op_lw:
case op_lwl:
case op_lwr:
case op_ll:
case op_lld:
case op_ld:
case op_ldl:
case op_ldr:
setRegister(rt_reg, alu_out);
break;
case op_sb:
writeB(addr, I8(rt), instr);
break;
case op_sh:
writeH(addr, U16(rt), instr);
break;
case op_sw:
writeW(addr, I32(rt), instr);
break;
case op_swl:
writeW(addr, I32(mem_value), instr);
break;
case op_swr:
writeW(addr, I32(mem_value), instr);
break;
case op_sc:
setRegister(rt_reg, storeConditionalW(addr, I32(rt), instr));
break;
case op_scd:
setRegister(rt_reg, storeConditionalD(addr, rt, instr));
break;
case op_sd:
writeDW(addr, rt, instr);
break;
case op_sdl:
writeDW(addr, mem_value, instr);
break;
case op_sdr:
writeDW(addr, mem_value, instr);
break;
case op_lwc1:
setFpuRegisterLo(ft_reg, alu_out);
break;
case op_ldc1:
setFpuRegisterDouble(ft_reg, fp_out);
break;
case op_swc1:
writeW(addr, getFpuRegisterLo(ft_reg), instr);
break;
case op_sdc1:
writeD(addr, getFpuRegisterDouble(ft_reg), instr);
break;
default:
break;
};
if (execute_branch_delay_instruction) {
SimInstruction* branch_delay_instr = reinterpret_cast<SimInstruction*>(
current_pc + SimInstruction::kInstrSize);
branchDelayInstructionDecode(branch_delay_instr);
}
if (next_pc != bad_ra) {
set_pc(next_pc);
}
}
void Simulator::decodeTypeJump(SimInstruction* instr) {
int64_t current_pc = get_pc();
int64_t pc_high_bits = current_pc & 0xfffffffff0000000ul;
int64_t next_pc = pc_high_bits | (instr->imm26Value() << 2);
SimInstruction* branch_delay_instr = reinterpret_cast<SimInstruction*>(
current_pc + SimInstruction::kInstrSize);
branchDelayInstructionDecode(branch_delay_instr);
if (instr->isLinkingInstruction()) {
setRegister(31, current_pc + 2 * SimInstruction::kInstrSize);
}
set_pc(next_pc);
pc_modified_ = true;
}
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<int64_t>(instr) + SimInstruction::kInstrSize);
}
}
void Simulator::branchDelayInstructionDecode(SimInstruction* instr) {
if (instr->instructionBits() == NopInst) {
return;
}
if (instr->isForbiddenInBranchDelay()) {
MOZ_CRASH("Eror:Unexpected opcode in a branch delay slot.");
}
instructionDecode(instr);
}
void Simulator::enable_single_stepping(SingleStepCallback cb, void* arg) {
single_stepping_ = true;
single_step_callback_ = cb;
single_step_callback_arg_ = arg;
single_step_callback_(single_step_callback_arg_, this, (void*)get_pc());
}
void Simulator::disable_single_stepping() {
if (!single_stepping_) {
return;
}
single_step_callback_(single_step_callback_arg_, this, (void*)get_pc());
single_stepping_ = false;
single_step_callback_ = nullptr;
single_step_callback_arg_ = nullptr;
}
template <bool enableStopSimAt>
void Simulator::execute() {
if (single_stepping_) {
single_step_callback_(single_step_callback_arg_, this, nullptr);
}
int64_t program_counter = get_pc();
while (program_counter != end_sim_pc) {
if (enableStopSimAt && (icount_ == Simulator::StopSimAt)) {
MipsDebugger dbg(this);
dbg.debug();
} else {
if (single_stepping_) {
single_step_callback_(single_step_callback_arg_, this,
(void*)program_counter);
}
SimInstruction* instr =
reinterpret_cast<SimInstruction*>(program_counter);
instructionDecode(instr);
icount_++;
}
program_counter = get_pc();
}
if (single_stepping_) {
single_step_callback_(single_step_callback_arg_, this, nullptr);
}
}
void Simulator::callInternal(uint8_t* entry) {
setRegister(pc, reinterpret_cast<int64_t>(entry));
setRegister(ra, end_sim_pc);
int64_t s0_val = getRegister(s0);
int64_t s1_val = getRegister(s1);
int64_t s2_val = getRegister(s2);
int64_t s3_val = getRegister(s3);
int64_t s4_val = getRegister(s4);
int64_t s5_val = getRegister(s5);
int64_t s6_val = getRegister(s6);
int64_t s7_val = getRegister(s7);
int64_t gp_val = getRegister(gp);
int64_t sp_val = getRegister(sp);
int64_t fp_val = getRegister(fp);
int64_t callee_saved_value = icount_;
setRegister(s0, callee_saved_value);
setRegister(s1, callee_saved_value);
setRegister(s2, callee_saved_value);
setRegister(s3, callee_saved_value);
setRegister(s4, callee_saved_value);
setRegister(s5, callee_saved_value);
setRegister(s6, callee_saved_value);
setRegister(s7, callee_saved_value);
setRegister(gp, callee_saved_value);
setRegister(fp, callee_saved_value);
if (Simulator::StopSimAt != -1) {
execute<true>();
} else {
execute<false>();
}
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));
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);
}
int64_t Simulator::call(uint8_t* entry, int argument_count, ...) {
va_list parameters;
va_start(parameters, argument_count);
int64_t original_stack = getRegister(sp);
int64_t entry_stack = original_stack;
if (argument_count > kCArgSlotCount) {
entry_stack = entry_stack - argument_count * sizeof(int64_t);
} else {
entry_stack = entry_stack - kCArgsSlotsSize;
}
entry_stack &= ~U64(ABIStackAlignment - 1);
intptr_t* stack_argument = reinterpret_cast<intptr_t*>(entry_stack);
for (int i = 0; i < argument_count; i++) {
js::jit::Register argReg;
if (GetIntArgReg(i, &argReg)) {
setRegister(argReg.code(), va_arg(parameters, int64_t));
} else {
stack_argument[i] = va_arg(parameters, int64_t);
}
}
va_end(parameters);
setRegister(sp, entry_stack);
callInternal(entry);
MOZ_ASSERT(entry_stack == getRegister(sp));
setRegister(sp, original_stack);
int64_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;
}
} }
js::jit::Simulator* JSContext::simulator() const { return simulator_; }
uintptr_t* JSContext::addressOfSimulatorStackLimit() {
return simulator_->addressOfStackLimit();
}