Files
sdk/runtime/vm/simulator_mips.cc
T
johnmccutchan@google.com 6f0b2bb2a9 Profiler tweaks
* Rename Script tag to Dart tag.
* Respect ignore bit on samples.
* Mark samples collected with an exit frame.
* Don't give exclusive CPU ticks to top frame on samples collected from an exit frame (because CPU usage is in the native code we've called out to not the function waiting to return).

R=asiva@google.com

Review URL: https://codereview.chromium.org//418433002

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@38522 260f80e4-7a28-3924-810f-c04153c831b5
2014-07-23 21:09:25 +00:00

2258 lines
71 KiB
C++

// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include <setjmp.h>
#include <stdlib.h>
#include "vm/globals.h"
#if defined(TARGET_ARCH_MIPS)
// Only build the simulator if not compiling for real MIPS hardware.
#if !defined(HOST_ARCH_MIPS)
#include "vm/simulator.h"
#include "vm/assembler.h"
#include "vm/constants_mips.h"
#include "vm/disassembler.h"
#include "vm/native_arguments.h"
#include "vm/thread.h"
namespace dart {
DEFINE_FLAG(bool, trace_sim, false, "Trace simulator execution.");
DEFINE_FLAG(int, stop_sim_at, 0, "Address to stop simulator at.");
// This macro provides a platform independent use of sscanf. The reason for
// SScanF not being implemented in a platform independent way through
// OS in the same way as SNPrint is that the Windows C Run-Time
// Library does not provide vsscanf.
#define SScanF sscanf // NOLINT
// SimulatorSetjmpBuffer are linked together, and the last created one
// is referenced by the Simulator. When an exception is thrown, the exception
// runtime looks at where to jump and finds the corresponding
// SimulatorSetjmpBuffer based on the stack pointer of the exception handler.
// The runtime then does a Longjmp on that buffer to return to the simulator.
class SimulatorSetjmpBuffer {
public:
int Setjmp() { return setjmp(buffer_); }
void Longjmp() {
// "This" is now the last setjmp buffer.
simulator_->set_last_setjmp_buffer(this);
longjmp(buffer_, 1);
}
explicit SimulatorSetjmpBuffer(Simulator* sim) {
simulator_ = sim;
link_ = sim->last_setjmp_buffer();
sim->set_last_setjmp_buffer(this);
sp_ = static_cast<uword>(sim->get_register(SP));
native_sp_ = reinterpret_cast<uword>(&sim); // Current C++ stack pointer.
}
~SimulatorSetjmpBuffer() {
ASSERT(simulator_->last_setjmp_buffer() == this);
simulator_->set_last_setjmp_buffer(link_);
}
SimulatorSetjmpBuffer* link() { return link_; }
uword sp() { return sp_; }
uword native_sp() { return native_sp_; }
private:
uword sp_;
uword native_sp_;
Simulator* simulator_;
SimulatorSetjmpBuffer* link_;
jmp_buf buffer_;
friend class Simulator;
};
// The SimulatorDebugger class is used by the simulator while debugging
// simulated MIPS code.
class SimulatorDebugger {
public:
explicit SimulatorDebugger(Simulator* sim);
~SimulatorDebugger();
void Stop(Instr* instr, const char* message);
void Debug();
char* ReadLine(const char* prompt);
private:
Simulator* sim_;
bool GetValue(char* desc, uint32_t* value);
bool GetFValue(char* desc, double* value);
bool GetDValue(char* desc, double* value);
// Set or delete a breakpoint. Returns true if successful.
bool SetBreakpoint(Instr* breakpc);
bool DeleteBreakpoint(Instr* 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();
};
SimulatorDebugger::SimulatorDebugger(Simulator* sim) {
sim_ = sim;
}
SimulatorDebugger::~SimulatorDebugger() {
}
void SimulatorDebugger::Stop(Instr* instr, const char* message) {
OS::Print("Simulator hit %s\n", message);
Debug();
}
static Register LookupCpuRegisterByName(const char* name) {
static const char* kNames[] = {
"r0", "r1", "r2", "r3",
"r4", "r5", "r6", "r7",
"r8", "r9", "r10", "r11",
"r12", "r13", "r14", "r15",
"r16", "r17", "r18", "r19",
"r20", "r21", "r22", "r23",
"r24", "r25", "r26", "r27",
"r28", "r29", "r30", "r31",
"zr", "at", "v0", "v1",
"a0", "a1", "a2", "a3",
"t0", "t1", "t2", "t3",
"t4", "t5", "t6", "t7",
"s0", "s1", "s2", "s3",
"s4", "s5", "s6", "s7",
"t8", "t9", "k0", "k1",
"gp", "sp", "fp", "ra"
};
static const Register kRegisters[] = {
R0, R1, R2, R3,
R4, R5, R6, R7,
R8, R9, R10, R11,
R12, R13, R14, R15,
R16, R17, R18, R19,
R20, R21, R22, R23,
R24, R25, R26, R27,
R28, R29, R30, R31,
ZR, AT, V0, V1,
A0, A1, A2, A3,
T0, T1, T2, T3,
T4, T5, T6, T7,
S0, S1, S2, S3,
S4, S5, S6, S7,
T8, T9, K0, K1,
GP, SP, FP, RA
};
ASSERT(ARRAY_SIZE(kNames) == ARRAY_SIZE(kRegisters));
for (unsigned i = 0; i < ARRAY_SIZE(kNames); i++) {
if (strcmp(kNames[i], name) == 0) {
return kRegisters[i];
}
}
return kNoRegister;
}
static FRegister LookupFRegisterByName(const char* name) {
int reg_nr = -1;
bool ok = SScanF(name, "f%d", &reg_nr);
if (ok && (0 <= reg_nr) && (reg_nr < kNumberOfFRegisters)) {
return static_cast<FRegister>(reg_nr);
}
return kNoFRegister;
}
bool SimulatorDebugger::GetValue(char* desc, uint32_t* value) {
Register reg = LookupCpuRegisterByName(desc);
if (reg != kNoRegister) {
*value = sim_->get_register(reg);
return true;
}
if (desc[0] == '*') {
uint32_t addr;
if (GetValue(desc + 1, &addr)) {
if (Simulator::IsIllegalAddress(addr)) {
return false;
}
*value = *(reinterpret_cast<uint32_t*>(addr));
return true;
}
}
if (strcmp("pc", desc) == 0) {
*value = sim_->get_pc();
return true;
}
bool retval = SScanF(desc, "0x%x", value) == 1;
if (!retval) {
retval = SScanF(desc, "%x", value) == 1;
}
return retval;
}
bool SimulatorDebugger::GetFValue(char* desc, double* value) {
FRegister freg = LookupFRegisterByName(desc);
if (freg != kNoFRegister) {
*value = sim_->get_fregister(freg);
return true;
}
if (desc[0] == '*') {
uint32_t addr;
if (GetValue(desc + 1, &addr)) {
if (Simulator::IsIllegalAddress(addr)) {
return false;
}
*value = *(reinterpret_cast<float*>(addr));
return true;
}
}
return false;
}
bool SimulatorDebugger::GetDValue(char* desc, double* value) {
FRegister freg = LookupFRegisterByName(desc);
if (freg != kNoFRegister) {
*value = sim_->get_fregister_double(freg);
return true;
}
if (desc[0] == '*') {
uint32_t addr;
if (GetValue(desc + 1, &addr)) {
if (Simulator::IsIllegalAddress(addr)) {
return false;
}
*value = *(reinterpret_cast<double*>(addr));
return true;
}
}
return false;
}
bool SimulatorDebugger::SetBreakpoint(Instr* breakpc) {
// Check if a breakpoint can be set. If not return without any side-effects.
if (sim_->break_pc_ != NULL) {
return false;
}
// Set the breakpoint.
sim_->break_pc_ = breakpc;
sim_->break_instr_ = breakpc->InstructionBits();
// Not setting the breakpoint instruction in the code itself. It will be set
// when the debugger shell continues.
return true;
}
bool SimulatorDebugger::DeleteBreakpoint(Instr* breakpc) {
if (sim_->break_pc_ != NULL) {
sim_->break_pc_->SetInstructionBits(sim_->break_instr_);
}
sim_->break_pc_ = NULL;
sim_->break_instr_ = 0;
return true;
}
void SimulatorDebugger::UndoBreakpoints() {
if (sim_->break_pc_ != NULL) {
sim_->break_pc_->SetInstructionBits(sim_->break_instr_);
}
}
void SimulatorDebugger::RedoBreakpoints() {
if (sim_->break_pc_ != NULL) {
sim_->break_pc_->SetInstructionBits(Instr::kBreakPointInstruction);
}
}
void SimulatorDebugger::Debug() {
intptr_t last_pc = -1;
bool done = false;
#define COMMAND_SIZE 63
#define ARG_SIZE 255
#define STR(a) #a
#define XSTR(a) STR(a)
char cmd[COMMAND_SIZE + 1];
char arg1[ARG_SIZE + 1];
char arg2[ARG_SIZE + 1];
// make sure to have a proper terminating character if reaching the limit
cmd[COMMAND_SIZE] = 0;
arg1[ARG_SIZE] = 0;
arg2[ARG_SIZE] = 0;
// Undo all set breakpoints while running in the debugger shell. This will
// make them invisible to all commands.
UndoBreakpoints();
while (!done) {
if (last_pc != sim_->get_pc()) {
last_pc = sim_->get_pc();
if (Simulator::IsIllegalAddress(last_pc)) {
OS::Print("pc is out of bounds: 0x%" Px "\n", last_pc);
} else {
Disassembler::Disassemble(last_pc, last_pc + Instr::kInstrSize);
}
}
char* line = ReadLine("sim> ");
if (line == NULL) {
FATAL("ReadLine failed");
} else {
// Use sscanf to parse the individual parts of the command line. At the
// moment no command expects more than two parameters.
int args = SScanF(line,
"%" XSTR(COMMAND_SIZE) "s "
"%" XSTR(ARG_SIZE) "s "
"%" XSTR(ARG_SIZE) "s",
cmd, arg1, arg2);
if ((strcmp(cmd, "h") == 0) || (strcmp(cmd, "help") == 0)) {
OS::Print("c/cont -- continue execution\n"
"disasm -- disassemble instrs at current pc location\n"
" other variants are:\n"
" disasm <address>\n"
" disasm <address> <number_of_instructions>\n"
" by default 10 instrs are disassembled\n"
"del -- delete breakpoints\n"
"gdb -- transfer control to gdb\n"
"h/help -- print this help string\n"
"break <address> -- set break point at specified address\n"
"p/print <reg or value or *addr> -- print integer value\n"
"pf/printfloat <freg or *addr> -- print float value\n"
"po/printobject <*reg or *addr> -- print object\n"
"si/stepi -- single step an instruction\n"
"unstop -- if current pc is a stop instr make it a nop\n"
"q/quit -- Quit the debugger and exit the program\n");
} else if ((strcmp(cmd, "quit") == 0) || (strcmp(cmd, "q") == 0)) {
OS::Print("Quitting\n");
OS::Exit(0);
} else if ((strcmp(cmd, "si") == 0) || (strcmp(cmd, "stepi") == 0)) {
sim_->InstructionDecode(reinterpret_cast<Instr*>(sim_->get_pc()));
} else if ((strcmp(cmd, "c") == 0) || (strcmp(cmd, "cont") == 0)) {
// Execute the one instruction we broke at with breakpoints disabled.
sim_->InstructionDecode(reinterpret_cast<Instr*>(sim_->get_pc()));
// Leave the debugger shell.
done = true;
} else if ((strcmp(cmd, "p") == 0) || (strcmp(cmd, "print") == 0)) {
if (args == 2) {
uint32_t value;
if (GetValue(arg1, &value)) {
OS::Print("%s: %u 0x%x\n", arg1, value, value);
} else {
OS::Print("%s unrecognized\n", arg1);
}
} else {
OS::Print("print <reg or value or *addr>\n");
}
} else if ((strcmp(cmd, "pf") == 0) ||
(strcmp(cmd, "printfloat") == 0)) {
if (args == 2) {
double dvalue;
if (GetFValue(arg1, &dvalue)) {
uint64_t long_value = bit_cast<uint64_t, double>(dvalue);
OS::Print("%s: %llu 0x%llx %.8g\n",
arg1, long_value, long_value, dvalue);
} else {
OS::Print("%s unrecognized\n", arg1);
}
} else {
OS::Print("printfloat <dreg or *addr>\n");
}
} else if ((strcmp(cmd, "pd") == 0) ||
(strcmp(cmd, "printdouble") == 0)) {
if (args == 2) {
double dvalue;
if (GetDValue(arg1, &dvalue)) {
uint64_t long_value = bit_cast<uint64_t, double>(dvalue);
OS::Print("%s: %llu 0x%llx %.8g\n",
arg1, long_value, long_value, dvalue);
} else {
OS::Print("%s unrecognized\n", arg1);
}
} else {
OS::Print("printfloat <dreg or *addr>\n");
}
} else if ((strcmp(cmd, "po") == 0) ||
(strcmp(cmd, "printobject") == 0)) {
if (args == 2) {
uint32_t value;
// Make the dereferencing '*' optional.
if (((arg1[0] == '*') && GetValue(arg1 + 1, &value)) ||
GetValue(arg1, &value)) {
if (Isolate::Current()->heap()->Contains(value)) {
OS::Print("%s: \n", arg1);
#if defined(DEBUG)
const Object& obj = Object::Handle(
reinterpret_cast<RawObject*>(value));
obj.Print();
#endif // defined(DEBUG)
} else {
OS::Print("0x%x is not an object reference\n", value);
}
} else {
OS::Print("%s unrecognized\n", arg1);
}
} else {
OS::Print("printobject <*reg or *addr>\n");
}
} else if (strcmp(cmd, "disasm") == 0) {
uint32_t start = 0;
uint32_t end = 0;
if (args == 1) {
start = sim_->get_pc();
end = start + (10 * Instr::kInstrSize);
} else if (args == 2) {
if (GetValue(arg1, &start)) {
// no length parameter passed, assume 10 instructions
if (Simulator::IsIllegalAddress(start)) {
// If start isn't a valid address, warn and use PC instead
OS::Print("First argument yields invalid address: 0x%x\n", start);
OS::Print("Using PC instead");
start = sim_->get_pc();
}
end = start + (10 * Instr::kInstrSize);
}
} else {
uint32_t length;
if (GetValue(arg1, &start) && GetValue(arg2, &length)) {
if (Simulator::IsIllegalAddress(start)) {
// If start isn't a valid address, warn and use PC instead
OS::Print("First argument yields invalid address: 0x%x\n", start);
OS::Print("Using PC instead\n");
start = sim_->get_pc();
}
end = start + (length * Instr::kInstrSize);
}
}
Disassembler::Disassemble(start, end);
} else if (strcmp(cmd, "gdb") == 0) {
OS::Print("relinquishing control to gdb\n");
OS::DebugBreak();
OS::Print("regaining control from gdb\n");
} else if (strcmp(cmd, "break") == 0) {
if (args == 2) {
uint32_t addr;
if (GetValue(arg1, &addr)) {
if (!SetBreakpoint(reinterpret_cast<Instr*>(addr))) {
OS::Print("setting breakpoint failed\n");
}
} else {
OS::Print("%s unrecognized\n", arg1);
}
} else {
OS::Print("break <addr>\n");
}
} else if (strcmp(cmd, "del") == 0) {
if (!DeleteBreakpoint(NULL)) {
OS::Print("deleting breakpoint failed\n");
}
} else if (strcmp(cmd, "unstop") == 0) {
intptr_t stop_pc = sim_->get_pc() - Instr::kInstrSize;
Instr* stop_instr = reinterpret_cast<Instr*>(stop_pc);
if (stop_instr->IsBreakPoint()) {
stop_instr->SetInstructionBits(Instr::kNopInstruction);
} else {
OS::Print("Not at debugger stop.\n");
}
} else {
OS::Print("Unknown command: %s\n", cmd);
}
}
delete[] line;
}
// Add all the breakpoints back to stop execution and enter the debugger
// shell when hit.
RedoBreakpoints();
#undef COMMAND_SIZE
#undef ARG_SIZE
#undef STR
#undef XSTR
}
char* SimulatorDebugger::ReadLine(const char* prompt) {
char* result = NULL;
char line_buf[256];
intptr_t offset = 0;
bool keep_going = true;
OS::Print("%s", prompt);
while (keep_going) {
if (fgets(line_buf, sizeof(line_buf), stdin) == NULL) {
// fgets got an error. Just give up.
if (result != NULL) {
delete[] result;
}
return NULL;
}
intptr_t len = strlen(line_buf);
if (len > 1 &&
line_buf[len - 2] == '\\' &&
line_buf[len - 1] == '\n') {
// When we read a line that ends with a "\" we remove the escape and
// append the remainder.
line_buf[len - 2] = '\n';
line_buf[len - 1] = 0;
len -= 1;
} else if ((len > 0) && (line_buf[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.
keep_going = false;
}
if (result == NULL) {
// Allocate the initial result and make room for the terminating '\0'
result = new char[len + 1];
if (result == NULL) {
// OOM, so cannot readline anymore.
return NULL;
}
} else {
// Allocate a new result with enough room for the new addition.
intptr_t new_len = offset + len + 1;
char* new_result = new char[new_len];
if (new_result == NULL) {
// OOM, free the buffer allocated so far and return NULL.
delete[] result;
return NULL;
} else {
// Copy the existing input into the new array and set the new
// array as the result.
memmove(new_result, result, offset);
delete[] result;
result = new_result;
}
}
// Copy the newly read line into the result.
memmove(result + offset, line_buf, len);
offset += len;
}
ASSERT(result != NULL);
result[offset] = '\0';
return result;
}
void Simulator::InitOnce() {
}
Simulator::Simulator() {
// Setup simulator support first. Some of this information is needed to
// setup the architecture state.
// We allocate the stack here, the size is computed as the sum of
// the size specified by the user and the buffer space needed for
// handling stack overflow exceptions. To be safe in potential
// stack underflows we also add some underflow buffer space.
stack_ = new char[(Isolate::GetSpecifiedStackSize() +
Isolate::kStackSizeBuffer +
kSimulatorStackUnderflowSize)];
icount_ = 0;
delay_slot_ = false;
break_pc_ = NULL;
break_instr_ = 0;
last_setjmp_buffer_ = NULL;
top_exit_frame_info_ = 0;
// Setup architecture state.
// All registers are initialized to zero to start with.
for (int i = 0; i < kNumberOfCpuRegisters; i++) {
registers_[i] = 0;
}
pc_ = 0;
// The sp is initialized to point to the bottom (high address) of the
// allocated stack area.
registers_[SP] = StackTop();
// All double-precision registers are initialized to zero.
for (int i = 0; i < kNumberOfFRegisters; i++) {
fregisters_[i] = 0.0;
}
fcsr_ = 0;
}
Simulator::~Simulator() {
delete[] stack_;
Isolate* isolate = Isolate::Current();
if (isolate != NULL) {
isolate->set_simulator(NULL);
}
}
// 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 break instruction with code 2 that is handled by
// the simulator. We write the original destination of the jump just at a known
// offset from the break instruction so the simulator knows what to call.
class Redirection {
public:
uword address_of_break_instruction() {
return reinterpret_cast<uword>(&break_instruction_);
}
uword external_function() const { return external_function_; }
Simulator::CallKind call_kind() const { return call_kind_; }
int argument_count() const { return argument_count_; }
static Redirection* Get(uword external_function,
Simulator::CallKind call_kind,
int argument_count) {
Redirection* current;
for (current = list_; current != NULL; current = current->next_) {
if (current->external_function_ == external_function) return current;
}
return new Redirection(external_function, call_kind, argument_count);
}
static Redirection* FromBreakInstruction(Instr* break_instruction) {
char* addr_of_break = reinterpret_cast<char*>(break_instruction);
char* addr_of_redirection =
addr_of_break - OFFSET_OF(Redirection, break_instruction_);
return reinterpret_cast<Redirection*>(addr_of_redirection);
}
private:
static const int32_t kRedirectInstruction =
Instr::kBreakPointInstruction | (Instr::kRedirectCode << kBreakCodeShift);
Redirection(uword external_function,
Simulator::CallKind call_kind,
int argument_count)
: external_function_(external_function),
call_kind_(call_kind),
argument_count_(argument_count),
break_instruction_(kRedirectInstruction),
next_(list_) {
list_ = this;
}
uword external_function_;
Simulator::CallKind call_kind_;
int argument_count_;
uint32_t break_instruction_;
Redirection* next_;
static Redirection* list_;
};
Redirection* Redirection::list_ = NULL;
uword Simulator::RedirectExternalReference(uword function,
CallKind call_kind,
int argument_count) {
Redirection* redirection =
Redirection::Get(function, call_kind, argument_count);
return redirection->address_of_break_instruction();
}
// Get the active Simulator for the current isolate.
Simulator* Simulator::Current() {
Simulator* simulator = Isolate::Current()->simulator();
if (simulator == NULL) {
simulator = new Simulator();
Isolate::Current()->set_simulator(simulator);
}
return simulator;
}
// Sets the register in the architecture state.
void Simulator::set_register(Register reg, int32_t value) {
if (reg != R0) {
registers_[reg] = value;
}
}
void Simulator::set_fregister(FRegister reg, int32_t value) {
ASSERT(reg >= 0);
ASSERT(reg < kNumberOfFRegisters);
fregisters_[reg] = value;
}
void Simulator::set_fregister_float(FRegister reg, float value) {
ASSERT(reg >= 0);
ASSERT(reg < kNumberOfFRegisters);
fregisters_[reg] = bit_cast<int32_t, float>(value);
}
void Simulator::set_fregister_long(FRegister reg, int64_t value) {
ASSERT(reg >= 0);
ASSERT(reg < kNumberOfFRegisters);
ASSERT((reg & 1) == 0);
fregisters_[reg] = Utils::Low32Bits(value);
fregisters_[reg + 1] = Utils::High32Bits(value);
}
void Simulator::set_fregister_double(FRegister reg, double value) {
const int64_t ival = bit_cast<int64_t, double>(value);
set_fregister_long(reg, ival);
}
void Simulator::set_dregister_bits(DRegister reg, int64_t value) {
ASSERT(reg >= 0);
ASSERT(reg < kNumberOfDRegisters);
FRegister lo = static_cast<FRegister>(reg * 2);
FRegister hi = static_cast<FRegister>((reg * 2) + 1);
set_fregister(lo, Utils::Low32Bits(value));
set_fregister(hi, Utils::High32Bits(value));
}
void Simulator::set_dregister(DRegister reg, double value) {
ASSERT(reg >= 0);
ASSERT(reg < kNumberOfDRegisters);
set_dregister_bits(reg, bit_cast<int64_t, double>(value));
}
// Get the register from the architecture state.
int32_t Simulator::get_register(Register reg) const {
if (reg == R0) {
return 0;
}
return registers_[reg];
}
int32_t Simulator::get_fregister(FRegister reg) const {
ASSERT((reg >= 0) && (reg < kNumberOfFRegisters));
return fregisters_[reg];
}
float Simulator::get_fregister_float(FRegister reg) const {
ASSERT(reg >= 0);
ASSERT(reg < kNumberOfFRegisters);
return bit_cast<float, int32_t>(fregisters_[reg]);
}
int64_t Simulator::get_fregister_long(FRegister reg) const {
ASSERT(reg >= 0);
ASSERT(reg < kNumberOfFRegisters);
ASSERT((reg & 1) == 0);
const int32_t low = fregisters_[reg];
const int32_t high = fregisters_[reg + 1];
const int64_t value = Utils::LowHighTo64Bits(low, high);
return value;
}
double Simulator::get_fregister_double(FRegister reg) const {
ASSERT(reg >= 0);
ASSERT(reg < kNumberOfFRegisters);
ASSERT((reg & 1) == 0);
const int64_t value = get_fregister_long(reg);
return bit_cast<double, int64_t>(value);
}
int64_t Simulator::get_dregister_bits(DRegister reg) const {
ASSERT(reg >= 0);
ASSERT(reg < kNumberOfDRegisters);
FRegister lo = static_cast<FRegister>(reg * 2);
FRegister hi = static_cast<FRegister>((reg * 2) + 1);
return Utils::LowHighTo64Bits(get_fregister(lo), get_fregister(hi));
}
double Simulator::get_dregister(DRegister reg) const {
ASSERT(reg >= 0);
ASSERT(reg < kNumberOfDRegisters);
const int64_t value = get_dregister_bits(reg);
return bit_cast<double, int64_t>(value);
}
void Simulator::UnimplementedInstruction(Instr* instr) {
char buffer[64];
snprintf(buffer, sizeof(buffer), "Unimplemented instruction: pc=%p\n", instr);
SimulatorDebugger dbg(this);
dbg.Stop(instr, buffer);
FATAL("Cannot continue execution after unimplemented instruction.");
}
void Simulator::HandleIllegalAccess(uword addr, Instr* instr) {
uword fault_pc = get_pc();
// The debugger will not be able to single step past this instruction, but
// it will be possible to disassemble the code and inspect registers.
char buffer[128];
snprintf(buffer, sizeof(buffer),
"illegal memory access at 0x%" Px ", pc=0x%" Px "\n",
addr, fault_pc);
SimulatorDebugger dbg(this);
dbg.Stop(instr, buffer);
// The debugger will return control in non-interactive mode.
FATAL("Cannot continue execution after illegal memory access.");
}
void Simulator::UnalignedAccess(const char* msg, uword addr, Instr* instr) {
// The debugger will not be able to single step past this instruction, but
// it will be possible to disassemble the code and inspect registers.
char buffer[128];
snprintf(buffer, sizeof(buffer),
"pc=%p, unaligned %s at 0x%" Px "\n", instr, msg, addr);
SimulatorDebugger dbg(this);
dbg.Stop(instr, buffer);
// The debugger will return control in non-interactive mode.
FATAL("Cannot continue execution after unaligned access.");
}
// Returns the top of the stack area to enable checking for stack pointer
// validity.
uword Simulator::StackTop() const {
// To be safe in potential stack underflows we leave some buffer above and
// set the stack top.
return reinterpret_cast<uword>(stack_) +
(Isolate::GetSpecifiedStackSize() + Isolate::kStackSizeBuffer);
}
void Simulator::Format(Instr* instr, const char* format) {
OS::PrintErr("Simulator - unknown instruction: %s\n", format);
UNIMPLEMENTED();
}
int8_t Simulator::ReadB(uword addr) {
int8_t* ptr = reinterpret_cast<int8_t*>(addr);
return *ptr;
}
uint8_t Simulator::ReadBU(uword addr) {
uint8_t* ptr = reinterpret_cast<uint8_t*>(addr);
return *ptr;
}
int16_t Simulator::ReadH(uword addr, Instr* instr) {
if ((addr & 1) == 0) {
int16_t* ptr = reinterpret_cast<int16_t*>(addr);
return *ptr;
}
UnalignedAccess("signed halfword read", addr, instr);
return 0;
}
uint16_t Simulator::ReadHU(uword addr, Instr* instr) {
if ((addr & 1) == 0) {
uint16_t* ptr = reinterpret_cast<uint16_t*>(addr);
return *ptr;
}
UnalignedAccess("unsigned halfword read", addr, instr);
return 0;
}
intptr_t Simulator::ReadW(uword addr, Instr* instr) {
if ((addr & 3) == 0) {
intptr_t* ptr = reinterpret_cast<intptr_t*>(addr);
return *ptr;
}
UnalignedAccess("read", addr, instr);
return 0;
}
void Simulator::WriteB(uword addr, uint8_t value) {
uint8_t* ptr = reinterpret_cast<uint8_t*>(addr);
*ptr = value;
}
void Simulator::WriteH(uword addr, uint16_t value, Instr* instr) {
if ((addr & 1) == 0) {
uint16_t* ptr = reinterpret_cast<uint16_t*>(addr);
*ptr = value;
return;
}
UnalignedAccess("halfword write", addr, instr);
}
void Simulator::WriteW(uword addr, intptr_t value, Instr* instr) {
if ((addr & 3) == 0) {
intptr_t* ptr = reinterpret_cast<intptr_t*>(addr);
*ptr = value;
return;
}
UnalignedAccess("write", addr, instr);
}
double Simulator::ReadD(uword addr, Instr* instr) {
if ((addr & 7) == 0) {
double* ptr = reinterpret_cast<double*>(addr);
return *ptr;
}
UnalignedAccess("double-precision floating point read", addr, instr);
return 0.0;
}
void Simulator::WriteD(uword addr, double value, Instr* instr) {
if ((addr & 7) == 0) {
double* ptr = reinterpret_cast<double*>(addr);
*ptr = value;
return;
}
UnalignedAccess("double-precision floating point write", addr, instr);
}
bool Simulator::OverflowFrom(int32_t alu_out,
int32_t left, int32_t right, bool addition) {
bool overflow;
if (addition) {
// Operands have the same sign.
overflow = ((left >= 0 && right >= 0) || (left < 0 && right < 0))
// And operands and result have different sign.
&& ((left < 0 && alu_out >= 0) || (left >= 0 && alu_out < 0));
} else {
// Operands have different signs.
overflow = ((left < 0 && right >= 0) || (left >= 0 && right < 0))
// And first operand and result have different signs.
&& ((left < 0 && alu_out >= 0) || (left >= 0 && alu_out < 0));
}
return overflow;
}
// Calls into the Dart runtime are based on this interface.
typedef void (*SimulatorRuntimeCall)(NativeArguments arguments);
// Calls to leaf Dart runtime functions are based on this interface.
typedef int32_t (*SimulatorLeafRuntimeCall)(
int32_t r0, int32_t r1, int32_t r2, int32_t r3);
// Calls to leaf float Dart runtime functions are based on this interface.
typedef double (*SimulatorLeafFloatRuntimeCall)(double d0, double d1);
// Calls to native Dart functions are based on this interface.
typedef void (*SimulatorBootstrapNativeCall)(NativeArguments* arguments);
typedef void (*SimulatorNativeCall)(NativeArguments* arguments, uword target);
void Simulator::DoBreak(Instr *instr) {
ASSERT(instr->OpcodeField() == SPECIAL);
ASSERT(instr->FunctionField() == BREAK);
if (instr->BreakCodeField() == Instr::kStopMessageCode) {
SimulatorDebugger dbg(this);
const char* message = *reinterpret_cast<const char**>(
reinterpret_cast<intptr_t>(instr) - Instr::kInstrSize);
set_pc(get_pc() + Instr::kInstrSize);
dbg.Stop(instr, message);
// Adjust for extra pc increment.
set_pc(get_pc() - Instr::kInstrSize);
} else if (instr->BreakCodeField() == Instr::kMsgMessageCode) {
const char* message = *reinterpret_cast<const char**>(
reinterpret_cast<intptr_t>(instr) - Instr::kInstrSize);
if (FLAG_trace_sim) {
OS::Print("Message: %s\n", message);
} else {
OS::PrintErr("Bad break code: 0x%x\n", instr->InstructionBits());
UnimplementedInstruction(instr);
}
} else if (instr->BreakCodeField() == Instr::kRedirectCode) {
SimulatorSetjmpBuffer buffer(this);
if (!setjmp(buffer.buffer_)) {
int32_t saved_ra = get_register(RA);
Redirection* redirection = Redirection::FromBreakInstruction(instr);
uword external = redirection->external_function();
if (FLAG_trace_sim) {
OS::Print("Call to host function at 0x%" Pd "\n", external);
}
if ((redirection->call_kind() == kRuntimeCall) ||
(redirection->call_kind() == kBootstrapNativeCall) ||
(redirection->call_kind() == kNativeCall)) {
// Set the top_exit_frame_info of this simulator to the native stack.
set_top_exit_frame_info(reinterpret_cast<uword>(&buffer));
}
if (redirection->call_kind() == kRuntimeCall) {
NativeArguments arguments;
ASSERT(sizeof(NativeArguments) == 4*kWordSize);
arguments.isolate_ = reinterpret_cast<Isolate*>(get_register(A0));
arguments.argc_tag_ = get_register(A1);
arguments.argv_ = reinterpret_cast<RawObject*(*)[]>(get_register(A2));
arguments.retval_ = reinterpret_cast<RawObject**>(get_register(A3));
SimulatorRuntimeCall target =
reinterpret_cast<SimulatorRuntimeCall>(external);
target(arguments);
set_register(V0, icount_); // Zap result registers from void function.
set_register(V1, icount_);
} else if (redirection->call_kind() == kLeafRuntimeCall) {
int32_t a0 = get_register(A0);
int32_t a1 = get_register(A1);
int32_t a2 = get_register(A2);
int32_t a3 = get_register(A3);
SimulatorLeafRuntimeCall target =
reinterpret_cast<SimulatorLeafRuntimeCall>(external);
a0 = target(a0, a1, a2, a3);
set_register(V0, a0); // Set returned result from function.
set_register(V1, icount_); // Zap second result register.
} else if (redirection->call_kind() == kLeafFloatRuntimeCall) {
ASSERT((0 <= redirection->argument_count()) &&
(redirection->argument_count() <= 2));
// double values are passed and returned in floating point registers.
SimulatorLeafFloatRuntimeCall target =
reinterpret_cast<SimulatorLeafFloatRuntimeCall>(external);
double d0 = 0.0;
double d6 = get_fregister_double(F12);
double d7 = get_fregister_double(F14);
d0 = target(d6, d7);
set_fregister_double(F0, d0);
} else if (redirection->call_kind() == kBootstrapNativeCall) {
NativeArguments* arguments;
arguments = reinterpret_cast<NativeArguments*>(get_register(A0));
SimulatorBootstrapNativeCall target =
reinterpret_cast<SimulatorBootstrapNativeCall>(external);
target(arguments);
set_register(V0, icount_); // Zap result register from void function.
set_register(V1, icount_);
} else {
ASSERT(redirection->call_kind() == kNativeCall);
NativeArguments* arguments;
arguments = reinterpret_cast<NativeArguments*>(get_register(A0));
uword target_func = get_register(A1);
SimulatorNativeCall target =
reinterpret_cast<SimulatorNativeCall>(external);
target(arguments, target_func);
set_register(V0, icount_); // Zap result register from void function.
set_register(V1, icount_);
}
set_top_exit_frame_info(0);
// Zap caller-saved registers, since the actual runtime call could have
// used them.
set_register(T0, icount_);
set_register(T1, icount_);
set_register(T2, icount_);
set_register(T3, icount_);
set_register(T4, icount_);
set_register(T5, icount_);
set_register(T6, icount_);
set_register(T7, icount_);
set_register(T8, icount_);
set_register(T9, icount_);
set_register(A0, icount_);
set_register(A1, icount_);
set_register(A2, icount_);
set_register(A3, icount_);
set_register(TMP, icount_);
set_register(RA, icount_);
// Zap floating point registers.
int32_t zap_dvalue = icount_;
for (int i = F4; i <= F18; i++) {
set_fregister(static_cast<FRegister>(i), zap_dvalue);
}
// Return. Subtract to account for pc_ increment after return.
set_pc(saved_ra - Instr::kInstrSize);
} else {
// Coming via long jump from a throw. Continue to exception handler.
set_top_exit_frame_info(0);
// Adjust for extra pc increment.
set_pc(get_pc() - Instr::kInstrSize);
}
} else {
SimulatorDebugger dbg(this);
dbg.Stop(instr, "breakpoint");
// Adjust for extra pc increment.
set_pc(get_pc() - Instr::kInstrSize);
}
}
void Simulator::DecodeSpecial(Instr* instr) {
ASSERT(instr->OpcodeField() == SPECIAL);
switch (instr->FunctionField()) {
case ADDU: {
ASSERT(instr->SaField() == 0);
// Format(instr, "addu 'rd, 'rs, 'rt");
int32_t rs_val = get_register(instr->RsField());
int32_t rt_val = get_register(instr->RtField());
set_register(instr->RdField(), rs_val + rt_val);
break;
}
case AND: {
ASSERT(instr->SaField() == 0);
// Format(instr, "and 'rd, 'rs, 'rt");
int32_t rs_val = get_register(instr->RsField());
int32_t rt_val = get_register(instr->RtField());
set_register(instr->RdField(), rs_val & rt_val);
break;
}
case BREAK: {
DoBreak(instr);
break;
}
case DIV: {
ASSERT(instr->RdField() == 0);
ASSERT(instr->SaField() == 0);
// Format(instr, "div 'rs, 'rt");
int32_t rs_val = get_register(instr->RsField());
int32_t rt_val = get_register(instr->RtField());
if (rt_val == 0) {
// Results are unpredictable, but there is no arithmetic exception.
set_hi_register(icount_);
set_lo_register(icount_);
break;
}
if ((rs_val == static_cast<int32_t>(0x80000000)) &&
(rt_val == static_cast<int32_t>(0xffffffff))) {
set_lo_register(0x80000000);
set_hi_register(0);
} else {
set_lo_register(rs_val / rt_val);
set_hi_register(rs_val % rt_val);
}
break;
}
case DIVU: {
ASSERT(instr->RdField() == 0);
ASSERT(instr->SaField() == 0);
// Format(instr, "divu 'rs, 'rt");
uint32_t rs_val = get_register(instr->RsField());
uint32_t rt_val = get_register(instr->RtField());
if (rt_val == 0) {
// Results are unpredictable, but there is no arithmetic exception.
set_hi_register(icount_);
set_lo_register(icount_);
break;
}
set_lo_register(rs_val / rt_val);
set_hi_register(rs_val % rt_val);
break;
}
case JALR: {
ASSERT(instr->RtField() == R0);
ASSERT(instr->RsField() != instr->RdField());
ASSERT(!delay_slot_);
// Format(instr, "jalr'hint 'rd, rs");
set_register(instr->RdField(), pc_ + 2*Instr::kInstrSize);
uword next_pc = get_register(instr->RsField());
ExecuteDelaySlot();
// Set return address to be the instruction after the delay slot.
pc_ = next_pc - Instr::kInstrSize; // Account for regular PC increment.
break;
}
case JR: {
ASSERT(instr->RtField() == R0);
ASSERT(instr->RdField() == R0);
ASSERT(!delay_slot_);
// Format(instr, "jr'hint 'rs");
uword next_pc = get_register(instr->RsField());
ExecuteDelaySlot();
pc_ = next_pc - Instr::kInstrSize; // Account for regular PC increment.
break;
}
case MFHI: {
ASSERT(instr->RsField() == 0);
ASSERT(instr->RtField() == 0);
ASSERT(instr->SaField() == 0);
// Format(instr, "mfhi 'rd");
set_register(instr->RdField(), get_hi_register());
break;
}
case MFLO: {
ASSERT(instr->RsField() == 0);
ASSERT(instr->RtField() == 0);
ASSERT(instr->SaField() == 0);
// Format(instr, "mflo 'rd");
set_register(instr->RdField(), get_lo_register());
break;
}
case MOVCI: {
ASSERT(instr->SaField() == 0);
ASSERT(instr->Bit(17) == 0);
int32_t rs_val = get_register(instr->RsField());
uint32_t cc, fcsr_cc, test, status;
cc = instr->Bits(18, 3);
fcsr_cc = get_fcsr_condition_bit(cc);
test = instr->Bit(16);
status = test_fcsr_bit(fcsr_cc);
if (test == status) {
set_register(instr->RdField(), rs_val);
}
break;
}
case MOVN: {
ASSERT(instr->SaField() == 0);
// Format(instr, "movn 'rd, 'rs, 'rt");
int32_t rt_val = get_register(instr->RtField());
int32_t rs_val = get_register(instr->RsField());
if (rt_val != 0) {
set_register(instr->RdField(), rs_val);
}
break;
}
case MOVZ: {
ASSERT(instr->SaField() == 0);
// Format(instr, "movz 'rd, 'rs, 'rt");
int32_t rt_val = get_register(instr->RtField());
int32_t rs_val = get_register(instr->RsField());
if (rt_val == 0) {
set_register(instr->RdField(), rs_val);
}
break;
}
case MTHI: {
ASSERT(instr->RtField() == 0);
ASSERT(instr->RdField() == 0);
ASSERT(instr->SaField() == 0);
// Format(instr, "mthi 'rd");
set_hi_register(get_register(instr->RsField()));
break;
}
case MTLO: {
ASSERT(instr->RtField() == 0);
ASSERT(instr->RdField() == 0);
ASSERT(instr->SaField() == 0);
// Format(instr, "mflo 'rd");
set_lo_register(get_register(instr->RsField()));
break;
}
case MULT: {
ASSERT(instr->RdField() == 0);
ASSERT(instr->SaField() == 0);
// Format(instr, "mult 'rs, 'rt");
int64_t rs = static_cast<int64_t>(get_register(instr->RsField()));
int64_t rt = static_cast<int64_t>(get_register(instr->RtField()));
int64_t res = rs * rt;
set_hi_register(Utils::High32Bits(res));
set_lo_register(Utils::Low32Bits(res));
break;
}
case MULTU: {
ASSERT(instr->RdField() == 0);
ASSERT(instr->SaField() == 0);
// Format(instr, "multu 'rs, 'rt");
uint64_t rs = static_cast<uint64_t>(get_register(instr->RsField()));
uint64_t rt = static_cast<uint64_t>(get_register(instr->RtField()));
uint64_t res = rs * rt;
set_hi_register(Utils::High32Bits(res));
set_lo_register(Utils::Low32Bits(res));
break;
}
case NOR: {
ASSERT(instr->SaField() == 0);
// Format(instr, "nor 'rd, 'rs, 'rt");
int32_t rs_val = get_register(instr->RsField());
int32_t rt_val = get_register(instr->RtField());
set_register(instr->RdField(), ~(rs_val | rt_val));
break;
}
case OR: {
ASSERT(instr->SaField() == 0);
// Format(instr, "or 'rd, 'rs, 'rt");
int32_t rs_val = get_register(instr->RsField());
int32_t rt_val = get_register(instr->RtField());
set_register(instr->RdField(), rs_val | rt_val);
break;
}
case SLL: {
ASSERT(instr->RsField() == 0);
if ((instr->RdField() == R0) &&
(instr->RtField() == R0) &&
(instr->SaField() == 0)) {
// Format(instr, "nop");
// Nothing to be done for NOP.
} else {
int32_t rt_val = get_register(instr->RtField());
int sa = instr->SaField();
set_register(instr->RdField(), rt_val << sa);
}
break;
}
case SLLV: {
ASSERT(instr->SaField() == 0);
// Format(instr, "sllv 'rd, 'rt, 'rs");
int32_t rt_val = get_register(instr->RtField());
int32_t rs_val = get_register(instr->RsField());
set_register(instr->RdField(), rt_val << (rs_val & 0x1f));
break;
}
case SLT: {
ASSERT(instr->SaField() == 0);
// Format(instr, "slt 'rd, 'rs, 'rt");
int32_t rs_val = get_register(instr->RsField());
int32_t rt_val = get_register(instr->RtField());
set_register(instr->RdField(), rs_val < rt_val ? 1 : 0);
break;
}
case SLTU: {
ASSERT(instr->SaField() == 0);
// Format(instr, "sltu 'rd, 'rs, 'rt");
uint32_t rs_val = static_cast<uint32_t>(get_register(instr->RsField()));
uint32_t rt_val = static_cast<uint32_t>(get_register(instr->RtField()));
set_register(instr->RdField(), rs_val < rt_val ? 1 : 0);
break;
}
case SRA: {
ASSERT(instr->RsField() == 0);
// Format(instr, "sra 'rd, 'rt, 'sa");
int32_t rt_val = get_register(instr->RtField());
int32_t sa = instr->SaField();
set_register(instr->RdField(), rt_val >> sa);
break;
}
case SRAV: {
ASSERT(instr->SaField() == 0);
// Format(instr, "srav 'rd, 'rt, 'rs");
int32_t rt_val = get_register(instr->RtField());
int32_t rs_val = get_register(instr->RsField());
set_register(instr->RdField(), rt_val >> (rs_val & 0x1f));
break;
}
case SRL: {
ASSERT(instr->RsField() == 0);
// Format(instr, "srl 'rd, 'rt, 'sa");
uint32_t rt_val = get_register(instr->RtField());
uint32_t sa = instr->SaField();
set_register(instr->RdField(), rt_val >> sa);
break;
}
case SRLV: {
ASSERT(instr->SaField() == 0);
// Format(instr, "srlv 'rd, 'rt, 'rs");
uint32_t rt_val = get_register(instr->RtField());
uint32_t rs_val = get_register(instr->RsField());
set_register(instr->RdField(), rt_val >> (rs_val & 0x1f));
break;
}
case SUBU: {
ASSERT(instr->SaField() == 0);
// Format(instr, "subu 'rd, 'rs, 'rt");
int32_t rs_val = get_register(instr->RsField());
int32_t rt_val = get_register(instr->RtField());
set_register(instr->RdField(), rs_val - rt_val);
break;
}
case XOR: {
ASSERT(instr->SaField() == 0);
// Format(instr, "xor 'rd, 'rs, 'rt");
int32_t rs_val = get_register(instr->RsField());
int32_t rt_val = get_register(instr->RtField());
set_register(instr->RdField(), rs_val ^ rt_val);
break;
}
default: {
OS::PrintErr("DecodeSpecial: 0x%x\n", instr->InstructionBits());
UnimplementedInstruction(instr);
break;
}
}
}
void Simulator::DecodeSpecial2(Instr* instr) {
ASSERT(instr->OpcodeField() == SPECIAL2);
switch (instr->FunctionField()) {
case MADD: {
ASSERT(instr->RdField() == 0);
ASSERT(instr->SaField() == 0);
// Format(instr, "madd 'rs, 'rt");
uint32_t lo = get_lo_register();
int32_t hi = get_hi_register();
int64_t accum = Utils::LowHighTo64Bits(lo, hi);
int64_t rs = get_register(instr->RsField());
int64_t rt = get_register(instr->RtField());
int64_t res = accum + rs * rt;
set_hi_register(Utils::High32Bits(res));
set_lo_register(Utils::Low32Bits(res));
break;
}
case MADDU: {
ASSERT(instr->RdField() == 0);
ASSERT(instr->SaField() == 0);
// Format(instr, "maddu 'rs, 'rt");
uint32_t lo = get_lo_register();
uint32_t hi = get_hi_register();
uint64_t accum = Utils::LowHighTo64Bits(lo, hi);
uint64_t rs = static_cast<uint32_t>(get_register(instr->RsField()));
uint64_t rt = static_cast<uint32_t>(get_register(instr->RtField()));
uint64_t res = accum + rs * rt;
set_hi_register(Utils::High32Bits(res));
set_lo_register(Utils::Low32Bits(res));
break;
}
case CLO: {
ASSERT(instr->SaField() == 0);
ASSERT(instr->RtField() == instr->RdField());
// Format(instr, "clo 'rd, 'rs");
int32_t rs_val = get_register(instr->RsField());
int32_t bitcount = 0;
while (rs_val < 0) {
bitcount++;
rs_val <<= 1;
}
set_register(instr->RdField(), bitcount);
break;
}
case CLZ: {
ASSERT(instr->SaField() == 0);
ASSERT(instr->RtField() == instr->RdField());
// Format(instr, "clz 'rd, 'rs");
int32_t rs_val = get_register(instr->RsField());
int32_t bitcount = 0;
if (rs_val != 0) {
while (rs_val > 0) {
bitcount++;
rs_val <<= 1;
}
} else {
bitcount = 32;
}
set_register(instr->RdField(), bitcount);
break;
}
default: {
OS::PrintErr("DecodeSpecial2: 0x%x\n", instr->InstructionBits());
UnimplementedInstruction(instr);
break;
}
}
}
void Simulator::DoBranch(Instr* instr, bool taken, bool likely) {
ASSERT(!delay_slot_);
int32_t imm_val = instr->SImmField() << 2;
uword next_pc;
if (taken) {
// imm_val is added to the address of the instruction following the branch.
next_pc = pc_ + imm_val + Instr::kInstrSize;
if (likely) {
ExecuteDelaySlot();
}
} else {
next_pc = pc_ + (2 * Instr::kInstrSize); // Next after delay slot.
}
if (!likely) {
ExecuteDelaySlot();
}
pc_ = next_pc - Instr::kInstrSize;
return;
}
void Simulator::DecodeRegImm(Instr* instr) {
ASSERT(instr->OpcodeField() == REGIMM);
switch (instr->RegImmFnField()) {
case BGEZ: {
// Format(instr, "bgez 'rs, 'dest");
int32_t rs_val = get_register(instr->RsField());
DoBranch(instr, rs_val >= 0, false);
break;
}
case BGEZAL: {
int32_t rs_val = get_register(instr->RsField());
// Return address is one after the delay slot.
set_register(RA, pc_ + (2*Instr::kInstrSize));
DoBranch(instr, rs_val >= 0, false);
break;
}
case BLTZAL: {
int32_t rs_val = get_register(instr->RsField());
// Return address is one after the delay slot.
set_register(RA, pc_ + (2*Instr::kInstrSize));
DoBranch(instr, rs_val < 0, false);
break;
}
case BGEZL: {
// Format(instr, "bgezl 'rs, 'dest");
int32_t rs_val = get_register(instr->RsField());
DoBranch(instr, rs_val >= 0, true);
break;
}
case BLTZ: {
// Format(instr, "bltz 'rs, 'dest");
int32_t rs_val = get_register(instr->RsField());
DoBranch(instr, rs_val < 0, false);
break;
}
case BLTZL: {
// Format(instr, "bltzl 'rs, 'dest");
int32_t rs_val = get_register(instr->RsField());
DoBranch(instr, rs_val < 0, true);
break;
}
default: {
OS::PrintErr("DecodeRegImm: 0x%x\n", instr->InstructionBits());
UnimplementedInstruction(instr);
break;
}
}
}
void Simulator::DecodeCop1(Instr* instr) {
ASSERT(instr->OpcodeField() == COP1);
if (instr->HasFormat()) {
// If the rs field is a valid format, then the function field identifies the
// instruction.
double fs_val = get_fregister_double(instr->FsField());
double ft_val = get_fregister_double(instr->FtField());
uint32_t cc, fcsr_cc;
cc = instr->FpuCCField();
fcsr_cc = get_fcsr_condition_bit(cc);
switch (instr->Cop1FunctionField()) {
case COP1_ADD: {
// Format(instr, "add.'fmt 'fd, 'fs, 'ft");
ASSERT(instr->FormatField() == FMT_D); // Only D supported.
set_fregister_double(instr->FdField(), fs_val + ft_val);
break;
}
case COP1_SUB: {
// Format(instr, "sub.'fmt 'fd, 'fs, 'ft");
ASSERT(instr->FormatField() == FMT_D); // Only D supported.
set_fregister_double(instr->FdField(), fs_val - ft_val);
break;
}
case COP1_MUL: {
// Format(instr, "mul.'fmt 'fd, 'fs, 'ft");
ASSERT(instr->FormatField() == FMT_D); // Only D supported.
set_fregister_double(instr->FdField(), fs_val * ft_val);
break;
}
case COP1_DIV: {
// Format(instr, "div.'fmt 'fd, 'fs, 'ft");
ASSERT(instr->FormatField() == FMT_D); // Only D supported.
set_fregister_double(instr->FdField(), fs_val / ft_val);
break;
}
case COP1_SQRT: {
// Format(instr, "sqrt.'fmt 'fd, 'fs");
ASSERT(instr->FormatField() == FMT_D); // Only D supported.
set_fregister_double(instr->FdField(), sqrt(fs_val));
break;
}
case COP1_MOV: {
// Format(instr, "mov.'fmt 'fd, 'fs");
ASSERT(instr->FormatField() == FMT_D); // Only D supported.
set_fregister_double(instr->FdField(), fs_val);
break;
}
case COP1_C_F: {
ASSERT(instr->FormatField() == FMT_D); // Only D supported.
ASSERT(instr->FdField() == F0);
set_fcsr_bit(fcsr_cc, false);
break;
}
case COP1_C_UN: {
ASSERT(instr->FormatField() == FMT_D); // Only D supported.
ASSERT(instr->FdField() == F0);
set_fcsr_bit(fcsr_cc, isnan(fs_val) || isnan(ft_val));
break;
}
case COP1_C_EQ: {
ASSERT(instr->FormatField() == FMT_D); // Only D supported.
ASSERT(instr->FdField() == F0);
set_fcsr_bit(fcsr_cc, (fs_val == ft_val));
break;
}
case COP1_C_UEQ: {
ASSERT(instr->FormatField() == FMT_D); // Only D supported.
ASSERT(instr->FdField() == F0);
set_fcsr_bit(fcsr_cc,
(fs_val == ft_val) || isnan(fs_val) || isnan(ft_val));
break;
}
case COP1_C_OLT: {
ASSERT(instr->FormatField() == FMT_D); // Only D supported.
ASSERT(instr->FdField() == F0);
set_fcsr_bit(fcsr_cc, (fs_val < ft_val));
break;
}
case COP1_C_ULT: {
ASSERT(instr->FormatField() == FMT_D); // Only D supported.
ASSERT(instr->FdField() == F0);
set_fcsr_bit(fcsr_cc,
(fs_val < ft_val) || isnan(fs_val) || isnan(ft_val));
break;
}
case COP1_C_OLE: {
ASSERT(instr->FormatField() == FMT_D); // Only D supported.
ASSERT(instr->FdField() == F0);
set_fcsr_bit(fcsr_cc, (fs_val <= ft_val));
break;
}
case COP1_C_ULE: {
ASSERT(instr->FormatField() == FMT_D); // Only D supported.
ASSERT(instr->FdField() == F0);
set_fcsr_bit(fcsr_cc,
(fs_val <= ft_val) || isnan(fs_val) || isnan(ft_val));
break;
}
case COP1_CVT_D: {
switch (instr->FormatField()) {
case FMT_W: {
int32_t fs_int = get_fregister(instr->FsField());
double fs_dbl = static_cast<double>(fs_int);
set_fregister_double(instr->FdField(), fs_dbl);
break;
}
case FMT_S: {
float fs_flt = get_fregister_float(instr->FsField());
double fs_dbl = static_cast<double>(fs_flt);
set_fregister_double(instr->FdField(), fs_dbl);
break;
}
case FMT_L: {
int64_t fs_int = get_fregister_long(instr->FsField());
double fs_dbl = static_cast<double>(fs_int);
set_fregister_double(instr->FdField(), fs_dbl);
break;
}
default: {
OS::PrintErr("DecodeCop1: 0x%x\n", instr->InstructionBits());
UnimplementedInstruction(instr);
break;
}
}
break;
}
case COP1_CVT_W: {
switch (instr->FormatField()) {
case FMT_D: {
double fs_dbl = get_fregister_double(instr->FsField());
int32_t fs_int;
if (isnan(fs_dbl) || isinf(fs_dbl) || (fs_dbl > INT_MAX) ||
(fs_dbl < INT_MIN)) {
fs_int = INT_MIN;
} else {
fs_int = static_cast<int32_t>(fs_dbl);
}
set_fregister(instr->FdField(), fs_int);
break;
}
default: {
OS::PrintErr("DecodeCop1: 0x%x\n", instr->InstructionBits());
UnimplementedInstruction(instr);
break;
}
}
break;
}
case COP1_CVT_S: {
switch (instr->FormatField()) {
case FMT_D: {
double fs_dbl = get_fregister_double(instr->FsField());
float fs_flt = static_cast<float>(fs_dbl);
set_fregister_float(instr->FdField(), fs_flt);
break;
}
default: {
OS::PrintErr("DecodeCop1: 0x%x\n", instr->InstructionBits());
UnimplementedInstruction(instr);
break;
}
}
break;
}
default: {
OS::PrintErr("DecodeCop1: 0x%x\n", instr->InstructionBits());
UnimplementedInstruction(instr);
break;
}
}
} else {
// If the rs field isn't a valid format, then it must be a sub-op.
switch (instr->Cop1SubField()) {
case COP1_MF: {
// Format(instr, "mfc1 'rt, 'fs");
ASSERT(instr->Bits(0, 11) == 0);
int32_t fs_val = get_fregister(instr->FsField());
set_register(instr->RtField(), fs_val);
break;
}
case COP1_MT: {
// Format(instr, "mtc1 'rt, 'fs");
ASSERT(instr->Bits(0, 11) == 0);
int32_t rt_val = get_register(instr->RtField());
set_fregister(instr->FsField(), rt_val);
break;
}
case COP1_BC: {
ASSERT(instr->Bit(17) == 0);
uint32_t cc, fcsr_cc;
cc = instr->Bits(18, 3);
fcsr_cc = get_fcsr_condition_bit(cc);
if (instr->Bit(16) == 1) { // Branch on true.
DoBranch(instr, test_fcsr_bit(fcsr_cc), false);
} else { // Branch on false.
DoBranch(instr, !test_fcsr_bit(fcsr_cc), false);
}
break;
}
default: {
OS::PrintErr("DecodeCop1: 0x%x\n", instr->InstructionBits());
UnimplementedInstruction(instr);
break;
}
}
}
}
void Simulator::InstructionDecode(Instr* instr) {
if (FLAG_trace_sim) {
const uword start = reinterpret_cast<uword>(instr);
const uword end = start + Instr::kInstrSize;
Disassembler::Disassemble(start, end);
}
switch (instr->OpcodeField()) {
case SPECIAL: {
DecodeSpecial(instr);
break;
}
case SPECIAL2: {
DecodeSpecial2(instr);
break;
}
case REGIMM: {
DecodeRegImm(instr);
break;
}
case COP1: {
DecodeCop1(instr);
break;
}
case ADDIU: {
// Format(instr, "addiu 'rt, 'rs, 'imms");
int32_t rs_val = get_register(instr->RsField());
int32_t imm_val = instr->SImmField();
int32_t res = rs_val + imm_val;
// Rt is set even on overflow.
set_register(instr->RtField(), res);
break;
}
case ANDI: {
// Format(instr, "andi 'rt, 'rs, 'immu");
int32_t rs_val = get_register(instr->RsField());
set_register(instr->RtField(), rs_val & instr->UImmField());
break;
}
case BEQ: {
// Format(instr, "beq 'rs, 'rt, 'dest");
int32_t rs_val = get_register(instr->RsField());
int32_t rt_val = get_register(instr->RtField());
DoBranch(instr, rs_val == rt_val, false);
break;
}
case BEQL: {
// Format(instr, "beql 'rs, 'rt, 'dest");
int32_t rs_val = get_register(instr->RsField());
int32_t rt_val = get_register(instr->RtField());
DoBranch(instr, rs_val == rt_val, true);
break;
}
case BGTZ: {
ASSERT(instr->RtField() == R0);
// Format(instr, "bgtz 'rs, 'dest");
int32_t rs_val = get_register(instr->RsField());
DoBranch(instr, rs_val > 0, false);
break;
}
case BGTZL: {
ASSERT(instr->RtField() == R0);
// Format(instr, "bgtzl 'rs, 'dest");
int32_t rs_val = get_register(instr->RsField());
DoBranch(instr, rs_val > 0, true);
break;
}
case BLEZ: {
ASSERT(instr->RtField() == R0);
// Format(instr, "blez 'rs, 'dest");
int32_t rs_val = get_register(instr->RsField());
DoBranch(instr, rs_val <= 0, false);
break;
}
case BLEZL: {
ASSERT(instr->RtField() == R0);
// Format(instr, "blezl 'rs, 'dest");
int32_t rs_val = get_register(instr->RsField());
DoBranch(instr, rs_val <= 0, true);
break;
}
case BNE: {
// Format(instr, "bne 'rs, 'rt, 'dest");
int32_t rs_val = get_register(instr->RsField());
int32_t rt_val = get_register(instr->RtField());
DoBranch(instr, rs_val != rt_val, false);
break;
}
case BNEL: {
// Format(instr, "bnel 'rs, 'rt, 'dest");
int32_t rs_val = get_register(instr->RsField());
int32_t rt_val = get_register(instr->RtField());
DoBranch(instr, rs_val != rt_val, true);
break;
}
case LB: {
// Format(instr, "lb 'rt, 'imms('rs)");
int32_t base_val = get_register(instr->RsField());
int32_t imm_val = instr->SImmField();
uword addr = base_val + imm_val;
if (Simulator::IsIllegalAddress(addr)) {
HandleIllegalAccess(addr, instr);
} else {
int32_t res = ReadB(addr);
set_register(instr->RtField(), res);
}
break;
}
case LBU: {
// Format(instr, "lbu 'rt, 'imms('rs)");
int32_t base_val = get_register(instr->RsField());
int32_t imm_val = instr->SImmField();
uword addr = base_val + imm_val;
if (Simulator::IsIllegalAddress(addr)) {
HandleIllegalAccess(addr, instr);
} else {
uint32_t res = ReadBU(addr);
set_register(instr->RtField(), res);
}
break;
}
case LDC1: {
// Format(instr, "ldc1 'ft, 'imms('rs)");
int32_t base_val = get_register(instr->RsField());
int32_t imm_val = instr->SImmField();
uword addr = base_val + imm_val;
if (Simulator::IsIllegalAddress(addr)) {
HandleIllegalAccess(addr, instr);
} else {
double value = ReadD(addr, instr);
set_fregister_double(instr->FtField(), value);
}
break;
}
case LH: {
// Format(instr, "lh 'rt, 'imms('rs)");
int32_t base_val = get_register(instr->RsField());
int32_t imm_val = instr->SImmField();
uword addr = base_val + imm_val;
if (Simulator::IsIllegalAddress(addr)) {
HandleIllegalAccess(addr, instr);
} else {
int32_t res = ReadH(addr, instr);
set_register(instr->RtField(), res);
}
break;
}
case LHU: {
// Format(instr, "lhu 'rt, 'imms('rs)");
int32_t base_val = get_register(instr->RsField());
int32_t imm_val = instr->SImmField();
uword addr = base_val + imm_val;
if (Simulator::IsIllegalAddress(addr)) {
HandleIllegalAccess(addr, instr);
} else {
int32_t res = ReadHU(addr, instr);
set_register(instr->RtField(), res);
}
break;
}
case LUI: {
ASSERT(instr->RsField() == 0);
set_register(instr->RtField(), instr->UImmField() << 16);
break;
}
case LW: {
// Format(instr, "lw 'rt, 'imms('rs)");
int32_t base_val = get_register(instr->RsField());
int32_t imm_val = instr->SImmField();
uword addr = base_val + imm_val;
if (Simulator::IsIllegalAddress(addr)) {
HandleIllegalAccess(addr, instr);
} else {
int32_t res = ReadW(addr, instr);
set_register(instr->RtField(), res);
}
break;
}
case LWC1: {
// Format(instr, "lwc1 'ft, 'imms('rs)");
int32_t base_val = get_register(instr->RsField());
int32_t imm_val = instr->SImmField();
uword addr = base_val + imm_val;
if (Simulator::IsIllegalAddress(addr)) {
HandleIllegalAccess(addr, instr);
} else {
int32_t value = ReadW(addr, instr);
set_fregister(instr->FtField(), value);
}
break;
}
case ORI: {
// Format(instr, "ori 'rt, 'rs, 'immu");
int32_t rs_val = get_register(instr->RsField());
set_register(instr->RtField(), rs_val | instr->UImmField());
break;
}
case SB: {
// Format(instr, "sb 'rt, 'imms('rs)");
int32_t rt_val = get_register(instr->RtField());
int32_t base_val = get_register(instr->RsField());
int32_t imm_val = instr->SImmField();
uword addr = base_val + imm_val;
if (Simulator::IsIllegalAddress(addr)) {
HandleIllegalAccess(addr, instr);
} else {
WriteB(addr, rt_val & 0xff);
}
break;
}
case SLTI: {
// Format(instr, "slti 'rt, 'rs, 'imms");
int32_t rs_val = get_register(instr->RsField());
int32_t imm_val = instr->SImmField();
set_register(instr->RtField(), rs_val < imm_val ? 1 : 0);
break;
}
case SLTIU: {
// Format(instr, "slti 'rt, 'rs, 'immu");
uint32_t rs_val = get_register(instr->RsField());
uint32_t imm_val = instr->UImmField();
set_register(instr->RtField(), rs_val < imm_val ? 1 : 0);
break;
}
case SDC1: {
// Format(instr, "sdc1 'ft, 'imms('rs)");
int32_t base_val = get_register(instr->RsField());
int32_t imm_val = instr->SImmField();
uword addr = base_val + imm_val;
if (Simulator::IsIllegalAddress(addr)) {
HandleIllegalAccess(addr, instr);
} else {
double value = get_fregister_double(instr->FtField());
WriteD(addr, value, instr);
}
break;
}
case SH: {
// Format(instr, "sh 'rt, 'imms('rs)");
int32_t rt_val = get_register(instr->RtField());
int32_t base_val = get_register(instr->RsField());
int32_t imm_val = instr->SImmField();
uword addr = base_val + imm_val;
if (Simulator::IsIllegalAddress(addr)) {
HandleIllegalAccess(addr, instr);
} else {
WriteH(addr, rt_val & 0xffff, instr);
}
break;
}
case SW: {
// Format(instr, "sw 'rt, 'imms('rs)");
int32_t rt_val = get_register(instr->RtField());
int32_t base_val = get_register(instr->RsField());
int32_t imm_val = instr->SImmField();
uword addr = base_val + imm_val;
if (Simulator::IsIllegalAddress(addr)) {
HandleIllegalAccess(addr, instr);
} else {
WriteW(addr, rt_val, instr);
}
break;
}
case SWC1: {
// Format(instr, "swc1 'ft, 'imms('rs)");
int32_t base_val = get_register(instr->RsField());
int32_t imm_val = instr->SImmField();
uword addr = base_val + imm_val;
if (Simulator::IsIllegalAddress(addr)) {
HandleIllegalAccess(addr, instr);
} else {
int32_t value = get_fregister(instr->FtField());
WriteW(addr, value, instr);
}
break;
}
case XORI: {
// Format(instr, "xori 'rt, 'rs, 'immu");
int32_t rs_val = get_register(instr->RsField());
set_register(instr->RtField(), rs_val ^ instr->UImmField());
break;
break;
}
default: {
OS::PrintErr("Undecoded instruction: 0x%x at %p\n",
instr->InstructionBits(), instr);
UnimplementedInstruction(instr);
break;
}
}
pc_ += Instr::kInstrSize;
}
void Simulator::ExecuteDelaySlot() {
ASSERT(pc_ != kEndSimulatingPC);
delay_slot_ = true;
icount_++;
Instr* instr = Instr::At(pc_ + Instr::kInstrSize);
if ((FLAG_stop_sim_at != 0) && (icount_ == FLAG_stop_sim_at)) {
SimulatorDebugger dbg(this);
dbg.Stop(instr, "Instruction count reached");
}
InstructionDecode(instr);
delay_slot_ = false;
}
void Simulator::Execute() {
if (FLAG_stop_sim_at == 0) {
// Fast version of the dispatch loop without checking whether the simulator
// should be stopping at a particular executed instruction.
while (pc_ != kEndSimulatingPC) {
icount_++;
Instr* instr = Instr::At(pc_);
if (IsIllegalAddress(pc_)) {
HandleIllegalAccess(pc_, instr);
} else {
InstructionDecode(instr);
}
}
} else {
// FLAG_stop_sim_at is at the non-default value. Stop in the debugger when
// we reach the particular instruction count.
while (pc_ != kEndSimulatingPC) {
icount_++;
Instr* instr = Instr::At(pc_);
if (icount_ == FLAG_stop_sim_at) {
SimulatorDebugger dbg(this);
dbg.Stop(instr, "Instruction count reached");
} else {
if (IsIllegalAddress(pc_)) {
HandleIllegalAccess(pc_, instr);
} else {
InstructionDecode(instr);
}
}
}
}
}
int64_t Simulator::Call(int32_t entry,
int32_t parameter0,
int32_t parameter1,
int32_t parameter2,
int32_t parameter3,
bool fp_return,
bool fp_args) {
// Save the SP register before the call so we can restore it.
int32_t sp_before_call = get_register(SP);
// Setup parameters.
if (fp_args) {
set_fregister(F0, parameter0);
set_fregister(F1, parameter1);
set_fregister(F2, parameter2);
set_fregister(F3, parameter3);
} else {
set_register(A0, parameter0);
set_register(A1, parameter1);
set_register(A2, parameter2);
set_register(A3, parameter3);
}
// Make sure the activation frames are properly aligned.
int32_t stack_pointer = sp_before_call;
if (OS::ActivationFrameAlignment() > 1) {
stack_pointer =
Utils::RoundDown(stack_pointer, OS::ActivationFrameAlignment());
}
set_register(SP, stack_pointer);
// Prepare to execute the code at entry.
set_pc(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
// RA the simulation stops when returning to this call point.
set_register(RA, kEndSimulatingPC);
// Remember the values of callee-saved registers.
// The code below assumes that r9 is not used as sb (static base) in
// simulator code and therefore is regarded as a callee-saved register.
int32_t r16_val = get_register(R16);
int32_t r17_val = get_register(R17);
int32_t r18_val = get_register(R18);
int32_t r19_val = get_register(R19);
int32_t r20_val = get_register(R20);
int32_t r21_val = get_register(R21);
int32_t r22_val = get_register(R22);
int32_t r23_val = get_register(R23);
double d10_val = get_dregister(D10);
double d11_val = get_dregister(D11);
double d12_val = get_dregister(D12);
double d13_val = get_dregister(D13);
double d14_val = get_dregister(D14);
double d15_val = get_dregister(D15);
// Setup the callee-saved registers with a known value. To be able to check
// that they are preserved properly across dart execution.
int32_t callee_saved_value = icount_;
set_register(R16, callee_saved_value);
set_register(R17, callee_saved_value);
set_register(R18, callee_saved_value);
set_register(R19, callee_saved_value);
set_register(R20, callee_saved_value);
set_register(R21, callee_saved_value);
set_register(R22, callee_saved_value);
set_register(R23, callee_saved_value);
set_dregister_bits(D10, callee_saved_value);
set_dregister_bits(D11, callee_saved_value);
set_dregister_bits(D12, callee_saved_value);
set_dregister_bits(D13, callee_saved_value);
set_dregister_bits(D14, callee_saved_value);
set_dregister_bits(D15, callee_saved_value);
// Start the simulation
Execute();
// Check that the callee-saved registers have been preserved.
ASSERT(callee_saved_value == get_register(R16));
ASSERT(callee_saved_value == get_register(R17));
ASSERT(callee_saved_value == get_register(R18));
ASSERT(callee_saved_value == get_register(R19));
ASSERT(callee_saved_value == get_register(R20));
ASSERT(callee_saved_value == get_register(R21));
ASSERT(callee_saved_value == get_register(R22));
ASSERT(callee_saved_value == get_register(R23));
ASSERT(callee_saved_value == get_dregister_bits(D10));
ASSERT(callee_saved_value == get_dregister_bits(D11));
ASSERT(callee_saved_value == get_dregister_bits(D12));
ASSERT(callee_saved_value == get_dregister_bits(D13));
ASSERT(callee_saved_value == get_dregister_bits(D14));
ASSERT(callee_saved_value == get_dregister_bits(D15));
// Restore callee-saved registers with the original value.
set_register(R16, r16_val);
set_register(R17, r17_val);
set_register(R18, r18_val);
set_register(R19, r19_val);
set_register(R20, r20_val);
set_register(R21, r21_val);
set_register(R22, r22_val);
set_register(R23, r23_val);
set_dregister(D10, d10_val);
set_dregister(D11, d11_val);
set_dregister(D12, d12_val);
set_dregister(D13, d13_val);
set_dregister(D14, d14_val);
set_dregister(D15, d15_val);
// Restore the SP register and return V1:V0.
set_register(SP, sp_before_call);
int64_t return_value;
if (fp_return) {
return_value = Utils::LowHighTo64Bits(get_fregister(F0), get_fregister(F1));
} else {
return_value = Utils::LowHighTo64Bits(get_register(V0), get_register(V1));
}
return return_value;
}
void Simulator::Longjmp(uword pc,
uword sp,
uword fp,
RawObject* raw_exception,
RawObject* raw_stacktrace,
Isolate* isolate) {
// Walk over all setjmp buffers (simulated --> C++ transitions)
// and try to find the setjmp associated with the simulated stack pointer.
SimulatorSetjmpBuffer* buf = last_setjmp_buffer();
while (buf->link() != NULL && buf->link()->sp() <= sp) {
buf = buf->link();
}
ASSERT(buf != NULL);
// The C++ caller has not cleaned up the stack memory of C++ frames.
// Prepare for unwinding frames by destroying all the stack resources
// in the previous C++ frames.
uword native_sp = buf->native_sp();
while (isolate->top_resource() != NULL &&
(reinterpret_cast<uword>(isolate->top_resource()) < native_sp)) {
isolate->top_resource()->~StackResource();
}
// Unwind the C++ stack and continue simulation in the target frame.
set_pc(static_cast<int32_t>(pc));
set_register(SP, static_cast<int32_t>(sp));
set_register(FP, static_cast<int32_t>(fp));
// Set the tag.
isolate->set_vm_tag(VMTag::kDartTagId);
// Clear top exit frame.
isolate->set_top_exit_frame_info(0);
ASSERT(raw_exception != Object::null());
set_register(kExceptionObjectReg, bit_cast<int32_t>(raw_exception));
set_register(kStackTraceObjectReg, bit_cast<int32_t>(raw_stacktrace));
buf->Longjmp();
}
} // namespace dart
#endif // !defined(HOST_ARCH_MIPS)
#endif // defined TARGET_ARCH_MIPS