/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80: */ // Copyright 2020 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following // disclaimer in the documentation and/or other materials provided // with the distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// ----------------------------------------------------------------------------- // LoongArch64 assembly various constants.
class SimInstruction { public: enum {
kInstrSize = 4, // On LoongArch, PC cannot actually be directly accessed. We behave as if PC // was always the value of the current instruction being executed.
kPCReadOffset = 0
};
// Get the raw instruction bits. inline Instr instructionBits() const { return *reinterpret_cast<const Instr*>(this);
}
// Set the raw instruction bits to value. inlinevoid setInstructionBits(Instr value) {
*reinterpret_cast<Instr*>(this) = value;
}
// Read one particular bit out of the instruction bits. inlineint bit(int nr) const { return (instructionBits() >> nr) & 1; }
// Read a bit field out of the instruction bits. inlineint bits(int hi, int lo) const { return (instructionBits() >> lo) & ((2 << (hi - lo)) - 1);
}
// Check for kOp6Type switch (bits(31, 26) << 26) { case op_beqz: case op_bnez: case op_bcz: case op_jirl: case op_b: case op_bl: case op_beq: case op_bne: case op_blt: case op_bge: case op_bltu: case op_bgeu: case op_addu16i_d:
kType = kOp6Type; break; default:
kType = kUnsupported;
}
if (kType == kUnsupported) { // Check for kOp7Type switch (bits(31, 25) << 25) { case op_lu12i_w: case op_lu32i_d: case op_pcaddi: case op_pcalau12i: case op_pcaddu12i: case op_pcaddu18i:
kType = kOp7Type; break; default:
kType = kUnsupported;
}
}
if (kType == kUnsupported) { // Check for kOp8Type switch (bits(31, 24) << 24) { case op_ll_w: case op_sc_w: case op_ll_d: case op_sc_d: case op_ldptr_w: case op_stptr_w: case op_ldptr_d: case op_stptr_d:
kType = kOp8Type; break; default:
kType = kUnsupported;
}
}
if (kType == kUnsupported) { // Check for kOp10Type switch (bits(31, 22) << 22) { case op_bstrins_d: case op_bstrpick_d: case op_slti: case op_sltui: case op_addi_w: case op_addi_d: case op_lu52i_d: case op_andi: case op_ori: case op_xori: case op_ld_b: case op_ld_h: case op_ld_w: case op_ld_d: case op_st_b: case op_st_h: case op_st_w: case op_st_d: case op_ld_bu: case op_ld_hu: case op_ld_wu: case op_preld: case op_fld_s: case op_fst_s: case op_fld_d: case op_fst_d: case op_bstr_w: // BSTRINS_W & BSTRPICK_W
kType = kOp10Type; break; default:
kType = kUnsupported;
}
}
if (kType == kUnsupported) { // Check for kOp11Type switch (bits(31, 21) << 21) { case op_bstr_w:
kType = kOp11Type; break; default:
kType = kUnsupported;
}
}
if (kType == kUnsupported) { // Check for kOp12Type switch (bits(31, 20) << 20) { case op_fmadd_s: case op_fmadd_d: case op_fmsub_s: case op_fmsub_d: case op_fnmadd_s: case op_fnmadd_d: case op_fnmsub_s: case op_fnmsub_d: case op_fcmp_cond_s: case op_fcmp_cond_d:
kType = kOp12Type; break; default:
kType = kUnsupported;
}
}
if (kType == kUnsupported) { // Check for kOp14Type switch (bits(31, 18) << 18) { case op_bytepick_d: case op_fsel:
kType = kOp14Type; break; default:
kType = kUnsupported;
}
}
if (kType == kUnsupported) { // Check for kOp15Type switch (bits(31, 17) << 17) { case op_bytepick_w: case op_alsl_w: case op_alsl_wu: case op_alsl_d:
kType = kOp15Type; break; default:
kType = kUnsupported;
}
}
if (kType == kUnsupported) { // Check for kOp16Type switch (bits(31, 16) << 16) { case op_slli_d: case op_srli_d: case op_srai_d: case op_rotri_d:
kType = kOp16Type; break; default:
kType = kUnsupported;
}
}
if (kType == kUnsupported) { // Check for kOp17Type switch (bits(31, 15) << 15) { case op_slli_w: case op_srli_w: case op_srai_w: case op_rotri_w: case op_add_w: case op_add_d: case op_sub_w: case op_sub_d: case op_slt: case op_sltu: case op_maskeqz: case op_masknez: case op_nor: case op_and: case op_or: case op_xor: case op_orn: case op_andn: case op_sll_w: case op_srl_w: case op_sra_w: case op_sll_d: case op_srl_d: case op_sra_d: case op_rotr_w: case op_rotr_d: case op_mul_w: case op_mul_d: case op_mulh_d: case op_mulh_du: case op_mulh_w: case op_mulh_wu: case op_mulw_d_w: case op_mulw_d_wu: case op_div_w: case op_mod_w: case op_div_wu: case op_mod_wu: case op_div_d: case op_mod_d: case op_div_du: case op_mod_du: case op_break: case op_fadd_s: case op_fadd_d: case op_fsub_s: case op_fsub_d: case op_fmul_s: case op_fmul_d: case op_fdiv_s: case op_fdiv_d: case op_fmax_s: case op_fmax_d: case op_fmin_s: case op_fmin_d: case op_fmaxa_s: case op_fmaxa_d: case op_fmina_s: case op_fmina_d: case op_fcopysign_s: case op_fcopysign_d: case op_ldx_b: case op_ldx_h: case op_ldx_w: case op_ldx_d: case op_stx_b: case op_stx_h: case op_stx_w: case op_stx_d: case op_ldx_bu: case op_ldx_hu: case op_ldx_wu: case op_fldx_s: case op_fldx_d: case op_fstx_s: case op_fstx_d: case op_amswap_w: case op_amswap_d: case op_amadd_w: case op_amadd_d: case op_amand_w: case op_amand_d: case op_amor_w: case op_amor_d: case op_amxor_w: case op_amxor_d: case op_ammax_w: case op_ammax_d: case op_ammin_w: case op_ammin_d: case op_ammax_wu: case op_ammax_du: case op_ammin_wu: case op_ammin_du: case op_amswap_db_w: case op_amswap_db_d: case op_amadd_db_w: case op_amadd_db_d: case op_amand_db_w: case op_amand_db_d: case op_amor_db_w: case op_amor_db_d: case op_amxor_db_w: case op_amxor_db_d: case op_ammax_db_w: case op_ammax_db_d: case op_ammin_db_w: case op_ammin_db_d: case op_ammax_db_wu: case op_ammax_db_du: case op_ammin_db_wu: case op_ammin_db_du: case op_dbar: case op_ibar:
kType = kOp17Type; break; default:
kType = kUnsupported;
}
}
if (kType == kUnsupported) { // Check for kOp22Type switch (bits(31, 10) << 10) { case op_clo_w: case op_clz_w: case op_cto_w: case op_ctz_w: case op_clo_d: case op_clz_d: case op_cto_d: case op_ctz_d: case op_revb_2h: case op_revb_4h: case op_revb_2w: case op_revb_d: case op_revh_2w: case op_revh_d: case op_bitrev_4b: case op_bitrev_8b: case op_bitrev_w: case op_bitrev_d: case op_ext_w_h: case op_ext_w_b: case op_fabs_s: case op_fabs_d: case op_fneg_s: case op_fneg_d: case op_fsqrt_s: case op_fsqrt_d: case op_fmov_s: case op_fmov_d: case op_movgr2fr_w: case op_movgr2fr_d: case op_movgr2frh_w: case op_movfr2gr_s: case op_movfr2gr_d: case op_movfrh2gr_s: case op_movfcsr2gr: case op_movfr2cf: case op_movgr2cf: case op_fcvt_s_d: case op_fcvt_d_s: case op_ftintrm_w_s: case op_ftintrm_w_d: case op_ftintrm_l_s: case op_ftintrm_l_d: case op_ftintrp_w_s: case op_ftintrp_w_d: case op_ftintrp_l_s: case op_ftintrp_l_d: case op_ftintrz_w_s: case op_ftintrz_w_d: case op_ftintrz_l_s: case op_ftintrz_l_d: case op_ftintrne_w_s: case op_ftintrne_w_d: case op_ftintrne_l_s: case op_ftintrne_l_d: case op_ftint_w_s: case op_ftint_w_d: case op_ftint_l_s: case op_ftint_l_d: case op_ffint_s_w: case op_ffint_s_l: case op_ffint_d_w: case op_ffint_d_l: case op_frint_s: case op_frint_d:
kType = kOp22Type; break; default:
kType = kUnsupported;
}
}
if (kType == kUnsupported) { // Check for kOp24Type switch (bits(31, 8) << 8) { case op_movcf2fr: case op_movcf2gr:
kType = kOp24Type; break; default:
kType = kUnsupported;
}
}
private: char data_[kPageSize]; // The cached data. staticconstint 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>;
// The loong64Debugger class is used by the simulator while debugging simulated // code. class loong64Debugger { public: explicit loong64Debugger(Simulator* sim) : sim_(sim) {}
void stop(SimInstruction* instr); void debug(); // Print all registers with a nice formatting. void printAllRegs(); void printAllRegsIncludingFPU();
private: // We set the breakpoint code to 0x7fff to easily recognize it. staticconst Instr kBreakpointInstr = op_break | (0x7fff & CODEMask); staticconst Instr kNopInstr = 0x0;
// 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();
};
void loong64Debugger::stop(SimInstruction* instr) { // Get the stop code.
uint32_t code = instr->bits(25, 6); // Retrieve the encoded address, which comes just after this stop. char* msg =
*reinterpret_cast<char**>(sim_->get_pc() + SimInstruction::kInstrSize); // Update this stop description. if (!sim_->watchedStops_[code].desc_) {
sim_->watchedStops_[code].desc_ = msg;
} // Print the stop message and code if it is not the default code. if (code != kMaxStopCode) {
printf("Simulator hit stop %u: %s\n", code, msg);
} else {
printf("Simulator hit %s\n", msg);
}
sim_->set_pc(sim_->get_pc() + 2 * SimInstruction::kInstrSize);
debug();
}
bool loong64Debugger::setBreakpoint(SimInstruction* breakpc) { // Check if a breakpoint can be set. If not return without any side-effects. if (sim_->break_pc_ != nullptr) { returnfalse;
}
// 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. returntrue;
}
printf("\n\n"); // f0, f1, f2, ... f31. for (uint32_t i = 0; i < FloatRegisters::TotalPhys; i++) {
printf("%3s: 0x%016" PRIi64 "\tflt: %-8.4g\tdbl: %-16.4g\n",
FloatRegisters::GetName(i), getFPURegisterValueLong(i),
getFPURegisterValueFloat(i), getFPURegisterValueDouble(i));
}
}
staticchar* ReadLine(constchar* prompt) {
UniqueChars result; char lineBuf[256]; int offset = 0; bool keepGoing = true;
fprintf(stdout, "%s", prompt);
fflush(stdout); while (keepGoing) { if (fgets(lineBuf, sizeof(lineBuf), stdin) == nullptr) { // fgets got an error. Just give up. return nullptr;
} int len = strlen(lineBuf); if (len > 0 && lineBuf[len - 1] == '\n') { // Since we read a new line we are done reading the line. This // will exit the loop after copying this buffer into the result.
keepGoing = false;
} if (!result) { // Allocate the initial result and make room for the terminating '\0'
result.reset(js_pod_malloc<char>(len + 1)); if (!result) { return nullptr;
}
} else { // Allocate a new result with enough room for the new addition. int new_len = offset + len + 1; char* new_result = js_pod_malloc<char>(new_len); if (!new_result) { return nullptr;
} // Copy the existing input into the new array and set the new // array as the result.
memcpy(new_result, result.get(), offset * sizeof(char));
result.reset(new_result);
} // Copy the newly read line into the result.
memcpy(result.get() + offset, lineBuf, len * sizeof(char));
offset += len;
}
if (argc == 1) {
cur = reinterpret_cast<uint8_t*>(sim_->get_pc());
end = cur + (10 * SimInstruction::kInstrSize);
} elseif (argc == 2) { Register reg = Register::FromName(arg1); if (reg != InvalidReg || strncmp(arg1, "0x", 2) == 0) { // The argument is an address or a register name.
int64_t value; if (getValue(arg1, &value)) {
cur = reinterpret_cast<uint8_t*>(value); // Disassemble 10 instructions at <arg1>.
end = cur + (10 * SimInstruction::kInstrSize);
}
} else { // The argument is the number of instructions.
int64_t value; if (getValue(arg1, &value)) {
cur = reinterpret_cast<uint8_t*>(sim_->get_pc()); // Disassemble <arg1> instructions.
end = cur + (value * SimInstruction::kInstrSize);
}
}
} else {
int64_t value1;
int64_t value2; if (getValue(arg1, &value1) && getValue(arg2, &value2)) {
cur = reinterpret_cast<uint8_t*>(value1);
end = cur + (value2 * SimInstruction::kInstrSize);
}
}
while (cur < end) {
DisassembleInstruction(uint64_t(cur));
cur += SimInstruction::kInstrSize;
}
} elseif (strcmp(cmd, "gdb") == 0) {
printf("relinquishing control to gdb\n"); asm("int $3");
printf("regaining control from gdb\n");
} elseif (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");
}
} elseif (strcmp(cmd, "del") == 0) { if (!deleteBreakpoint(nullptr)) {
printf("deleting breakpoint failed\n");
}
} elseif (strcmp(cmd, "flags") == 0) {
printf("No flags on LOONG64 !\n");
} elseif (strcmp(cmd, "stop") == 0) {
int64_t value;
intptr_t stop_pc = sim_->get_pc() - 2 * SimInstruction::kInstrSize;
SimInstruction* stop_instr = reinterpret_cast<SimInstruction*>(stop_pc);
SimInstruction* msg_address = reinterpret_cast<SimInstruction*>(
stop_pc + SimInstruction::kInstrSize); if ((argc == 2) && (strcmp(arg1, "unstop") == 0)) { // Remove the current stop. if (sim_->isStopInstruction(stop_instr)) {
stop_instr->setInstructionBits(kNopInstr);
msg_address->setInstructionBits(kNopInstr);
} else {
printf("Not at debugger stop.\n");
}
} elseif (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);
}
} elseif (getValue(arg2, &value)) {
sim_->printStopInfo(value);
} else {
printf("Unrecognized argument.\n");
}
} elseif (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);
}
} elseif (getValue(arg2, &value)) {
sim_->enableStop(value);
} else {
printf("Unrecognized argument.\n");
}
} elseif (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);
}
} elseif (getValue(arg2, &value)) {
sim_->disableStop(value);
} else {
printf("Unrecognized argument.\n");
}
}
} else {
printf("Wrong usage. Use help command for more information.\n");
}
} elseif ((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();
if (cache_hit) { // Check that the data in memory matches the contents of the I-cache.
mozilla::DebugOnly<int> cmpret =
memcmp(reinterpret_cast<void*>(instr), cache_page->cachedData(offset),
SimInstruction::kInstrSize);
MOZ_ASSERT(cmpret == 0);
} else { // Cache miss. Load memory into the cache.
memcpy(cached_line, line, CachePage::kLineLength);
*cache_valid_byte = CachePage::LINE_VALID;
}
}
// Set up architecture state. // All registers are initialized to zero to start with. for (int i = 0; i < Register::kNumSimuRegisters; i++) {
registers_[i] = 0;
} for (int i = 0; i < Simulator::FPURegister::kNumFPURegisters; i++) {
FPUregisters_[i] = 0;
}
for (int i = 0; i < kNumCFRegisters; i++) {
CFregisters_[i] = 0;
}
// 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. staticconst size_t stackSize = 2 * 1024 * 1024;
stack_ = js_pod_malloc<char>(stackSize); if (!stack_) { returnfalse;
}
// Leave a safety margin of 1MB to prevent overrunning the stack when // pushing values (total stack size is 2MB).
stackLimit_ = reinterpret_cast<uintptr_t>(stack_) + 1024 * 1024;
// The sp is initialized to point to the bottom (high address) of the // allocated stack area. To be safe in potential stack underflows we leave // some buffer below.
registers_[sp] = reinterpret_cast<int64_t>(stack_) + stackSize - 64;
returntrue;
}
// 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 { friendclass 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);
}
Redirection* current = SimulatorProcess::redirection(); for (; current != nullptr; current = current->next_) { if (current->nativeFunction_ == nativeFunction) {
MOZ_ASSERT(current->type() == type); return current;
}
}
// Note: we can't use js_new here because the constructor is private.
AutoEnterOOMUnsafeRegion oomUnsafe;
Redirection* redir = js_pod_malloc<Redirection>(1); if (!redir) {
oomUnsafe.crash("Simulator redirection");
} new (redir) Redirection(nativeFunction, type); return redir;
}
// Get the active Simulator for the current thread.
Simulator* Simulator::Current() {
JSContext* cx = TlsContext.get();
MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime())); return cx->simulator();
}
// Sets the register in the architecture state. It will also deal with updating // Simulator internal state for special registers such as PC. void Simulator::setRegister(int reg, int64_t value) {
MOZ_ASSERT((reg >= 0) && (reg < Register::kNumSimuRegisters)); if (reg == pc) {
pc_modified_ = true;
}
void Simulator::setFpuRegisterInvalidResult64(float original, float rounded, int fpureg) { // The value of INT64_MAX (2^63-1) can't be represented as double exactly, // loading the most accurate representation into max_int64, which is 2^63. double max_int64 = static_cast<double>(INT64_MAX); double min_int64 = static_cast<double>(INT64_MIN);
void Simulator::setFpuRegisterInvalidResult64(double original, double rounded, int fpureg) { // The value of INT64_MAX (2^63-1) can't be represented as double exactly, // loading the most accurate representation into max_int64, which is 2^63. double max_int64 = static_cast<double>(INT64_MAX); double min_int64 = static_cast<double>(INT64_MIN);
// Get the register from the architecture state. This function does handle // the special case of accessing the PC register.
int64_t Simulator::getRegister(int reg) const {
MOZ_ASSERT((reg >= 0) && (reg < Register::kNumSimuRegisters)); if (reg == 0) { return 0;
} return registers_[reg] + ((reg == pc) ? SimInstruction::kPCReadOffset : 0);
}
// Sets the rounding error codes in FCSR based on the result of the rounding. // Returns true if the operation was invalid. template <typename T> bool Simulator::setFCSRRoundError(double original, double rounded) { bool ret = false;
if ((longdouble)rounded > (longdouble)std::numeric_limits<T>::max() ||
(longdouble)rounded < (longdouble)std::numeric_limits<T>::min()) {
setFCSRBit(kFCSROverflowFlagBit, true);
setFCSRBit(kFCSROverflowCauseBit, true); // The reference is not really clear but it seems this is required:
setFCSRBit(kFCSRInvalidOpFlagBit, true);
setFCSRBit(kFCSRInvalidOpCauseBit, true);
ret = true;
}
return ret;
}
// For cvt instructions only template <typename T> void Simulator::roundAccordingToFCSR(T toRound, T* rounded,
int32_t* rounded_int) { switch ((FCSR_ >> 8) & 3) { case kRoundToNearest:
*rounded = std::floor(toRound + 0.5);
*rounded_int = static_cast<int32_t>(*rounded); if ((*rounded_int & 1) != 0 && *rounded_int - toRound == 0.5) { // If the number is halfway between two integers, // round to the even one.
*rounded_int -= 1;
*rounded -= 1.;
} break; case kRoundToZero:
*rounded = trunc(toRound);
*rounded_int = static_cast<int32_t>(*rounded); break; case kRoundToPlusInf:
*rounded = std::ceil(toRound);
*rounded_int = static_cast<int32_t>(*rounded); break; case kRoundToMinusInf:
*rounded = std::floor(toRound);
*rounded_int = static_cast<int32_t>(*rounded); break;
}
}
template <typename T> void Simulator::round64AccordingToFCSR(T toRound, T* rounded,
int64_t* rounded_int) { switch ((FCSR_ >> 8) & 3) { case kRoundToNearest:
*rounded = std::floor(toRound + 0.5);
*rounded_int = static_cast<int64_t>(*rounded); if ((*rounded_int & 1) != 0 && *rounded_int - toRound == 0.5) { // If the number is halfway between two integers, // round to the even one.
*rounded_int -= 1;
*rounded -= 1.;
} break; case kRoundToZero:
*rounded = trunc(toRound);
*rounded_int = static_cast<int64_t>(*rounded); break; case kRoundToPlusInf:
*rounded = std::ceil(toRound);
*rounded_int = static_cast<int64_t>(*rounded); break; case kRoundToMinusInf:
*rounded = std::floor(toRound);
*rounded_int = static_cast<int64_t>(*rounded); break;
}
}
// Raw access to the PC register. void Simulator::set_pc(int64_t value) {
pc_modified_ = true;
registers_[pc] = value;
}
int Simulator::loadLinkedW(uint64_t addr, SimInstruction* instr) { if ((addr & 3) == 0) { if (handleWasmSegFault(addr, 4)) { return -1;
}
volatile int32_t* ptr = reinterpret_cast<volatile int32_t*>(addr);
int32_t value = *ptr;
lastLLValue_ = value;
LLAddr_ = addr; // Note that any memory write or "external" interrupt should reset this // value to false.
LLBit_ = true; return value;
}
printf("Unaligned write at 0x%016" PRIx64 ", pc=0x%016" PRIxPTR "\n", addr, reinterpret_cast<intptr_t>(instr));
MOZ_CRASH(); return 0;
}
int Simulator::storeConditionalW(uint64_t addr, int value,
SimInstruction* instr) { // Correct behavior in this case, as defined by architecture, is to just // return 0, but there is no point at allowing that. It is certainly an // indicator of a bug. if (addr != LLAddr_) {
printf("SC to bad address: 0x%016" PRIx64 ", pc=0x%016" PRIx64 ", expected: 0x%016" PRIx64 "\n",
addr, reinterpret_cast<intptr_t>(instr), LLAddr_);
MOZ_CRASH();
}
int64_t Simulator::loadLinkedD(uint64_t addr, SimInstruction* instr) { if ((addr & kPointerAlignmentMask) == 0) { if (handleWasmSegFault(addr, 8)) { return -1;
}
volatile int64_t* ptr = reinterpret_cast<volatile int64_t*>(addr);
int64_t value = *ptr;
lastLLValue_ = value;
LLAddr_ = addr; // Note that any memory write or "external" interrupt should reset this // value to false.
LLBit_ = true; return value;
}
printf("Unaligned write at 0x%016" PRIx64 ", pc=0x%016" PRIxPTR "\n", addr, reinterpret_cast<intptr_t>(instr));
MOZ_CRASH(); return 0;
}
int Simulator::storeConditionalD(uint64_t addr, int64_t value,
SimInstruction* instr) { // Correct behavior in this case, as defined by architecture, is to just // return 0, but there is no point at allowing that. It is certainly an // indicator of a bug. if (addr != LLAddr_) {
printf("SC to bad address: 0x%016" PRIx64 ", pc=0x%016" PRIx64 ", expected: 0x%016" PRIx64 "\n",
addr, reinterpret_cast<intptr_t>(instr), LLAddr_);
MOZ_CRASH();
}
// Unsupported instructions use format to print an error and stop execution. void Simulator::format(SimInstruction* instr, constchar* format) {
printf("Simulator found unsupported instruction:\n 0x%016lx: %s\n", reinterpret_cast<intptr_t>(instr), format);
MOZ_CRASH();
}
// Software interrupt instructions are used by the simulator to call into C++. void Simulator::softwareInterrupt(SimInstruction* instr) { // the break_ instruction could get us here.
mozilla::DebugOnly<int32_t> opcode_hi15 = instr->bits(31, 17);
MOZ_ASSERT(opcode_hi15 == 0x15);
uint32_t code = instr->bits(14, 0);
// This is dodgy but it works because the C entry stubs are never moved. // See comment in codegen-arm.cc and bug 1242173.
int64_t saved_ra = getRegister(ra);
void Simulator::handleStop(uint32_t code, SimInstruction* instr) { // Stop if it is enabled, otherwise go on jumping over the stop // and the message address. if (isEnabledStop(code)) {
loong64Debugger dbg(this);
dbg.stop(instr);
} else {
set_pc(get_pc() + 1 * SimInstruction::kInstrSize);
}
}
template <typename T> staticbool FPUProcessNaNsAndZeros(T a, T b, MaxMinKind kind, T* result) { if (std::isnan(a) && std::isnan(b)) {
*result = a;
} elseif (std::isnan(a)) {
*result = b;
} elseif (std::isnan(b)) {
*result = a;
} elseif (b == a) { // Handle -0.0 == 0.0 case. // std::signbit() returns int 0 or 1 so subtracting MaxMinKind::kMax // negates the result.
*result = std::signbit(b) - static_cast<int>(kind) ? b : a;
} else { returnfalse;
} returntrue;
}
template <typename T> static T FPUMin(T a, T b) {
T result; if (FPUProcessNaNsAndZeros(a, b, MaxMinKind::kMin, &result)) { return result;
} else { return b < a ? b : a;
}
}
template <typename T> static T FPUMax(T a, T b) {
T result; if (FPUProcessNaNsAndZeros(a, b, MaxMinKind::kMax, &result)) { return result;
} else { return b > a ? b : a;
}
}
template <typename T> static T FPUMinA(T a, T b) {
T result; if (!FPUProcessNaNsAndZeros(a, b, MaxMinKind::kMin, &result)) { if (FPAbs(a) < FPAbs(b)) {
result = a;
} elseif (FPAbs(b) < FPAbs(a)) {
result = b;
} else {
result = a < b ? a : b;
}
} return result;
}
template <typename T> static T FPUMaxA(T a, T b) {
T result; if (!FPUProcessNaNsAndZeros(a, b, MaxMinKind::kMin, &result)) { if (FPAbs(a) > FPAbs(b)) {
result = a;
} elseif (FPAbs(b) > FPAbs(a)) {
result = b;
} else {
result = a > b ? a : b;
}
} return result;
}
enumclass KeepSign : bool { no = false, yes };
// Handle execution based on instruction types. // decodeTypeImmediate void Simulator::decodeTypeOp6(SimInstruction* instr) { // Next pc.
int64_t next_pc = bad_ra;
// Used for memory instructions.
int64_t alu_out = 0;
// Reverse the bit in byte for each individual byte for (int i = 0; i < 4; i++) {
output = output >> 8;
i_byte = input & 0xFF;
// Fast way to reverse bits in byte // Devised by Sean Anderson, July 13, 2001
o_byte = static_cast<uint8_t>(((i_byte * 0x0802LU & 0x22110LU) |
(i_byte * 0x8020LU & 0x88440LU)) *
0x10101LU >>
16);
// Reverse the bit in byte for each individual byte for (int i = 0; i < 8; i++) {
output = output >> 8;
i_byte = input & 0xFF;
// Fast way to reverse bits in byte // Devised by Sean Anderson, July 13, 2001
o_byte = static_cast<uint8_t>(((i_byte * 0x0802LU & 0x22110LU) |
(i_byte * 0x8020LU & 0x88440LU)) *
0x10101LU >>
16);
// Executes the current instruction. void Simulator::instructionDecode(SimInstruction* instr) { if (!SimulatorProcess::ICacheCheckingDisableCount) {
AutoLockSimulatorCache als;
SimulatorProcess::checkICacheLocked(instr);
}
pc_modified_ = false;
switch (instr->instructionType()) { case SimInstruction::kOp6Type:
decodeTypeOp6(instr); break; case SimInstruction::kOp7Type:
decodeTypeOp7(instr); break; case SimInstruction::kOp8Type:
decodeTypeOp8(instr); break; case SimInstruction::kOp10Type:
decodeTypeOp10(instr); break; case SimInstruction::kOp11Type:
decodeTypeOp11(instr); break; case SimInstruction::kOp12Type:
decodeTypeOp12(instr); break; case SimInstruction::kOp14Type:
decodeTypeOp14(instr); break; case SimInstruction::kOp15Type:
decodeTypeOp15(instr); break; case SimInstruction::kOp16Type:
decodeTypeOp16(instr); break; case SimInstruction::kOp17Type:
decodeTypeOp17(instr); break; case SimInstruction::kOp22Type:
decodeTypeOp22(instr); break; case SimInstruction::kOp24Type:
decodeTypeOp24(instr); break; default:
UNSUPPORTED();
} if (!pc_modified_) {
setRegister(pc, reinterpret_cast<int64_t>(instr) + SimInstruction::kInstrSize);
}
}
// Get the PC to simulate. Cannot use the accessor here as we need the // raw PC value and not the one used as input to arithmetic instructions.
int64_t program_counter = get_pc();
if (single_stepping_) {
single_step_callback_(single_step_callback_arg_, this, nullptr);
}
}
void Simulator::callInternal(uint8_t* entry) { // Prepare to execute the code at entry.
setRegister(pc, reinterpret_cast<int64_t>(entry)); // Put down marker for end of simulation. The simulator will stop simulation // when the PC reaches this value. By saving the "end simulation" value into // the LR the simulation stops when returning to this call point.
setRegister(ra, end_sim_pc);
// Remember the values of callee-saved registers. // The code below assumes that r9 is not used as sb (static base) in // simulator code and therefore is regarded as a callee-saved register.
int64_t s0_val = getRegister(s0);
int64_t s1_val = getRegister(s1);
int64_t s2_val = getRegister(s2);
int64_t s3_val = getRegister(s3);
int64_t s4_val = getRegister(s4);
int64_t s5_val = getRegister(s5);
int64_t s6_val = getRegister(s6);
int64_t s7_val = getRegister(s7);
int64_t s8_val = getRegister(s8);
int64_t gp_val = getRegister(gp);
int64_t sp_val = getRegister(sp);
int64_t tp_val = getRegister(tp);
int64_t fp_val = getRegister(fp);
// Set up the callee-saved registers with a known value. To be able to check // that they are preserved properly across JS execution.
int64_t callee_saved_value = icount_;
setRegister(s0, callee_saved_value);
setRegister(s1, callee_saved_value);
setRegister(s2, callee_saved_value);
setRegister(s3, callee_saved_value);
setRegister(s4, callee_saved_value);
setRegister(s5, callee_saved_value);
setRegister(s6, callee_saved_value);
setRegister(s7, callee_saved_value);
setRegister(s8, callee_saved_value);
setRegister(gp, callee_saved_value);
setRegister(tp, callee_saved_value);
setRegister(fp, callee_saved_value);
// Start the simulation. if (Simulator::StopSimAt != -1) {
execute<true>();
} else {
execute<false>();
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.