Files
sdk/runtime/vm/simulator_arm.cc
T
Regis Crelier cfc006a8f4 [VM/nnbd] Fix sharing of type argument vectors (fixes #40451).
Apply the same fix as in https://dart-review.googlesource.com/c/sdk/+/136709
to TypeArguments::CanShareInstantiatorTypeArguments and to TypeArguments::CanShareFunctionTypeArguments.

Move the inlined code performing an instantiation cache lookup to stubs in order to reduce code size.
Add stubs checking nullability of type arguments to share instantiator (or function) type arguments before looking up instantiation cache, and possibly calling into the runtime for instantiation.
Modify constant propagator to instantiate constant type arguments at compile time.

Change-Id: I72fee1a6881ac3bec55fae7d0ef5a3361544a141
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/138009
Commit-Queue: Régis Crelier <regis@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
2020-03-13 22:29:47 +00:00

3664 lines
124 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> // NOLINT
#include <stdlib.h>
#include "vm/globals.h"
#if defined(TARGET_ARCH_ARM)
// Only build the simulator if not compiling for real ARM hardware.
#if defined(USING_SIMULATOR)
#include "vm/simulator.h"
#include "vm/compiler/assembler/assembler.h"
#include "vm/compiler/assembler/disassembler.h"
#include "vm/constants.h"
#include "vm/cpu.h"
#include "vm/native_arguments.h"
#include "vm/os_thread.h"
#include "vm/stack_frame.h"
namespace dart {
DEFINE_FLAG(uint64_t,
trace_sim_after,
ULLONG_MAX,
"Trace simulator execution after instruction count reached.");
DEFINE_FLAG(uint64_t,
stop_sim_at,
ULLONG_MAX,
"Instruction address or instruction count 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:
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));
}
~SimulatorSetjmpBuffer() {
ASSERT(simulator_->last_setjmp_buffer() == this);
simulator_->set_last_setjmp_buffer(link_);
}
SimulatorSetjmpBuffer* link() { return link_; }
uword sp() { return sp_; }
private:
uword sp_;
Simulator* simulator_;
SimulatorSetjmpBuffer* link_;
jmp_buf buffer_;
friend class Simulator;
};
// The SimulatorDebugger class is used by the simulator while debugging
// simulated ARM 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, float* value);
bool GetDValue(char* desc, double* value);
static TokenPosition GetApproximateTokenIndex(const Code& code, uword pc);
static void PrintDartFrame(uword pc,
uword fp,
uword sp,
const Function& function,
TokenPosition token_pos,
bool is_optimized,
bool is_inlined);
void PrintBacktrace();
// 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::PrintErr("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", "pc", "lr", "sp", "ip", "fp", "pp"};
static const Register kRegisters[] = {R0, R1, R2, R3, R4, R5, R6, R7,
R8, R9, R10, R11, R12, R13, R14, R15,
PC, LR, SP, IP, FP, PP};
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 SRegister LookupSRegisterByName(const char* name) {
int reg_nr = -1;
bool ok = SScanF(name, "s%d", &reg_nr);
if (ok && (0 <= reg_nr) && (reg_nr < kNumberOfSRegisters)) {
return static_cast<SRegister>(reg_nr);
}
return kNoSRegister;
}
static DRegister LookupDRegisterByName(const char* name) {
int reg_nr = -1;
bool ok = SScanF(name, "d%d", &reg_nr);
if (ok && (0 <= reg_nr) && (reg_nr < kNumberOfDRegisters)) {
return static_cast<DRegister>(reg_nr);
}
return kNoDRegister;
}
bool SimulatorDebugger::GetValue(char* desc, uint32_t* value) {
Register reg = LookupCpuRegisterByName(desc);
if (reg != kNoRegister) {
if (reg == PC) {
*value = sim_->get_pc();
} else {
*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;
}
}
bool retval = SScanF(desc, "0x%x", value) == 1;
if (!retval) {
retval = SScanF(desc, "%x", value) == 1;
}
return retval;
}
bool SimulatorDebugger::GetFValue(char* desc, float* value) {
SRegister sreg = LookupSRegisterByName(desc);
if (sreg != kNoSRegister) {
*value = sim_->get_sregister(sreg);
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) {
DRegister dreg = LookupDRegisterByName(desc);
if (dreg != kNoDRegister) {
*value = sim_->get_dregister(dreg);
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;
}
TokenPosition SimulatorDebugger::GetApproximateTokenIndex(const Code& code,
uword pc) {
TokenPosition token_pos = TokenPosition::kNoSource;
uword pc_offset = pc - code.PayloadStart();
const PcDescriptors& descriptors =
PcDescriptors::Handle(code.pc_descriptors());
PcDescriptors::Iterator iter(descriptors, RawPcDescriptors::kAnyKind);
while (iter.MoveNext()) {
if (iter.PcOffset() == pc_offset) {
return iter.TokenPos();
} else if (!token_pos.IsReal() && (iter.PcOffset() > pc_offset)) {
token_pos = iter.TokenPos();
}
}
return token_pos;
}
void SimulatorDebugger::PrintDartFrame(uword pc,
uword fp,
uword sp,
const Function& function,
TokenPosition token_pos,
bool is_optimized,
bool is_inlined) {
const Script& script = Script::Handle(function.script());
const String& func_name = String::Handle(function.QualifiedScrubbedName());
const String& url = String::Handle(script.url());
intptr_t line = -1;
intptr_t column = -1;
if (token_pos.IsReal()) {
script.GetTokenLocation(token_pos, &line, &column);
}
OS::PrintErr(
"pc=0x%" Px " fp=0x%" Px " sp=0x%" Px " %s%s (%s:%" Pd ":%" Pd ")\n", pc,
fp, sp, is_optimized ? (is_inlined ? "inlined " : "optimized ") : "",
func_name.ToCString(), url.ToCString(), line, column);
}
void SimulatorDebugger::PrintBacktrace() {
StackFrameIterator frames(
sim_->get_register(FP), sim_->get_register(SP), sim_->get_pc(),
ValidationPolicy::kDontValidateFrames, Thread::Current(),
StackFrameIterator::kNoCrossThreadIteration);
StackFrame* frame = frames.NextFrame();
ASSERT(frame != NULL);
Function& function = Function::Handle();
Function& inlined_function = Function::Handle();
Code& code = Code::Handle();
Code& unoptimized_code = Code::Handle();
while (frame != NULL) {
if (frame->IsDartFrame()) {
ASSERT(!frame->is_interpreted()); // Not yet supported.
code = frame->LookupDartCode();
function = code.function();
if (code.is_optimized()) {
// For optimized frames, extract all the inlined functions if any
// into the stack trace.
InlinedFunctionsIterator it(code, frame->pc());
while (!it.Done()) {
// Print each inlined frame with its pc in the corresponding
// unoptimized frame.
inlined_function = it.function();
unoptimized_code = it.code();
uword unoptimized_pc = it.pc();
it.Advance();
if (!it.Done()) {
PrintDartFrame(
unoptimized_pc, frame->fp(), frame->sp(), inlined_function,
GetApproximateTokenIndex(unoptimized_code, unoptimized_pc),
true, true);
}
}
// Print the optimized inlining frame below.
}
PrintDartFrame(frame->pc(), frame->fp(), frame->sp(), function,
GetApproximateTokenIndex(code, frame->pc()),
code.is_optimized(), false);
} else {
OS::PrintErr("pc=0x%" Px " fp=0x%" Px " sp=0x%" Px " %s frame\n",
frame->pc(), frame->fp(), frame->sp(),
frame->IsEntryFrame()
? "entry"
: frame->IsExitFrame()
? "exit"
: frame->IsStubFrame() ? "stub" : "invalid");
}
frame = frames.NextFrame();
}
}
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::kSimulatorBreakpointInstruction);
}
}
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::PrintErr("pc is out of bounds: 0x%" Px "\n", last_pc);
} else {
if (FLAG_support_disassembler) {
Disassembler::Disassemble(last_pc, last_pc + Instr::kInstrSize);
} else {
OS::PrintErr("Disassembler not supported in this mode.\n");
}
}
}
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::PrintErr(
"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"
"flags -- print flag values\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 icount or value or *addr> -- print integer\n"
"ps/printsingle <sreg or *addr> -- print float value\n"
"pd/printdouble <dreg or *addr> -- print double value\n"
"po/printobject <*reg or *addr> -- print object\n"
"si/stepi -- single step an instruction\n"
"trace -- toggle execution tracing mode\n"
"bt -- print backtrace\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::PrintErr("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 (strcmp(arg1, "icount") == 0) {
const uint64_t icount = sim_->get_icount();
OS::PrintErr("icount: %" Pu64 " 0x%" Px64 "\n", icount, icount);
} else if (GetValue(arg1, &value)) {
OS::PrintErr("%s: %u 0x%x\n", arg1, value, value);
} else {
OS::PrintErr("%s unrecognized\n", arg1);
}
} else {
OS::PrintErr("print <reg or icount or value or *addr>\n");
}
} else if ((strcmp(cmd, "ps") == 0) ||
(strcmp(cmd, "printsingle") == 0)) {
if (args == 2) {
float fvalue;
if (GetFValue(arg1, &fvalue)) {
uint32_t value = bit_cast<uint32_t, float>(fvalue);
OS::PrintErr("%s: 0%u 0x%x %.8g\n", arg1, value, value, fvalue);
} else {
OS::PrintErr("%s unrecognized\n", arg1);
}
} else {
OS::PrintErr("printfloat <sreg 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::PrintErr("%s: %llu 0x%llx %.8g\n", arg1, long_value, long_value,
dvalue);
} else {
OS::PrintErr("%s unrecognized\n", arg1);
}
} else {
OS::PrintErr("printdouble <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::PrintErr("%s: \n", arg1);
#if defined(DEBUG)
const Object& obj =
Object::Handle(reinterpret_cast<RawObject*>(value));
obj.Print();
#endif // defined(DEBUG)
} else {
OS::PrintErr("0x%x is not an object reference\n", value);
}
} else {
OS::PrintErr("%s unrecognized\n", arg1);
}
} else {
OS::PrintErr("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::PrintErr("First argument yields invalid address: 0x%x\n",
start);
OS::PrintErr("Using PC instead\n");
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::PrintErr("First argument yields invalid address: 0x%x\n",
start);
OS::PrintErr("Using PC instead\n");
start = sim_->get_pc();
}
end = start + (length * Instr::kInstrSize);
}
}
if ((start > 0) && (end > start)) {
if (FLAG_support_disassembler) {
Disassembler::Disassemble(start, end);
} else {
OS::PrintErr("Disassembler not supported in this mode.\n");
}
} else {
OS::PrintErr("disasm [<address> [<number_of_instructions>]]\n");
}
} else if (strcmp(cmd, "gdb") == 0) {
OS::PrintErr("relinquishing control to gdb\n");
OS::DebugBreak();
OS::PrintErr("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::PrintErr("setting breakpoint failed\n");
}
} else {
OS::PrintErr("%s unrecognized\n", arg1);
}
} else {
OS::PrintErr("break <addr>\n");
}
} else if (strcmp(cmd, "del") == 0) {
if (!DeleteBreakpoint(NULL)) {
OS::PrintErr("deleting breakpoint failed\n");
}
} else if (strcmp(cmd, "flags") == 0) {
OS::PrintErr("APSR: ");
OS::PrintErr("N flag: %d; ", sim_->n_flag_);
OS::PrintErr("Z flag: %d; ", sim_->z_flag_);
OS::PrintErr("C flag: %d; ", sim_->c_flag_);
OS::PrintErr("V flag: %d\n", sim_->v_flag_);
OS::PrintErr("FPSCR: ");
OS::PrintErr("N flag: %d; ", sim_->fp_n_flag_);
OS::PrintErr("Z flag: %d; ", sim_->fp_z_flag_);
OS::PrintErr("C flag: %d; ", sim_->fp_c_flag_);
OS::PrintErr("V flag: %d\n", sim_->fp_v_flag_);
} 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->IsSvc() || stop_instr->IsBkpt()) {
stop_instr->SetInstructionBits(Instr::kNopInstruction);
} else {
OS::PrintErr("Not at debugger stop.\n");
}
} else if (strcmp(cmd, "trace") == 0) {
if (FLAG_trace_sim_after == ULLONG_MAX) {
FLAG_trace_sim_after = sim_->get_icount();
OS::PrintErr("execution tracing on\n");
} else {
FLAG_trace_sim_after = ULLONG_MAX;
OS::PrintErr("execution tracing off\n");
}
} else if (strcmp(cmd, "bt") == 0) {
Thread* thread = reinterpret_cast<Thread*>(sim_->get_register(THR));
thread->set_execution_state(Thread::kThreadInVM);
PrintBacktrace();
thread->set_execution_state(Thread::kThreadInGenerated);
} else {
OS::PrintErr("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::PrintErr("%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::Init() {}
Simulator::Simulator() : exclusive_access_addr_(0), exclusive_access_value_(0) {
// 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[(OSThread::GetSpecifiedStackSize() +
OSThread::kStackSizeBufferMax + kSimulatorStackUnderflowSize)];
// Low address.
stack_limit_ = reinterpret_cast<uword>(stack_);
// Limit for StackOverflowError.
overflow_stack_limit_ = stack_limit_ + OSThread::kStackSizeBufferMax;
// High address.
stack_base_ = overflow_stack_limit_ + OSThread::GetSpecifiedStackSize();
pc_modified_ = false;
icount_ = 0;
break_pc_ = NULL;
break_instr_ = 0;
last_setjmp_buffer_ = NULL;
// Setup architecture state.
// All registers are initialized to zero to start with.
for (int i = 0; i < kNumberOfCpuRegisters; i++) {
registers_[i] = 0;
}
n_flag_ = false;
z_flag_ = false;
c_flag_ = false;
v_flag_ = false;
// The sp is initialized to point to the bottom (high address) of the
// allocated stack area.
registers_[SP] = stack_base();
// The lr 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] = kBadLR;
registers_[LR] = kBadLR;
// All double-precision registers are initialized to zero.
for (int i = 0; i < kNumberOfDRegisters; i++) {
dregisters_[i] = 0;
}
// Since VFP registers are overlapping, single-precision registers should
// already be initialized.
ASSERT(2 * kNumberOfDRegisters >= kNumberOfSRegisters);
for (int i = 0; i < kNumberOfSRegisters; i++) {
ASSERT(sregisters_[i] == 0.0);
}
fp_n_flag_ = false;
fp_z_flag_ = false;
fp_c_flag_ = false;
fp_v_flag_ = false;
}
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 svc (supervisor call) instruction that is handled by
// the simulator. We write the original destination of the jump just at a known
// offset from the svc instruction so the simulator knows what to call.
class Redirection {
public:
uword address_of_svc_instruction() {
return reinterpret_cast<uword>(&svc_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) {
MutexLocker ml(mutex_);
Redirection* old_head = list_.load(std::memory_order_relaxed);
for (Redirection* current = old_head; current != nullptr;
current = current->next_) {
if (current->external_function_ == external_function) return current;
}
Redirection* redirection =
new Redirection(external_function, call_kind, argument_count);
redirection->next_ = old_head;
// Use a memory fence to ensure all pending writes are written at the time
// of updating the list head, so the profiling thread always has a valid
// list to look at.
list_.store(redirection, std::memory_order_release);
return redirection;
}
static Redirection* FromSvcInstruction(Instr* svc_instruction) {
char* addr_of_svc = reinterpret_cast<char*>(svc_instruction);
char* addr_of_redirection =
addr_of_svc - OFFSET_OF(Redirection, svc_instruction_);
return reinterpret_cast<Redirection*>(addr_of_redirection);
}
// Please note that this function is called by the signal handler of the
// profiling thread. It can therefore run at any point in time and is not
// allowed to hold any locks - which is precisely the reason why the list is
// prepend-only and a memory fence is used when writing the list head [list_]!
static uword FunctionForRedirect(uword address_of_svc) {
for (Redirection* current = list_.load(std::memory_order_acquire);
current != nullptr; current = current->next_) {
if (current->address_of_svc_instruction() == address_of_svc) {
return current->external_function_;
}
}
return 0;
}
private:
Redirection(uword external_function,
Simulator::CallKind call_kind,
int argument_count)
: external_function_(external_function),
call_kind_(call_kind),
argument_count_(argument_count),
svc_instruction_(Instr::kSimulatorRedirectInstruction),
next_(NULL) {}
uword external_function_;
Simulator::CallKind call_kind_;
int argument_count_;
uint32_t svc_instruction_;
Redirection* next_;
static std::atomic<Redirection*> list_;
static Mutex* mutex_;
};
std::atomic<Redirection*> Redirection::list_ = {nullptr};
Mutex* Redirection::mutex_ = new Mutex();
uword Simulator::RedirectExternalReference(uword function,
CallKind call_kind,
int argument_count) {
Redirection* redirection =
Redirection::Get(function, call_kind, argument_count);
return redirection->address_of_svc_instruction();
}
uword Simulator::FunctionForRedirect(uword redirect) {
return Redirection::FunctionForRedirect(redirect);
}
// Get the active Simulator for the current isolate.
Simulator* Simulator::Current() {
Isolate* isolate = Isolate::Current();
Simulator* simulator = isolate->simulator();
if (simulator == NULL) {
NoSafepointScope no_safepoint;
simulator = new Simulator();
isolate->set_simulator(simulator);
}
return simulator;
}
// Sets the register in the architecture state. It will also deal with updating
// Simulator internal state for special registers such as PC.
DART_FORCE_INLINE void Simulator::set_register(Register reg, int32_t value) {
ASSERT((reg >= 0) && (reg < kNumberOfCpuRegisters));
if (reg == PC) {
pc_modified_ = true;
}
registers_[reg] = value;
}
// Raw access to the PC register.
DART_FORCE_INLINE void Simulator::set_pc(int32_t value) {
pc_modified_ = true;
registers_[PC] = value;
}
// Accessors for VFP register state.
DART_FORCE_INLINE void Simulator::set_sregister(SRegister reg, float value) {
ASSERT(TargetCPUFeatures::vfp_supported());
ASSERT((reg >= 0) && (reg < kNumberOfSRegisters));
sregisters_[reg] = bit_cast<int32_t, float>(value);
}
DART_FORCE_INLINE float Simulator::get_sregister(SRegister reg) const {
ASSERT(TargetCPUFeatures::vfp_supported());
ASSERT((reg >= 0) && (reg < kNumberOfSRegisters));
return bit_cast<float, int32_t>(sregisters_[reg]);
}
DART_FORCE_INLINE void Simulator::set_dregister(DRegister reg, double value) {
ASSERT(TargetCPUFeatures::vfp_supported());
ASSERT((reg >= 0) && (reg < kNumberOfDRegisters));
dregisters_[reg] = bit_cast<int64_t, double>(value);
}
DART_FORCE_INLINE double Simulator::get_dregister(DRegister reg) const {
ASSERT(TargetCPUFeatures::vfp_supported());
ASSERT((reg >= 0) && (reg < kNumberOfDRegisters));
return bit_cast<double, int64_t>(dregisters_[reg]);
}
void Simulator::set_qregister(QRegister reg, const simd_value_t& value) {
ASSERT(TargetCPUFeatures::neon_supported());
ASSERT((reg >= 0) && (reg < kNumberOfQRegisters));
qregisters_[reg].data_[0] = value.data_[0];
qregisters_[reg].data_[1] = value.data_[1];
qregisters_[reg].data_[2] = value.data_[2];
qregisters_[reg].data_[3] = value.data_[3];
}
void Simulator::get_qregister(QRegister reg, simd_value_t* value) const {
ASSERT(TargetCPUFeatures::neon_supported());
// TODO(zra): Replace this test with an assert after we support
// 16 Q registers.
if ((reg >= 0) && (reg < kNumberOfQRegisters)) {
*value = qregisters_[reg];
}
}
void Simulator::set_sregister_bits(SRegister reg, int32_t value) {
ASSERT(TargetCPUFeatures::vfp_supported());
ASSERT((reg >= 0) && (reg < kNumberOfSRegisters));
sregisters_[reg] = value;
}
int32_t Simulator::get_sregister_bits(SRegister reg) const {
ASSERT(TargetCPUFeatures::vfp_supported());
ASSERT((reg >= 0) && (reg < kNumberOfSRegisters));
return sregisters_[reg];
}
void Simulator::set_dregister_bits(DRegister reg, int64_t value) {
ASSERT(TargetCPUFeatures::vfp_supported());
ASSERT((reg >= 0) && (reg < kNumberOfDRegisters));
dregisters_[reg] = value;
}
int64_t Simulator::get_dregister_bits(DRegister reg) const {
ASSERT(TargetCPUFeatures::vfp_supported());
ASSERT((reg >= 0) && (reg < kNumberOfDRegisters));
return dregisters_[reg];
}
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.");
}
// Processor versions prior to ARMv7 could not do unaligned reads and writes.
// On some ARM platforms an interrupt is caused. On others it does a funky
// rotation thing. However, from version v7, unaligned access is supported.
// Note that simulator runs have the runtime system running directly on the host
// system and only generated code is executed in the simulator. Since the host
// is typically IA32 we will get the correct ARMv7-like behaviour on unaligned
// accesses, but we should actually not generate code accessing unaligned data,
// so we still want to know and abort if we encounter such code.
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[64];
snprintf(buffer, sizeof(buffer), "unaligned %s at 0x%" Px ", pc=%p\n", msg,
addr, instr);
SimulatorDebugger dbg(this);
dbg.Stop(instr, buffer);
// The debugger will return control in non-interactive mode.
FATAL("Cannot continue execution after unaligned access.");
}
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.");
}
DART_FORCE_INLINE 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;
}
DART_FORCE_INLINE 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);
}
DART_FORCE_INLINE 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;
}
DART_FORCE_INLINE 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;
}
DART_FORCE_INLINE 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);
}
DART_FORCE_INLINE uint8_t Simulator::ReadBU(uword addr) {
uint8_t* ptr = reinterpret_cast<uint8_t*>(addr);
return *ptr;
}
DART_FORCE_INLINE int8_t Simulator::ReadB(uword addr) {
int8_t* ptr = reinterpret_cast<int8_t*>(addr);
return *ptr;
}
DART_FORCE_INLINE void Simulator::WriteB(uword addr, uint8_t value) {
uint8_t* ptr = reinterpret_cast<uint8_t*>(addr);
*ptr = value;
}
void Simulator::ClearExclusive() {
exclusive_access_addr_ = 0;
exclusive_access_value_ = 0;
}
intptr_t Simulator::ReadExclusiveW(uword addr, Instr* instr) {
exclusive_access_addr_ = addr;
exclusive_access_value_ = ReadW(addr, instr);
return exclusive_access_value_;
}
intptr_t Simulator::WriteExclusiveW(uword addr, intptr_t value, Instr* instr) {
// In a well-formed code store-exclusive instruction should always follow
// a corresponding load-exclusive instruction with the same address.
ASSERT((exclusive_access_addr_ == 0) || (exclusive_access_addr_ == addr));
if (exclusive_access_addr_ != addr) {
return 1; // Failure.
}
int32_t old_value = static_cast<uint32_t>(exclusive_access_value_);
ClearExclusive();
auto atomic_addr = reinterpret_cast<RelaxedAtomic<int32_t>*>(addr);
if (atomic_addr->compare_exchange_weak(old_value, value)) {
return 0; // Success.
}
return 1; // Failure.
}
bool Simulator::IsTracingExecution() const {
return icount_ > FLAG_trace_sim_after;
}
// Unsupported instructions use Format to print an error and stop execution.
void Simulator::Format(Instr* instr, const char* format) {
OS::PrintErr("Simulator found unsupported instruction:\n 0x%p: %s\n", instr,
format);
UNIMPLEMENTED();
}
// Checks if the current instruction should be executed based on its
// condition bits.
DART_FORCE_INLINE bool Simulator::ConditionallyExecute(Instr* instr) {
switch (instr->ConditionField()) {
case EQ:
return z_flag_;
case NE:
return !z_flag_;
case CS:
return c_flag_;
case CC:
return !c_flag_;
case MI:
return n_flag_;
case PL:
return !n_flag_;
case VS:
return v_flag_;
case VC:
return !v_flag_;
case HI:
return c_flag_ && !z_flag_;
case LS:
return !c_flag_ || z_flag_;
case GE:
return n_flag_ == v_flag_;
case LT:
return n_flag_ != v_flag_;
case GT:
return !z_flag_ && (n_flag_ == v_flag_);
case LE:
return z_flag_ || (n_flag_ != v_flag_);
case AL:
return true;
default:
UNREACHABLE();
}
return false;
}
// Calculate and set the Negative and Zero flags.
DART_FORCE_INLINE void Simulator::SetNZFlags(int32_t val) {
n_flag_ = (val < 0);
z_flag_ = (val == 0);
}
// Set the Carry flag.
DART_FORCE_INLINE void Simulator::SetCFlag(bool val) {
c_flag_ = val;
}
// Set the oVerflow flag.
DART_FORCE_INLINE void Simulator::SetVFlag(bool val) {
v_flag_ = val;
}
// Calculate C flag value for additions (and subtractions with adjusted args).
DART_FORCE_INLINE bool Simulator::CarryFrom(int32_t left,
int32_t right,
int32_t carry) {
uint64_t uleft = static_cast<uint32_t>(left);
uint64_t uright = static_cast<uint32_t>(right);
uint64_t ucarry = static_cast<uint32_t>(carry);
return ((uleft + uright + ucarry) >> 32) != 0;
}
// Calculate V flag value for additions (and subtractions with adjusted args).
DART_FORCE_INLINE bool Simulator::OverflowFrom(int32_t left,
int32_t right,
int32_t carry) {
int64_t result = static_cast<int64_t>(left) + right + carry;
return (result >> 31) != (result >> 32);
}
// Addressing Mode 1 - Data-processing operands:
// Get the value based on the shifter_operand with register.
int32_t Simulator::GetShiftRm(Instr* instr, bool* carry_out) {
Shift shift = instr->ShiftField();
int shift_amount = instr->ShiftAmountField();
int32_t result = get_register(instr->RmField());
if (instr->Bit(4) == 0) {
// by immediate
if ((shift == ROR) && (shift_amount == 0)) {
UnimplementedInstruction(instr);
} else if (((shift == LSR) || (shift == ASR)) && (shift_amount == 0)) {
shift_amount = 32;
}
switch (shift) {
case ASR: {
if (shift_amount == 0) {
if (result < 0) {
result = 0xffffffff;
*carry_out = true;
} else {
result = 0;
*carry_out = false;
}
} else {
result >>= (shift_amount - 1);
*carry_out = (result & 1) == 1;
result >>= 1;
}
break;
}
case LSL: {
if (shift_amount == 0) {
*carry_out = c_flag_;
} else {
result <<= (shift_amount - 1);
*carry_out = (result < 0);
result <<= 1;
}
break;
}
case LSR: {
if (shift_amount == 0) {
result = 0;
*carry_out = c_flag_;
} else {
uint32_t uresult = static_cast<uint32_t>(result);
uresult >>= (shift_amount - 1);
*carry_out = (uresult & 1) == 1;
uresult >>= 1;
result = static_cast<int32_t>(uresult);
}
break;
}
case ROR: {
UnimplementedInstruction(instr);
break;
}
default: {
UNREACHABLE();
break;
}
}
} else {
// by register
Register rs = instr->RsField();
shift_amount = get_register(rs) & 0xff;
switch (shift) {
case ASR: {
if (shift_amount == 0) {
*carry_out = c_flag_;
} else if (shift_amount < 32) {
result >>= (shift_amount - 1);
*carry_out = (result & 1) == 1;
result >>= 1;
} else {
ASSERT(shift_amount >= 32);
if (result < 0) {
*carry_out = true;
result = 0xffffffff;
} else {
*carry_out = false;
result = 0;
}
}
break;
}
case LSL: {
if (shift_amount == 0) {
*carry_out = c_flag_;
} else if (shift_amount < 32) {
result <<= (shift_amount - 1);
*carry_out = (result < 0);
result <<= 1;
} else if (shift_amount == 32) {
*carry_out = (result & 1) == 1;
result = 0;
} else {
ASSERT(shift_amount > 32);
*carry_out = false;
result = 0;
}
break;
}
case LSR: {
if (shift_amount == 0) {
*carry_out = c_flag_;
} else if (shift_amount < 32) {
uint32_t uresult = static_cast<uint32_t>(result);
uresult >>= (shift_amount - 1);
*carry_out = (uresult & 1) == 1;
uresult >>= 1;
result = static_cast<int32_t>(uresult);
} else if (shift_amount == 32) {
*carry_out = (result < 0);
result = 0;
} else {
*carry_out = false;
result = 0;
}
break;
}
case ROR: {
UnimplementedInstruction(instr);
break;
}
default: {
UNREACHABLE();
break;
}
}
}
return result;
}
// Addressing Mode 1 - Data-processing operands:
// Get the value based on the shifter_operand with immediate.
DART_FORCE_INLINE int32_t Simulator::GetImm(Instr* instr, bool* carry_out) {
int rotate = instr->RotateField() * 2;
int immed8 = instr->Immed8Field();
int imm = (immed8 >> rotate) | (immed8 << (32 - rotate));
*carry_out = (rotate == 0) ? c_flag_ : (imm < 0);
return imm;
}
// Addressing Mode 4 - Load and Store Multiple
void Simulator::HandleRList(Instr* instr, bool load) {
Register rn = instr->RnField();
int32_t rn_val = get_register(rn);
int rlist = instr->RlistField();
int num_regs = Utils::CountOneBits32(static_cast<uint32_t>(rlist));
uword address = 0;
uword end_address = 0;
switch (instr->PUField()) {
case 0: {
// Print("da");
address = rn_val - (num_regs * 4) + 4;
end_address = rn_val + 4;
rn_val = rn_val - (num_regs * 4);
break;
}
case 1: {
// Print("ia");
address = rn_val;
end_address = rn_val + (num_regs * 4);
rn_val = rn_val + (num_regs * 4);
break;
}
case 2: {
// Print("db");
address = rn_val - (num_regs * 4);
end_address = rn_val;
rn_val = address;
break;
}
case 3: {
// Print("ib");
address = rn_val + 4;
end_address = rn_val + (num_regs * 4) + 4;
rn_val = rn_val + (num_regs * 4);
break;
}
default: {
UNREACHABLE();
break;
}
}
if (IsIllegalAddress(address)) {
HandleIllegalAccess(address, instr);
} else {
if (instr->HasW()) {
set_register(rn, rn_val);
}
int reg = 0;
while (rlist != 0) {
if ((rlist & 1) != 0) {
if (load) {
set_register(static_cast<Register>(reg), ReadW(address, instr));
} else {
WriteW(address, get_register(static_cast<Register>(reg)), instr);
}
address += 4;
}
reg++;
rlist >>= 1;
}
ASSERT(end_address == address);
}
}
// 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,
int32_t r4);
// 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::SupervisorCall(Instr* instr) {
int svc = instr->SvcField();
switch (svc) {
case Instr::kSimulatorRedirectCode: {
SimulatorSetjmpBuffer buffer(this);
if (!setjmp(buffer.buffer_)) {
int32_t saved_lr = get_register(LR);
Redirection* redirection = Redirection::FromSvcInstruction(instr);
uword external = redirection->external_function();
if (IsTracingExecution()) {
THR_Print("Call to host function at 0x%" Pd "\n", external);
}
if (redirection->call_kind() == kRuntimeCall) {
NativeArguments arguments(NULL, 0, NULL, NULL);
ASSERT(sizeof(NativeArguments) == 4 * kWordSize);
arguments.thread_ = reinterpret_cast<Thread*>(get_register(R0));
arguments.argc_tag_ = get_register(R1);
arguments.argv_ = reinterpret_cast<RawObject**>(get_register(R2));
arguments.retval_ = reinterpret_cast<RawObject**>(get_register(R3));
SimulatorRuntimeCall target =
reinterpret_cast<SimulatorRuntimeCall>(external);
target(arguments);
set_register(R0, icount_); // Zap result register from void function.
set_register(R1, icount_);
} else if (redirection->call_kind() == kLeafRuntimeCall) {
ASSERT((0 <= redirection->argument_count()) &&
(redirection->argument_count() <= 5));
int32_t r0 = get_register(R0);
int32_t r1 = get_register(R1);
int32_t r2 = get_register(R2);
int32_t r3 = get_register(R3);
int32_t r4 = *reinterpret_cast<int32_t*>(get_register(SP));
SimulatorLeafRuntimeCall target =
reinterpret_cast<SimulatorLeafRuntimeCall>(external);
r0 = target(r0, r1, r2, r3, r4);
set_register(R0, r0); // Set returned result from function.
set_register(R1, icount_); // Zap unused result register.
} else if (redirection->call_kind() == kLeafFloatRuntimeCall) {
ASSERT((0 <= redirection->argument_count()) &&
(redirection->argument_count() <= 2));
SimulatorLeafFloatRuntimeCall target =
reinterpret_cast<SimulatorLeafFloatRuntimeCall>(external);
if (TargetCPUFeatures::hardfp_supported()) {
// If we're doing "hardfp", the double arguments are already in the
// floating point registers.
double d0 = get_dregister(D0);
double d1 = get_dregister(D1);
d0 = target(d0, d1);
set_dregister(D0, d0);
} else {
// If we're not doing "hardfp", we must be doing "soft" or "softfp",
// So take the double arguments from the integer registers.
uint32_t r0 = get_register(R0);
int32_t r1 = get_register(R1);
uint32_t r2 = get_register(R2);
int32_t r3 = get_register(R3);
int64_t a0 = Utils::LowHighTo64Bits(r0, r1);
int64_t a1 = Utils::LowHighTo64Bits(r2, r3);
double d0 = bit_cast<double, int64_t>(a0);
double d1 = bit_cast<double, int64_t>(a1);
d0 = target(d0, d1);
a0 = bit_cast<int64_t, double>(d0);
r0 = Utils::Low32Bits(a0);
r1 = Utils::High32Bits(a0);
set_register(R0, r0);
set_register(R1, r1);
}
} else if (redirection->call_kind() == kBootstrapNativeCall) {
ASSERT(redirection->argument_count() == 1);
NativeArguments* arguments;
arguments = reinterpret_cast<NativeArguments*>(get_register(R0));
SimulatorBootstrapNativeCall target =
reinterpret_cast<SimulatorBootstrapNativeCall>(external);
target(arguments);
set_register(R0, icount_); // Zap result register from void function.
} else {
ASSERT(redirection->call_kind() == kNativeCall);
NativeArguments* arguments;
arguments = reinterpret_cast<NativeArguments*>(get_register(R0));
uword target_func = get_register(R1);
SimulatorNativeCall target =
reinterpret_cast<SimulatorNativeCall>(external);
target(arguments, target_func);
set_register(R0, icount_); // Zap result register from void function.
set_register(R1, icount_);
}
// Zap caller-saved registers, since the actual runtime call could have
// used them.
set_register(R2, icount_);
set_register(R3, icount_);
set_register(IP, icount_);
set_register(LR, icount_);
if (TargetCPUFeatures::vfp_supported()) {
double zap_dvalue = static_cast<double>(icount_);
// Do not zap D0, as it may contain a float result.
for (int i = D1; i <= D7; i++) {
set_dregister(static_cast<DRegister>(i), zap_dvalue);
}
// The above loop also zaps overlapping registers S2-S15.
// Registers D8-D15 (overlapping with S16-S31) are preserved.
#if defined(VFPv3_D32)
for (int i = D16; i <= D31; i++) {
set_dregister(static_cast<DRegister>(i), zap_dvalue);
}
#endif
}
// Return.
set_pc(saved_lr);
} else {
// Coming via long jump from a throw. Continue to exception handler.
}
break;
}
case Instr::kSimulatorBreakCode: {
SimulatorDebugger dbg(this);
dbg.Stop(instr, "breakpoint");
break;
}
default: {
UNREACHABLE();
break;
}
}
}
// Handle execution based on instruction types.
// Instruction types 0 and 1 are both rolled into one function because they
// only differ in the handling of the shifter_operand.
DART_FORCE_INLINE void Simulator::DecodeType01(Instr* instr) {
if (!instr->IsDataProcessing()) {
// miscellaneous, multiply, sync primitives, extra loads and stores.
if (instr->IsMiscellaneous()) {
switch (instr->Bits(4, 3)) {
case 1: {
if (instr->Bits(21, 2) == 0x3) {
// Format(instr, "clz'cond 'rd, 'rm");
Register rm = instr->RmField();
Register rd = instr->RdField();
int32_t rm_val = get_register(rm);
int32_t rd_val = 0;
if (rm_val != 0) {
while (rm_val > 0) {
rd_val++;
rm_val <<= 1;
}
} else {
rd_val = 32;
}
set_register(rd, rd_val);
} else {
ASSERT(instr->Bits(21, 2) == 0x1);
// Format(instr, "bx'cond 'rm");
Register rm = instr->RmField();
int32_t rm_val = get_register(rm);
set_pc(rm_val);
}
break;
}
case 3: {
ASSERT(instr->Bits(21, 2) == 0x1);
// Format(instr, "blx'cond 'rm");
Register rm = instr->RmField();
int32_t rm_val = get_register(rm);
intptr_t pc = get_pc();
set_register(LR, pc + Instr::kInstrSize);
set_pc(rm_val);
break;
}
case 7: {
if ((instr->Bits(21, 2) == 0x1) && (instr->ConditionField() == AL)) {
// Format(instr, "bkpt #'imm12_4");
SimulatorDebugger dbg(this);
int32_t imm = instr->BkptField();
char buffer[32];
snprintf(buffer, sizeof(buffer), "bkpt #0x%x", imm);
set_pc(get_pc() + Instr::kInstrSize);
dbg.Stop(instr, buffer);
} else {
// Format(instr, "smc'cond");
UnimplementedInstruction(instr);
}
break;
}
default: {
UnimplementedInstruction(instr);
break;
}
}
} else if (instr->IsMultiplyOrSyncPrimitive()) {
if (instr->Bit(24) == 0) {
// multiply instructions.
Register rn = instr->RnField();
Register rd = instr->RdField();
Register rs = instr->RsField();
Register rm = instr->RmField();
int32_t rm_val = get_register(rm);
int32_t rs_val = get_register(rs);
int32_t rd_val = 0;
switch (instr->Bits(21, 3)) {
case 1:
// Registers rd, rn, rm, ra are encoded as rn, rm, rs, rd.
// Format(instr, "mla'cond's 'rn, 'rm, 'rs, 'rd");
case 3: {
// Registers rd, rn, rm, ra are encoded as rn, rm, rs, rd.
// Format(instr, "mls'cond's 'rn, 'rm, 'rs, 'rd");
rd_val = get_register(rd);
FALL_THROUGH;
}
case 0: {
// Registers rd, rn, rm are encoded as rn, rm, rs.
// Format(instr, "mul'cond's 'rn, 'rm, 'rs");
int32_t alu_out = rm_val * rs_val;
if (instr->Bits(21, 3) == 3) { // mls
if (TargetCPUFeatures::arm_version() != ARMv7) {
UnimplementedInstruction(instr);
break;
}
alu_out = -alu_out;
}
alu_out += rd_val;
set_register(rn, alu_out);
if (instr->HasS()) {
SetNZFlags(alu_out);
}
break;
}
case 4:
// Registers rd_lo, rd_hi, rn, rm are encoded as rd, rn, rm, rs.
// Format(instr, "umull'cond's 'rd, 'rn, 'rm, 'rs");
case 6: {
// Registers rd_lo, rd_hi, rn, rm are encoded as rd, rn, rm, rs.
// Format(instr, "smull'cond's 'rd, 'rn, 'rm, 'rs");
int64_t result;
if (instr->Bits(21, 3) == 4) { // umull
uint64_t left_op = static_cast<uint32_t>(rm_val);
uint64_t right_op = static_cast<uint32_t>(rs_val);
result = left_op * right_op; // Unsigned multiplication.
} else { // smull
int64_t left_op = static_cast<int32_t>(rm_val);
int64_t right_op = static_cast<int32_t>(rs_val);
result = left_op * right_op; // Signed multiplication.
}
int32_t hi_res = Utils::High32Bits(result);
int32_t lo_res = Utils::Low32Bits(result);
set_register(rd, lo_res);
set_register(rn, hi_res);
if (instr->HasS()) {
if (lo_res != 0) {
// Collapse bits 0..31 into bit 32 so that 32-bit Z check works.
hi_res |= 1;
}
ASSERT((result == 0) == (hi_res == 0)); // Z bit
ASSERT(((result & (1LL << 63)) != 0) == (hi_res < 0)); // N bit
SetNZFlags(hi_res);
}
break;
}
case 2:
// Registers rd_lo, rd_hi, rn, rm are encoded as rd, rn, rm, rs.
// Format(instr, "umaal'cond's 'rd, 'rn, 'rm, 'rs");
FALL_THROUGH;
case 5:
// Registers rd_lo, rd_hi, rn, rm are encoded as rd, rn, rm, rs.
// Format(instr, "umlal'cond's 'rd, 'rn, 'rm, 'rs");
FALL_THROUGH;
case 7: {
// Registers rd_lo, rd_hi, rn, rm are encoded as rd, rn, rm, rs.
// Format(instr, "smlal'cond's 'rd, 'rn, 'rm, 'rs");
int32_t rd_lo_val = get_register(rd);
int32_t rd_hi_val = get_register(rn);
uint32_t accum_lo = static_cast<uint32_t>(rd_lo_val);
int32_t accum_hi = static_cast<int32_t>(rd_hi_val);
int64_t accum = Utils::LowHighTo64Bits(accum_lo, accum_hi);
int64_t result;
if (instr->Bits(21, 3) == 5) { // umlal
uint64_t left_op = static_cast<uint32_t>(rm_val);
uint64_t right_op = static_cast<uint32_t>(rs_val);
result = accum + left_op * right_op; // Unsigned multiplication.
} else if (instr->Bits(21, 3) == 7) { // smlal
int64_t left_op = static_cast<int32_t>(rm_val);
int64_t right_op = static_cast<int32_t>(rs_val);
result = accum + left_op * right_op; // Signed multiplication.
} else {
ASSERT(instr->Bits(21, 3) == 2); // umaal
ASSERT(!instr->HasS());
uint64_t left_op = static_cast<uint32_t>(rm_val);
uint64_t right_op = static_cast<uint32_t>(rs_val);
result = left_op * right_op + // Unsigned multiplication.
static_cast<uint32_t>(rd_lo_val) +
static_cast<uint32_t>(rd_hi_val);
}
int32_t hi_res = Utils::High32Bits(result);
int32_t lo_res = Utils::Low32Bits(result);
set_register(rd, lo_res);
set_register(rn, hi_res);
if (instr->HasS()) {
if (lo_res != 0) {
// Collapse bits 0..31 into bit 32 so that 32-bit Z check works.
hi_res |= 1;
}
ASSERT((result == 0) == (hi_res == 0)); // Z bit
ASSERT(((result & (1LL << 63)) != 0) == (hi_res < 0)); // N bit
SetNZFlags(hi_res);
}
break;
}
default: {
UnimplementedInstruction(instr);
break;
}
}
} else {
// synchronization primitives
Register rd = instr->RdField();
Register rn = instr->RnField();
uword addr = get_register(rn);
switch (instr->Bits(20, 4)) {
case 8: {
// Format(instr, "strex'cond 'rd, 'rm, ['rn]");
if (IsIllegalAddress(addr)) {
HandleIllegalAccess(addr, instr);
} else {
Register rm = instr->RmField();
set_register(rd, WriteExclusiveW(addr, get_register(rm), instr));
}
break;
}
case 9: {
// Format(instr, "ldrex'cond 'rd, ['rn]");
if (IsIllegalAddress(addr)) {
HandleIllegalAccess(addr, instr);
} else {
set_register(rd, ReadExclusiveW(addr, instr));
}
break;
}
default: {
UnimplementedInstruction(instr);
break;
}
}
}
} else if (instr->Bit(25) == 1) {
// 16-bit immediate loads, msr (immediate), and hints
switch (instr->Bits(20, 5)) {
case 16:
case 20: {
if (TargetCPUFeatures::arm_version() == ARMv7) {
uint16_t imm16 = instr->MovwField();
Register rd = instr->RdField();
if (instr->Bit(22) == 0) {
// Format(instr, "movw'cond 'rd, #'imm4_12");
set_register(rd, imm16);
} else {
// Format(instr, "movt'cond 'rd, #'imm4_12");
set_register(rd, (get_register(rd) & 0xffff) | (imm16 << 16));
}
} else {
UnimplementedInstruction(instr);
}
break;
}
case 18: {
if ((instr->Bits(16, 4) == 0) && (instr->Bits(0, 8) == 0)) {
// Format(instr, "nop'cond");
} else {
UnimplementedInstruction(instr);
}
break;
}
default: {
UnimplementedInstruction(instr);
break;
}
}
} else {
// extra load/store instructions
Register rd = instr->RdField();
Register rn = instr->RnField();
int32_t rn_val = get_register(rn);
uword addr = 0;
bool write_back = false;
if (instr->Bit(22) == 0) {
Register rm = instr->RmField();
int32_t rm_val = get_register(rm);
switch (instr->PUField()) {
case 0: {
// Format(instr, "'memop'cond'x 'rd2, ['rn], -'rm");
ASSERT(!instr->HasW());
addr = rn_val;
rn_val -= rm_val;
write_back = true;
break;
}
case 1: {
// Format(instr, "'memop'cond'x 'rd2, ['rn], +'rm");
ASSERT(!instr->HasW());
addr = rn_val;
rn_val += rm_val;
write_back = true;
break;
}
case 2: {
// Format(instr, "'memop'cond'x 'rd2, ['rn, -'rm]'w");
rn_val -= rm_val;
addr = rn_val;
write_back = instr->HasW();
break;
}
case 3: {
// Format(instr, "'memop'cond'x 'rd2, ['rn, +'rm]'w");
rn_val += rm_val;
addr = rn_val;
write_back = instr->HasW();
break;
}
default: {
// The PU field is a 2-bit field.
UNREACHABLE();
break;
}
}
} else {
int32_t imm_val = (instr->ImmedHField() << 4) | instr->ImmedLField();
switch (instr->PUField()) {
case 0: {
// Format(instr, "'memop'cond'x 'rd2, ['rn], #-'off8");
ASSERT(!instr->HasW());
addr = rn_val;
rn_val -= imm_val;
write_back = true;
break;
}
case 1: {
// Format(instr, "'memop'cond'x 'rd2, ['rn], #+'off8");
ASSERT(!instr->HasW());
addr = rn_val;
rn_val += imm_val;
write_back = true;
break;
}
case 2: {
// Format(instr, "'memop'cond'x 'rd2, ['rn, #-'off8]'w");
rn_val -= imm_val;
addr = rn_val;
write_back = instr->HasW();
break;
}
case 3: {
// Format(instr, "'memop'cond'x 'rd2, ['rn, #+'off8]'w");
rn_val += imm_val;
addr = rn_val;
write_back = instr->HasW();
break;
}
default: {
// The PU field is a 2-bit field.
UNREACHABLE();
break;
}
}
}
if (IsIllegalAddress(addr)) {
HandleIllegalAccess(addr, instr);
} else {
if (write_back) {
ASSERT(rd != rn); // Unpredictable.
set_register(rn, rn_val);
}
if (!instr->HasSign()) {
if (instr->HasL()) {
uint16_t val = ReadHU(addr, instr);
set_register(rd, val);
} else {
uint16_t val = get_register(rd);
WriteH(addr, val, instr);
}
} else if (instr->HasL()) {
if (instr->HasH()) {
int16_t val = ReadH(addr, instr);
set_register(rd, val);
} else {
int8_t val = ReadB(addr);
set_register(rd, val);
}
} else if ((rd & 1) == 0) {
Register rd1 = static_cast<Register>(rd | 1);
ASSERT(rd1 < kNumberOfCpuRegisters);
if (instr->HasH()) {
int32_t val_low = get_register(rd);
int32_t val_high = get_register(rd1);
WriteW(addr, val_low, instr);
WriteW(addr + 4, val_high, instr);
} else {
int32_t val_low = ReadW(addr, instr);
int32_t val_high = ReadW(addr + 4, instr);
set_register(rd, val_low);
set_register(rd1, val_high);
}
} else {
UnimplementedInstruction(instr);
}
}
}
} else {
Register rd = instr->RdField();
Register rn = instr->RnField();
int32_t rn_val = get_register(rn);
int32_t shifter_operand = 0;
bool shifter_carry_out = 0;
if (instr->TypeField() == 0) {
shifter_operand = GetShiftRm(instr, &shifter_carry_out);
} else {
ASSERT(instr->TypeField() == 1);
shifter_operand = GetImm(instr, &shifter_carry_out);
}
int32_t carry_in;
int32_t alu_out;
switch (instr->OpcodeField()) {
case AND: {
// Format(instr, "and'cond's 'rd, 'rn, 'shift_rm");
// Format(instr, "and'cond's 'rd, 'rn, 'imm");
alu_out = rn_val & shifter_operand;
set_register(rd, alu_out);
if (instr->HasS()) {
SetNZFlags(alu_out);
SetCFlag(shifter_carry_out);
}
break;
}
case EOR: {
// Format(instr, "eor'cond's 'rd, 'rn, 'shift_rm");
// Format(instr, "eor'cond's 'rd, 'rn, 'imm");
alu_out = rn_val ^ shifter_operand;
set_register(rd, alu_out);
if (instr->HasS()) {
SetNZFlags(alu_out);
SetCFlag(shifter_carry_out);
}
break;
}
case SUB: {
// Format(instr, "sub'cond's 'rd, 'rn, 'shift_rm");
// Format(instr, "sub'cond's 'rd, 'rn, 'imm");
alu_out = rn_val - shifter_operand;
set_register(rd, alu_out);
if (instr->HasS()) {
SetNZFlags(alu_out);
SetCFlag(CarryFrom(rn_val, ~shifter_operand, 1));
SetVFlag(OverflowFrom(rn_val, ~shifter_operand, 1));
}
break;
}
case RSB: {
// Format(instr, "rsb'cond's 'rd, 'rn, 'shift_rm");
// Format(instr, "rsb'cond's 'rd, 'rn, 'imm");
alu_out = shifter_operand - rn_val;
set_register(rd, alu_out);
if (instr->HasS()) {
SetNZFlags(alu_out);
SetCFlag(CarryFrom(shifter_operand, ~rn_val, 1));
SetVFlag(OverflowFrom(shifter_operand, ~rn_val, 1));
}
break;
}
case ADD: {
// Format(instr, "add'cond's 'rd, 'rn, 'shift_rm");
// Format(instr, "add'cond's 'rd, 'rn, 'imm");
alu_out = rn_val + shifter_operand;
set_register(rd, alu_out);
if (instr->HasS()) {
SetNZFlags(alu_out);
SetCFlag(CarryFrom(rn_val, shifter_operand, 0));
SetVFlag(OverflowFrom(rn_val, shifter_operand, 0));
}
break;
}
case ADC: {
// Format(instr, "adc'cond's 'rd, 'rn, 'shift_rm");
// Format(instr, "adc'cond's 'rd, 'rn, 'imm");
carry_in = c_flag_ ? 1 : 0;
alu_out = rn_val + shifter_operand + carry_in;
set_register(rd, alu_out);
if (instr->HasS()) {
SetNZFlags(alu_out);
SetCFlag(CarryFrom(rn_val, shifter_operand, carry_in));
SetVFlag(OverflowFrom(rn_val, shifter_operand, carry_in));
}
break;
}
case SBC: {
// Format(instr, "sbc'cond's 'rd, 'rn, 'shift_rm");
// Format(instr, "sbc'cond's 'rd, 'rn, 'imm");
carry_in = c_flag_ ? 1 : 0;
alu_out = rn_val + ~shifter_operand + carry_in;
set_register(rd, alu_out);
if (instr->HasS()) {
SetNZFlags(alu_out);
SetCFlag(CarryFrom(rn_val, ~shifter_operand, carry_in));
SetVFlag(OverflowFrom(rn_val, ~shifter_operand, carry_in));
}
break;
}
case RSC: {
// Format(instr, "rsc'cond's 'rd, 'rn, 'shift_rm");
// Format(instr, "rsc'cond's 'rd, 'rn, 'imm");
carry_in = c_flag_ ? 1 : 0;
alu_out = shifter_operand + ~rn_val + carry_in;
set_register(rd, alu_out);
if (instr->HasS()) {
SetNZFlags(alu_out);
SetCFlag(CarryFrom(shifter_operand, ~rn_val, carry_in));
SetVFlag(OverflowFrom(shifter_operand, ~rn_val, carry_in));
}
break;
}
case TST: {
if (instr->HasS()) {
// Format(instr, "tst'cond 'rn, 'shift_rm");
// Format(instr, "tst'cond 'rn, 'imm");
alu_out = rn_val & shifter_operand;
SetNZFlags(alu_out);
SetCFlag(shifter_carry_out);
} else {
UnimplementedInstruction(instr);
}
break;
}
case TEQ: {
if (instr->HasS()) {
// Format(instr, "teq'cond 'rn, 'shift_rm");
// Format(instr, "teq'cond 'rn, 'imm");
alu_out = rn_val ^ shifter_operand;
SetNZFlags(alu_out);
SetCFlag(shifter_carry_out);
} else {
UnimplementedInstruction(instr);
}
break;
}
case CMP: {
if (instr->HasS()) {
// Format(instr, "cmp'cond 'rn, 'shift_rm");
// Format(instr, "cmp'cond 'rn, 'imm");
alu_out = rn_val - shifter_operand;
SetNZFlags(alu_out);
SetCFlag(CarryFrom(rn_val, ~shifter_operand, 1));
SetVFlag(OverflowFrom(rn_val, ~shifter_operand, 1));
} else {
UnimplementedInstruction(instr);
}
break;
}
case CMN: {
if (instr->HasS()) {
// Format(instr, "cmn'cond 'rn, 'shift_rm");
// Format(instr, "cmn'cond 'rn, 'imm");
alu_out = rn_val + shifter_operand;
SetNZFlags(alu_out);
SetCFlag(CarryFrom(rn_val, shifter_operand, 0));
SetVFlag(OverflowFrom(rn_val, shifter_operand, 0));
} else {
UnimplementedInstruction(instr);
}
break;
}
case ORR: {
// Format(instr, "orr'cond's 'rd, 'rn, 'shift_rm");
// Format(instr, "orr'cond's 'rd, 'rn, 'imm");
alu_out = rn_val | shifter_operand;
set_register(rd, alu_out);
if (instr->HasS()) {
SetNZFlags(alu_out);
SetCFlag(shifter_carry_out);
}
break;
}
case MOV: {
// Format(instr, "mov'cond's 'rd, 'shift_rm");
// Format(instr, "mov'cond's 'rd, 'imm");
alu_out = shifter_operand;
set_register(rd, alu_out);
if (instr->HasS()) {
SetNZFlags(alu_out);
SetCFlag(shifter_carry_out);
}
break;
}
case BIC: {
// Format(instr, "bic'cond's 'rd, 'rn, 'shift_rm");
// Format(instr, "bic'cond's 'rd, 'rn, 'imm");
alu_out = rn_val & ~shifter_operand;
set_register(rd, alu_out);
if (instr->HasS()) {
SetNZFlags(alu_out);
SetCFlag(shifter_carry_out);
}
break;
}
case MVN: {
// Format(instr, "mvn'cond's 'rd, 'shift_rm");
// Format(instr, "mvn'cond's 'rd, 'imm");
alu_out = ~shifter_operand;
set_register(rd, alu_out);
if (instr->HasS()) {
SetNZFlags(alu_out);
SetCFlag(shifter_carry_out);
}
break;
}
default: {
UNREACHABLE();
break;
}
}
}
}
DART_FORCE_INLINE void Simulator::DecodeType2(Instr* instr) {
Register rd = instr->RdField();
Register rn = instr->RnField();
int32_t rn_val = get_register(rn);
int32_t im_val = instr->Offset12Field();
uword addr = 0;
bool write_back = false;
switch (instr->PUField()) {
case 0: {
// Format(instr, "'memop'cond'b 'rd, ['rn], #-'off12");
ASSERT(!instr->HasW());
addr = rn_val;
rn_val -= im_val;
write_back = true;
break;
}
case 1: {
// Format(instr, "'memop'cond'b 'rd, ['rn], #+'off12");
ASSERT(!instr->HasW());
addr = rn_val;
rn_val += im_val;
write_back = true;
break;
}
case 2: {
// Format(instr, "'memop'cond'b 'rd, ['rn, #-'off12]'w");
rn_val -= im_val;
addr = rn_val;
write_back = instr->HasW();
break;
}
case 3: {
// Format(instr, "'memop'cond'b 'rd, ['rn, #+'off12]'w");
rn_val += im_val;
addr = rn_val;
write_back = instr->HasW();
break;
}
default: {
UNREACHABLE();
break;
}
}
if (IsIllegalAddress(addr)) {
HandleIllegalAccess(addr, instr);
} else {
if (write_back) {
ASSERT(rd != rn); // Unpredictable.
set_register(rn, rn_val);
}
if (instr->HasB()) {
if (instr->HasL()) {
unsigned char val = ReadBU(addr);
set_register(rd, val);
} else {
unsigned char val = get_register(rd);
WriteB(addr, val);
}
} else {
if (instr->HasL()) {
set_register(rd, ReadW(addr, instr));
} else {
WriteW(addr, get_register(rd), instr);
}
}
}
}
void Simulator::DoDivision(Instr* instr) {
const Register rd = instr->DivRdField();
const Register rn = instr->DivRnField();
const Register rm = instr->DivRmField();
if (!TargetCPUFeatures::integer_division_supported()) {
UnimplementedInstruction(instr);
return;
}
// ARMv7-a does not trap on divide-by-zero. The destination register is just
// set to 0.
if (get_register(rm) == 0) {
set_register(rd, 0);
return;
}
if (instr->Bit(21) == 1) {
// unsigned division.
uint32_t rn_val = static_cast<uint32_t>(get_register(rn));
uint32_t rm_val = static_cast<uint32_t>(get_register(rm));
uint32_t result = rn_val / rm_val;
set_register(rd, static_cast<int32_t>(result));
} else {
// signed division.
int32_t rn_val = get_register(rn);
int32_t rm_val = get_register(rm);
int32_t result;
if ((rn_val == static_cast<int32_t>(0x80000000)) &&
(rm_val == static_cast<int32_t>(0xffffffff))) {
result = 0x80000000;
} else {
result = rn_val / rm_val;
}
set_register(rd, result);
}
}
void Simulator::DecodeType3(Instr* instr) {
if (instr->IsDivision()) {
DoDivision(instr);
return;
} else if (instr->IsRbit()) {
// Format(instr, "rbit'cond 'rd, 'rm");
Register rm = instr->RmField();
Register rd = instr->RdField();
set_register(rd, Utils::ReverseBits32(get_register(rm)));
return;
}
Register rd = instr->RdField();
Register rn = instr->RnField();
int32_t rn_val = get_register(rn);
bool shifter_carry_out = 0;
int32_t shifter_operand = GetShiftRm(instr, &shifter_carry_out);
uword addr = 0;
bool write_back = false;
switch (instr->PUField()) {
case 0: {
// Format(instr, "'memop'cond'b 'rd, ['rn], -'shift_rm");
ASSERT(!instr->HasW());
addr = rn_val;
rn_val -= shifter_operand;
write_back = true;
break;
}
case 1: {
// Format(instr, "'memop'cond'b 'rd, ['rn], +'shift_rm");
ASSERT(!instr->HasW());
addr = rn_val;
rn_val += shifter_operand;
write_back = true;
break;
}
case 2: {
// Format(instr, "'memop'cond'b 'rd, ['rn, -'shift_rm]'w");
rn_val -= shifter_operand;
addr = rn_val;
write_back = instr->HasW();
break;
}
case 3: {
// Format(instr, "'memop'cond'b 'rd, ['rn, +'shift_rm]'w");
rn_val += shifter_operand;
addr = rn_val;
write_back = instr->HasW();
break;
}
default: {
UNREACHABLE();
break;
}
}
if (IsIllegalAddress(addr)) {
HandleIllegalAccess(addr, instr);
} else {
if (write_back) {
ASSERT(rd != rn); // Unpredictable.
set_register(rn, rn_val);
}
if (instr->HasB()) {
if (instr->HasL()) {
unsigned char val = ReadBU(addr);
set_register(rd, val);
} else {
unsigned char val = get_register(rd);
WriteB(addr, val);
}
} else {
if (instr->HasL()) {
set_register(rd, ReadW(addr, instr));
} else {
WriteW(addr, get_register(rd), instr);
}
}
}
}
void Simulator::DecodeType4(Instr* instr) {
ASSERT(instr->Bit(22) == 0); // only allowed to be set in privileged mode
if (instr->HasL()) {
// Format(instr, "ldm'cond'pu 'rn'w, 'rlist");
HandleRList(instr, true);
} else {
// Format(instr, "stm'cond'pu 'rn'w, 'rlist");
HandleRList(instr, false);
}
}
void Simulator::DecodeType5(Instr* instr) {
// Format(instr, "b'l'cond 'target");
int off = (instr->SImmed24Field() << 2) + 8;
intptr_t pc = get_pc();
if (instr->HasLink()) {
set_register(LR, pc + Instr::kInstrSize);
}
set_pc(pc + off);
}
void Simulator::DecodeType6(Instr* instr) {
if (instr->IsVFPDoubleTransfer()) {
Register rd = instr->RdField();
Register rn = instr->RnField();
if (instr->Bit(8) == 0) {
SRegister sm = instr->SmField();
SRegister sm1 = static_cast<SRegister>(sm + 1);
ASSERT(sm1 < kNumberOfSRegisters);
if (instr->Bit(20) == 1) {
// Format(instr, "vmovrrs'cond 'rd, 'rn, {'sm', 'sm1}");
set_register(rd, get_sregister_bits(sm));
set_register(rn, get_sregister_bits(sm1));
} else {
// Format(instr, "vmovsrr'cond {'sm, 'sm1}, 'rd', 'rn");
set_sregister_bits(sm, get_register(rd));
set_sregister_bits(sm1, get_register(rn));
}
} else {
DRegister dm = instr->DmField();
if (instr->Bit(20) == 1) {
// Format(instr, "vmovrrd'cond 'rd, 'rn, 'dm");
int64_t dm_val = get_dregister_bits(dm);
set_register(rd, Utils::Low32Bits(dm_val));
set_register(rn, Utils::High32Bits(dm_val));
} else {
// Format(instr, "vmovdrr'cond 'dm, 'rd, 'rn");
int64_t dm_val =
Utils::LowHighTo64Bits(get_register(rd), get_register(rn));
set_dregister_bits(dm, dm_val);
}
}
} else if (instr->IsVFPLoadStore()) {
Register rn = instr->RnField();
int32_t addr = get_register(rn);
int32_t imm_val = instr->Bits(0, 8) << 2;
if (instr->Bit(23) == 1) {
addr += imm_val;
} else {
addr -= imm_val;
}
if (IsIllegalAddress(addr)) {
HandleIllegalAccess(addr, instr);
} else {
if (instr->Bit(8) == 0) {
SRegister sd = instr->SdField();
if (instr->Bit(20) == 1) { // vldrs
// Format(instr, "vldrs'cond 'sd, ['rn, #+'off10]");
// Format(instr, "vldrs'cond 'sd, ['rn, #-'off10]");
set_sregister_bits(sd, ReadW(addr, instr));
} else { // vstrs
// Format(instr, "vstrs'cond 'sd, ['rn, #+'off10]");
// Format(instr, "vstrs'cond 'sd, ['rn, #-'off10]");
WriteW(addr, get_sregister_bits(sd), instr);
}
} else {
DRegister dd = instr->DdField();
if (instr->Bit(20) == 1) { // vldrd
// Format(instr, "vldrd'cond 'dd, ['rn, #+'off10]");
// Format(instr, "vldrd'cond 'dd, ['rn, #-'off10]");
int64_t dd_val = Utils::LowHighTo64Bits(ReadW(addr, instr),
ReadW(addr + 4, instr));
set_dregister_bits(dd, dd_val);
} else { // vstrd
// Format(instr, "vstrd'cond 'dd, ['rn, #+'off10]");
// Format(instr, "vstrd'cond 'dd, ['rn, #-'off10]");
int64_t dd_val = get_dregister_bits(dd);
WriteW(addr, Utils::Low32Bits(dd_val), instr);
WriteW(addr + 4, Utils::High32Bits(dd_val), instr);
}
}
}
} else if (instr->IsVFPMultipleLoadStore()) {
Register rn = instr->RnField();
int32_t addr = get_register(rn);
int32_t imm_val = instr->Bits(0, 8);
if (instr->Bit(23) == 0) {
addr -= (imm_val << 2);
}
if (instr->HasW()) {
if (instr->Bit(23) == 1) {
set_register(rn, addr + (imm_val << 2));
} else {
set_register(rn, addr); // already subtracted from addr
}
}
if (IsIllegalAddress(addr)) {
HandleIllegalAccess(addr, instr);
} else {
if (instr->Bit(8) == 0) {
int32_t regs_cnt = imm_val;
int32_t start = instr->Bit(22) | (instr->Bits(12, 4) << 1);
for (int i = start; i < start + regs_cnt; i++) {
SRegister sd = static_cast<SRegister>(i);
if (instr->Bit(20) == 1) {
// Format(instr, "vldms'cond'pu 'rn'w, 'slist");
set_sregister_bits(sd, ReadW(addr, instr));
} else {
// Format(instr, "vstms'cond'pu 'rn'w, 'slist");
WriteW(addr, get_sregister_bits(sd), instr);
}
addr += 4;
}
} else {
int32_t regs_cnt = imm_val >> 1;
int32_t start = (instr->Bit(22) << 4) | instr->Bits(12, 4);
if ((regs_cnt <= 16) && (start + regs_cnt <= kNumberOfDRegisters)) {
for (int i = start; i < start + regs_cnt; i++) {
DRegister dd = static_cast<DRegister>(i);
if (instr->Bit(20) == 1) {
// Format(instr, "vldmd'cond'pu 'rn'w, 'dlist");
int64_t dd_val = Utils::LowHighTo64Bits(ReadW(addr, instr),
ReadW(addr + 4, instr));
set_dregister_bits(dd, dd_val);
} else {
// Format(instr, "vstmd'cond'pu 'rn'w, 'dlist");
int64_t dd_val = get_dregister_bits(dd);
WriteW(addr, Utils::Low32Bits(dd_val), instr);
WriteW(addr + 4, Utils::High32Bits(dd_val), instr);
}
addr += 8;
}
} else {
UnimplementedInstruction(instr);
}
}
}
} else {
UnimplementedInstruction(instr);
}
}
void Simulator::DecodeType7(Instr* instr) {
if (instr->Bit(24) == 1) {
// Format(instr, "svc #'svc");
SupervisorCall(instr);
} else if (instr->IsVFPDataProcessingOrSingleTransfer()) {
if (instr->Bit(4) == 0) {
// VFP Data Processing
SRegister sd;
SRegister sn;
SRegister sm;
DRegister dd;
DRegister dn;
DRegister dm;
if (instr->Bit(8) == 0) {
sd = instr->SdField();
sn = instr->SnField();
sm = instr->SmField();
dd = kNoDRegister;
dn = kNoDRegister;
dm = kNoDRegister;
} else {
sd = kNoSRegister;
sn = kNoSRegister;
sm = kNoSRegister;
dd = instr->DdField();
dn = instr->DnField();
dm = instr->DmField();
}
switch (instr->Bits(20, 4) & 0xb) {
case 1: // vnmla, vnmls, vnmul
default: {
UnimplementedInstruction(instr);
break;
}
case 0: { // vmla, vmls floating-point
if (instr->Bit(8) == 0) {
float addend = get_sregister(sn) * get_sregister(sm);
float sd_val = get_sregister(sd);
if (instr->Bit(6) == 0) {
// Format(instr, "vmlas'cond 'sd, 'sn, 'sm");
} else {
// Format(instr, "vmlss'cond 'sd, 'sn, 'sm");
addend = -addend;
}
set_sregister(sd, sd_val + addend);
} else {
double addend = get_dregister(dn) * get_dregister(dm);
double dd_val = get_dregister(dd);
if (instr->Bit(6) == 0) {
// Format(instr, "vmlad'cond 'dd, 'dn, 'dm");
} else {
// Format(instr, "vmlsd'cond 'dd, 'dn, 'dm");
addend = -addend;
}
set_dregister(dd, dd_val + addend);
}
break;
}
case 2: { // vmul
if (instr->Bit(8) == 0) {
// Format(instr, "vmuls'cond 'sd, 'sn, 'sm");
set_sregister(sd, get_sregister(sn) * get_sregister(sm));
} else {
// Format(instr, "vmuld'cond 'dd, 'dn, 'dm");
set_dregister(dd, get_dregister(dn) * get_dregister(dm));
}
break;
}
case 8: { // vdiv
if (instr->Bit(8) == 0) {
// Format(instr, "vdivs'cond 'sd, 'sn, 'sm");
set_sregister(sd, get_sregister(sn) / get_sregister(sm));
} else {
// Format(instr, "vdivd'cond 'dd, 'dn, 'dm");
set_dregister(dd, get_dregister(dn) / get_dregister(dm));
}
break;
}
case 3: { // vadd, vsub floating-point
if (instr->Bit(8) == 0) {
if (instr->Bit(6) == 0) {
// Format(instr, "vadds'cond 'sd, 'sn, 'sm");
set_sregister(sd, get_sregister(sn) + get_sregister(sm));
} else {
// Format(instr, "vsubs'cond 'sd, 'sn, 'sm");
set_sregister(sd, get_sregister(sn) - get_sregister(sm));
}
} else {
if (instr->Bit(6) == 0) {
// Format(instr, "vaddd'cond 'dd, 'dn, 'dm");
set_dregister(dd, get_dregister(dn) + get_dregister(dm));
} else {
// Format(instr, "vsubd'cond 'dd, 'dn, 'dm");
set_dregister(dd, get_dregister(dn) - get_dregister(dm));
}
}
break;
}
case 0xb: { // Other VFP data-processing instructions
if (instr->Bit(6) == 0) { // vmov immediate
if (instr->Bit(8) == 0) {
// Format(instr, "vmovs'cond 'sd, #'immf");
set_sregister(sd, instr->ImmFloatField());
} else {
// Format(instr, "vmovd'cond 'dd, #'immd");
set_dregister(dd, instr->ImmDoubleField());
}
break;
}
switch (instr->Bits(16, 4)) {
case 0: { // vmov immediate, vmov register, vabs
switch (instr->Bits(6, 2)) {
case 1: { // vmov register
if (instr->Bit(8) == 0) {
// Format(instr, "vmovs'cond 'sd, 'sm");
set_sregister(sd, get_sregister(sm));
} else {
// Format(instr, "vmovd'cond 'dd, 'dm");
set_dregister(dd, get_dregister(dm));
}
break;
}
case 3: { // vabs
if (instr->Bit(8) == 0) {
// Format(instr, "vabss'cond 'sd, 'sm");
set_sregister(sd, fabsf(get_sregister(sm)));
} else {
// Format(instr, "vabsd'cond 'dd, 'dm");
set_dregister(dd, fabs(get_dregister(dm)));
}
break;
}
default: {
UnimplementedInstruction(instr);
break;
}
}
break;
}
case 1: { // vneg, vsqrt
switch (instr->Bits(6, 2)) {
case 1: { // vneg
if (instr->Bit(8) == 0) {
// Format(instr, "vnegs'cond 'sd, 'sm");
set_sregister(sd, -get_sregister(sm));
} else {
// Format(instr, "vnegd'cond 'dd, 'dm");
set_dregister(dd, -get_dregister(dm));
}
break;
}
case 3: { // vsqrt
if (instr->Bit(8) == 0) {
// Format(instr, "vsqrts'cond 'sd, 'sm");
set_sregister(sd, sqrtf(get_sregister(sm)));
} else {
// Format(instr, "vsqrtd'cond 'dd, 'dm");
set_dregister(dd, sqrt(get_dregister(dm)));
}
break;
}
default: {
UnimplementedInstruction(instr);
break;
}
}
break;
}
case 4: // vcmp, vcmpe
case 5: { // vcmp #0.0, vcmpe #0.0
if (instr->Bit(7) == 1) { // vcmpe
UnimplementedInstruction(instr);
} else {
fp_n_flag_ = false;
fp_z_flag_ = false;
fp_c_flag_ = false;
fp_v_flag_ = false;
if (instr->Bit(8) == 0) { // vcmps
float sd_val = get_sregister(sd);
float sm_val;
if (instr->Bit(16) == 0) {
// Format(instr, "vcmps'cond 'sd, 'sm");
sm_val = get_sregister(sm);
} else {
// Format(instr, "vcmps'cond 'sd, #0.0");
sm_val = 0.0f;
}
if (isnan(sd_val) || isnan(sm_val)) {
fp_c_flag_ = true;
fp_v_flag_ = true;
} else if (sd_val == sm_val) {
fp_z_flag_ = true;
fp_c_flag_ = true;
} else if (sd_val < sm_val) {
fp_n_flag_ = true;
} else {
fp_c_flag_ = true;
}
} else { // vcmpd
double dd_val = get_dregister(dd);
double dm_val;
if (instr->Bit(16) == 0) {
// Format(instr, "vcmpd'cond 'dd, 'dm");
dm_val = get_dregister(dm);
} else {
// Format(instr, "vcmpd'cond 'dd, #0.0");
dm_val = 0.0;
}
if (isnan(dd_val) || isnan(dm_val)) {
fp_c_flag_ = true;
fp_v_flag_ = true;
} else if (dd_val == dm_val) {
fp_z_flag_ = true;
fp_c_flag_ = true;
} else if (dd_val < dm_val) {
fp_n_flag_ = true;
} else {
fp_c_flag_ = true;
}
}
}
break;
}
case 7: { // vcvt between double-precision and single-precision
if (instr->Bit(8) == 0) {
// Format(instr, "vcvtds'cond 'dd, 'sm");
dd = instr->DdField();
set_dregister(dd, static_cast<double>(get_sregister(sm)));
} else {
// Format(instr, "vcvtsd'cond 'sd, 'dm");
sd = instr->SdField();
set_sregister(sd, static_cast<float>(get_dregister(dm)));
}
break;
}
case 8: { // vcvt, vcvtr between floating-point and integer
sm = instr->SmField();
int32_t sm_int = get_sregister_bits(sm);
uint32_t ud_val = 0;
int32_t id_val = 0;
if (instr->Bit(7) == 0) { // vcvtsu, vcvtdu
ud_val = static_cast<uint32_t>(sm_int);
} else { // vcvtsi, vcvtdi
id_val = sm_int;
}
if (instr->Bit(8) == 0) {
float sd_val;
if (instr->Bit(7) == 0) {
// Format(instr, "vcvtsu'cond 'sd, 'sm");
sd_val = static_cast<float>(ud_val);
} else {
// Format(instr, "vcvtsi'cond 'sd, 'sm");
sd_val = static_cast<float>(id_val);
}
set_sregister(sd, sd_val);
} else {
double dd_val;
if (instr->Bit(7) == 0) {
// Format(instr, "vcvtdu'cond 'dd, 'sm");
dd_val = static_cast<double>(ud_val);
} else {
// Format(instr, "vcvtdi'cond 'dd, 'sm");
dd_val = static_cast<double>(id_val);
}
set_dregister(dd, dd_val);
}
break;
}
case 12:
case 13: { // vcvt, vcvtr between floating-point and integer
// We do not need to record exceptions in the FPSCR cumulative
// flags, because we do not use them.
if (instr->Bit(7) == 0) {
// We only support round-to-zero mode
UnimplementedInstruction(instr);
break;
}
int32_t id_val = 0;
uint32_t ud_val = 0;
if (instr->Bit(8) == 0) {
float sm_val = get_sregister(sm);
if (instr->Bit(16) == 0) {
// Format(instr, "vcvtus'cond 'sd, 'sm");
if (sm_val >= static_cast<float>(INT32_MAX)) {
ud_val = INT32_MAX;
} else if (sm_val > 0.0) {
ud_val = static_cast<uint32_t>(sm_val);
}
} else {
// Format(instr, "vcvtis'cond 'sd, 'sm");
if (sm_val <= static_cast<float>(INT32_MIN)) {
id_val = INT32_MIN;
} else if (sm_val >= static_cast<float>(INT32_MAX)) {
id_val = INT32_MAX;
} else {
id_val = static_cast<int32_t>(sm_val);
}
ASSERT((id_val >= 0) || !(sm_val >= 0.0));
}
} else {
sd = instr->SdField();
double dm_val = get_dregister(dm);
if (instr->Bit(16) == 0) {
// Format(instr, "vcvtud'cond 'sd, 'dm");
if (dm_val >= static_cast<double>(INT32_MAX)) {
ud_val = INT32_MAX;
} else if (dm_val > 0.0) {
ud_val = static_cast<uint32_t>(dm_val);
}
} else {
// Format(instr, "vcvtid'cond 'sd, 'dm");
if (dm_val <= static_cast<double>(INT32_MIN)) {
id_val = INT32_MIN;
} else if (dm_val >= static_cast<double>(INT32_MAX)) {
id_val = INT32_MAX;
} else if (isnan(dm_val)) {
id_val = 0;
} else {
id_val = static_cast<int32_t>(dm_val);
}
ASSERT((id_val >= 0) || !(dm_val >= 0.0));
}
}
int32_t sd_val;
if (instr->Bit(16) == 0) {
sd_val = static_cast<int32_t>(ud_val);
} else {
sd_val = id_val;
}
set_sregister_bits(sd, sd_val);
break;
}
case 2: // vcvtb, vcvtt
case 3: // vcvtb, vcvtt
case 9: // undefined
case 10: // vcvt between floating-point and fixed-point
case 11: // vcvt between floating-point and fixed-point
case 14: // vcvt between floating-point and fixed-point
case 15: // vcvt between floating-point and fixed-point
default: {
UnimplementedInstruction(instr);
break;
}
}
} break;
}
} else {
// 8, 16, or 32-bit Transfer between ARM Core and VFP
if ((instr->Bits(21, 3) == 0) && (instr->Bit(8) == 0)) {
Register rd = instr->RdField();
SRegister sn = instr->SnField();
if (instr->Bit(20) == 0) {
// Format(instr, "vmovs'cond 'sn, 'rd");
set_sregister_bits(sn, get_register(rd));
} else {
// Format(instr, "vmovr'cond 'rd, 'sn");
set_register(rd, get_sregister_bits(sn));
}
} else if ((instr->Bits(22, 3) == 0) && (instr->Bit(20) == 0) &&
(instr->Bit(8) == 1) && (instr->Bits(5, 2) == 0)) {
DRegister dn = instr->DnField();
Register rd = instr->RdField();
if (instr->Bit(21) == 0) {
// Format(instr, "vmovd'cond 'dd[0], 'rd");
SRegister sd = EvenSRegisterOf(dn);
set_sregister_bits(sd, get_register(rd));
} else {
// Format(instr, "vmovd'cond 'dd[1], 'rd");
SRegister sd = OddSRegisterOf(dn);
set_sregister_bits(sd, get_register(rd));
}
} else if ((instr->Bits(20, 4) == 0xf) && (instr->Bit(8) == 0)) {
if (instr->Bits(12, 4) == 0xf) {
// Format(instr, "vmrs'cond APSR, FPSCR");
n_flag_ = fp_n_flag_;
z_flag_ = fp_z_flag_;
c_flag_ = fp_c_flag_;
v_flag_ = fp_v_flag_;
} else {
// Format(instr, "vmrs'cond 'rd, FPSCR");
const int32_t n_flag = fp_n_flag_ ? (1 << 31) : 0;
const int32_t z_flag = fp_z_flag_ ? (1 << 30) : 0;
const int32_t c_flag = fp_c_flag_ ? (1 << 29) : 0;
const int32_t v_flag = fp_v_flag_ ? (1 << 28) : 0;
set_register(instr->RdField(), n_flag | z_flag | c_flag | v_flag);
}
} else {
UnimplementedInstruction(instr);
}
}
} else {
UnimplementedInstruction(instr);
}
}
static void simd_value_swap(simd_value_t* s1,
int i1,
simd_value_t* s2,
int i2) {
uint32_t tmp;
tmp = s1->data_[i1].u;
s1->data_[i1].u = s2->data_[i2].u;
s2->data_[i2].u = tmp;
}
void Simulator::DecodeSIMDDataProcessing(Instr* instr) {
ASSERT(instr->ConditionField() == kSpecialCondition);
if (instr->Bit(6) == 1) {
// Q = 1, Using 128-bit Q registers.
const QRegister qd = instr->QdField();
const QRegister qn = instr->QnField();
const QRegister qm = instr->QmField();
simd_value_t s8d;
simd_value_t s8n;
simd_value_t s8m;
get_qregister(qn, &s8n);
get_qregister(qm, &s8m);
int8_t* s8d_8 = reinterpret_cast<int8_t*>(&s8d);
int8_t* s8n_8 = reinterpret_cast<int8_t*>(&s8n);
int8_t* s8m_8 = reinterpret_cast<int8_t*>(&s8m);
uint8_t* s8d_u8 = reinterpret_cast<uint8_t*>(&s8d);
uint8_t* s8n_u8 = reinterpret_cast<uint8_t*>(&s8n);
uint8_t* s8m_u8 = reinterpret_cast<uint8_t*>(&s8m);
int16_t* s8d_16 = reinterpret_cast<int16_t*>(&s8d);
int16_t* s8n_16 = reinterpret_cast<int16_t*>(&s8n);
int16_t* s8m_16 = reinterpret_cast<int16_t*>(&s8m);
uint16_t* s8d_u16 = reinterpret_cast<uint16_t*>(&s8d);
uint16_t* s8n_u16 = reinterpret_cast<uint16_t*>(&s8n);
uint16_t* s8m_u16 = reinterpret_cast<uint16_t*>(&s8m);
int32_t* s8d_32 = reinterpret_cast<int32_t*>(&s8d);
int32_t* s8n_32 = reinterpret_cast<int32_t*>(&s8n);
int32_t* s8m_32 = reinterpret_cast<int32_t*>(&s8m);
uint32_t* s8d_u32 = reinterpret_cast<uint32_t*>(&s8d);
uint32_t* s8m_u32 = reinterpret_cast<uint32_t*>(&s8m);
int64_t* s8d_64 = reinterpret_cast<int64_t*>(&s8d);
int64_t* s8n_64 = reinterpret_cast<int64_t*>(&s8n);
int64_t* s8m_64 = reinterpret_cast<int64_t*>(&s8m);
uint64_t* s8d_u64 = reinterpret_cast<uint64_t*>(&s8d);
uint64_t* s8m_u64 = reinterpret_cast<uint64_t*>(&s8m);
if ((instr->Bits(8, 4) == 8) && (instr->Bit(4) == 0) &&
(instr->Bits(23, 2) == 0)) {
// Uses q registers.
// Format(instr, "vadd.'sz 'qd, 'qn, 'qm");
const int size = instr->Bits(20, 2);
if (size == 0) {
for (int i = 0; i < 16; i++) {
s8d_8[i] = s8n_8[i] + s8m_8[i];
}
} else if (size == 1) {
for (int i = 0; i < 8; i++) {
s8d_16[i] = s8n_16[i] + s8m_16[i];
}
} else if (size == 2) {
for (int i = 0; i < 4; i++) {
s8d.data_[i].u = s8n.data_[i].u + s8m.data_[i].u;
}
} else if (size == 3) {
for (int i = 0; i < 2; i++) {
s8d_64[i] = s8n_64[i] + s8m_64[i];
}
} else {
UNREACHABLE();
}
} else if ((instr->Bits(8, 4) == 13) && (instr->Bit(4) == 0) &&
(instr->Bits(23, 2) == 0) && (instr->Bit(21) == 0)) {
// Format(instr, "vadd.F32 'qd, 'qn, 'qm");
for (int i = 0; i < 4; i++) {
s8d.data_[i].f = s8n.data_[i].f + s8m.data_[i].f;
}
} else if ((instr->Bits(8, 4) == 8) && (instr->Bit(4) == 0) &&
(instr->Bits(23, 2) == 2)) {
// Format(instr, "vsub.'sz 'qd, 'qn, 'qm");
const int size = instr->Bits(20, 2);
if (size == 0) {
for (int i = 0; i < 16; i++) {
s8d_8[i] = s8n_8[i] - s8m_8[i];
}
} else if (size == 1) {
for (int i = 0; i < 8; i++) {
s8d_16[i] = s8n_16[i] - s8m_16[i];
}
} else if (size == 2) {
for (int i = 0; i < 4; i++) {
s8d.data_[i].u = s8n.data_[i].u - s8m.data_[i].u;
}
} else if (size == 3) {
for (int i = 0; i < 2; i++) {
s8d_64[i] = s8n_64[i] - s8m_64[i];
}
} else {
UNREACHABLE();
}
} else if ((instr->Bits(8, 4) == 13) && (instr->Bit(4) == 0) &&
(instr->Bits(23, 2) == 0) && (instr->Bit(21) == 1)) {
// Format(instr, "vsub.F32 'qd, 'qn, 'qm");
for (int i = 0; i < 4; i++) {
s8d.data_[i].f = s8n.data_[i].f - s8m.data_[i].f;
}
} else if ((instr->Bits(8, 4) == 9) && (instr->Bit(4) == 1) &&
(instr->Bits(23, 2) == 0)) {
// Format(instr, "vmul.'sz 'qd, 'qn, 'qm");
const int size = instr->Bits(20, 2);
if (size == 0) {
for (int i = 0; i < 16; i++) {
s8d_8[i] = s8n_8[i] * s8m_8[i];
}
} else if (size == 1) {
for (int i = 0; i < 8; i++) {
s8d_16[i] = s8n_16[i] * s8m_16[i];
}
} else if (size == 2) {
for (int i = 0; i < 4; i++) {
s8d.data_[i].u = s8n.data_[i].u * s8m.data_[i].u;
}
} else if (size == 3) {
UnimplementedInstruction(instr);
} else {
UNREACHABLE();
}
} else if ((instr->Bits(8, 4) == 13) && (instr->Bit(4) == 1) &&
(instr->Bits(23, 2) == 2) && (instr->Bit(21) == 0)) {
// Format(instr, "vmul.F32 'qd, 'qn, 'qm");
for (int i = 0; i < 4; i++) {
s8d.data_[i].f = s8n.data_[i].f * s8m.data_[i].f;
}
} else if ((instr->Bits(8, 4) == 4) && (instr->Bit(4) == 0) &&
(instr->Bit(23) == 0) && (instr->Bits(25, 3) == 1)) {
// Format(instr, "vshlqu'sz 'qd, 'qm, 'qn");
// Format(instr, "vshlqi'sz 'qd, 'qm, 'qn");
const bool signd = instr->Bit(24) == 0;
const int size = instr->Bits(20, 2);
if (size == 0) {
for (int i = 0; i < 16; i++) {
int8_t shift = s8n_8[i];
if (shift > 0) {
s8d_u8[i] = s8m_u8[i] << shift;
} else if (shift < 0) {
if (signd) {
s8d_8[i] = s8m_8[i] >> (-shift);
} else {
s8d_u8[i] = s8m_u8[i] >> (-shift);
}
}
}
} else if (size == 1) {
for (int i = 0; i < 8; i++) {
int8_t shift = s8n_8[i * 2];
if (shift > 0) {
s8d_u16[i] = s8m_u16[i] << shift;
} else if (shift < 0) {
if (signd) {
s8d_16[i] = s8m_16[i] >> (-shift);
} else {
s8d_u16[i] = s8m_u16[i] >> (-shift);
}
}
}
} else if (size == 2) {
for (int i = 0; i < 4; i++) {
int8_t shift = s8n_8[i * 4];
if (shift > 0) {
s8d_u32[i] = s8m_u32[i] << shift;
} else if (shift < 0) {
if (signd) {
s8d_32[i] = s8m_32[i] >> (-shift);
} else {
s8d_u32[i] = s8m_u32[i] >> (-shift);
}
}
}
} else {
ASSERT(size == 3);
for (int i = 0; i < 2; i++) {
int8_t shift = s8n_8[i * 8];
if (shift > 0) {
s8d_u64[i] = s8m_u64[i] << shift;
} else if (shift < 0) {
if (signd) {
s8d_64[i] = s8m_64[i] >> (-shift);
} else {
s8d_u64[i] = s8m_u64[i] >> (-shift);
}
}
}
}
} else if ((instr->Bits(8, 4) == 1) && (instr->Bit(4) == 1) &&
(instr->Bits(20, 2) == 0) && (instr->Bits(23, 2) == 2)) {
// Format(instr, "veorq 'qd, 'qn, 'qm");
for (int i = 0; i < 4; i++) {
s8d.data_[i].u = s8n.data_[i].u ^ s8m.data_[i].u;
}
} else if ((instr->Bits(8, 4) == 1) && (instr->Bit(4) == 1) &&
(instr->Bits(20, 2) == 3) && (instr->Bits(23, 2) == 0)) {
// Format(instr, "vornq 'qd, 'qn, 'qm");
for (int i = 0; i < 4; i++) {
s8d.data_[i].u = s8n.data_[i].u | ~s8m.data_[i].u;
}
} else if ((instr->Bits(8, 4) == 1) && (instr->Bit(4) == 1) &&
(instr->Bits(20, 2) == 2) && (instr->Bits(23, 2) == 0)) {
if (qm == qn) {
// Format(instr, "vmovq 'qd, 'qm");
for (int i = 0; i < 4; i++) {
s8d.data_[i].u = s8m.data_[i].u;
}
} else {
// Format(instr, "vorrq 'qd, 'qm");
for (int i = 0; i < 4; i++) {
s8d.data_[i].u = s8n.data_[i].u | s8m.data_[i].u;
}
}
} else if ((instr->Bits(8, 4) == 1) && (instr->Bit(4) == 1) &&
(instr->Bits(20, 2) == 0) && (instr->Bits(23, 2) == 0)) {
// Format(instr, "vandq 'qd, 'qn, 'qm");
for (int i = 0; i < 4; i++) {
s8d.data_[i].u = s8n.data_[i].u & s8m.data_[i].u;
}
} else if ((instr->Bits(7, 5) == 11) && (instr->Bit(4) == 0) &&
(instr->Bits(20, 2) == 3) && (instr->Bits(23, 5) == 7) &&
(instr->Bits(16, 4) == 0)) {
// Format(instr, "vmvnq 'qd, 'qm");
for (int i = 0; i < 4; i++) {
s8d.data_[i].u = ~s8m.data_[i].u;
}
} else if ((instr->Bits(8, 4) == 15) && (instr->Bit(4) == 0) &&
(instr->Bits(20, 2) == 2) && (instr->Bits(23, 2) == 0)) {
// Format(instr, "vminqs 'qd, 'qn, 'qm");
for (int i = 0; i < 4; i++) {
s8d.data_[i].f = fminf(s8n.data_[i].f, s8m.data_[i].f);
}
} else if ((instr->Bits(8, 4) == 15) && (instr->Bit(4) == 0) &&
(instr->Bits(20, 2) == 0) && (instr->Bits(23, 2) == 0)) {
// Format(instr, "vmaxqs 'qd, 'qn, 'qm");
for (int i = 0; i < 4; i++) {
s8d.data_[i].f = fmaxf(s8n.data_[i].f, s8m.data_[i].f);
}
} else if ((instr->Bits(8, 4) == 7) && (instr->Bit(4) == 0) &&
(instr->Bits(20, 2) == 3) && (instr->Bits(23, 2) == 3) &&
(instr->Bit(7) == 0) && (instr->Bits(16, 4) == 9)) {
// Format(instr, "vabsqs 'qd, 'qm");
for (int i = 0; i < 4; i++) {
s8d.data_[i].f = fabsf(s8m.data_[i].f);
}
} else if ((instr->Bits(8, 4) == 7) && (instr->Bit(4) == 0) &&
(instr->Bits(20, 2) == 3) && (instr->Bits(23, 2) == 3) &&
(instr->Bit(7) == 1) && (instr->Bits(16, 4) == 9)) {
// Format(instr, "vnegqs 'qd, 'qm");
for (int i = 0; i < 4; i++) {
s8d.data_[i].f = -s8m.data_[i].f;
}
} else if ((instr->Bits(7, 5) == 10) && (instr->Bit(4) == 0) &&
(instr->Bits(20, 2) == 3) && (instr->Bits(23, 2) == 3) &&
(instr->Bits(16, 4) == 11)) {
// Format(instr, "vrecpeq 'qd, 'qm");
for (int i = 0; i < 4; i++) {
s8d.data_[i].f = ReciprocalEstimate(s8m.data_[i].f);
}
} else if ((instr->Bits(8, 4) == 15) && (instr->Bit(4) == 1) &&
(instr->Bits(20, 2) == 0) && (instr->Bits(23, 2) == 0)) {
// Format(instr, "vrecpsq 'qd, 'qn, 'qm");
for (int i = 0; i < 4; i++) {
s8d.data_[i].f = ReciprocalStep(s8n.data_[i].f, s8m.data_[i].f);
}
} else if ((instr->Bits(8, 4) == 5) && (instr->Bit(4) == 0) &&
(instr->Bits(20, 2) == 3) && (instr->Bits(23, 2) == 3) &&
(instr->Bit(7) == 1) && (instr->Bits(16, 4) == 11)) {
// Format(instr, "vrsqrteqs 'qd, 'qm");
for (int i = 0; i < 4; i++) {
s8d.data_[i].f = ReciprocalSqrtEstimate(s8m.data_[i].f);
}
} else if ((instr->Bits(8, 4) == 15) && (instr->Bit(4) == 1) &&
(instr->Bits(20, 2) == 2) && (instr->Bits(23, 2) == 0)) {
// Format(instr, "vrsqrtsqs 'qd, 'qn, 'qm");
for (int i = 0; i < 4; i++) {
s8d.data_[i].f = ReciprocalSqrtStep(s8n.data_[i].f, s8m.data_[i].f);
}
} else if ((instr->Bits(8, 4) == 12) && (instr->Bit(4) == 0) &&
(instr->Bits(20, 2) == 3) && (instr->Bits(23, 2) == 3) &&
(instr->Bit(7) == 0)) {
DRegister dm = instr->DmField();
int64_t dm_value = get_dregister_bits(dm);
int32_t imm4 = instr->Bits(16, 4);
int32_t idx;
if ((imm4 & 1) != 0) {
// Format(instr, "vdupb 'qd, 'dm['imm4_vdup]");
int8_t* dm_b = reinterpret_cast<int8_t*>(&dm_value);
idx = imm4 >> 1;
int8_t val = dm_b[idx];
for (int i = 0; i < 16; i++) {
s8d_8[i] = val;
}
} else if ((imm4 & 2) != 0) {
// Format(instr, "vduph 'qd, 'dm['imm4_vdup]");
int16_t* dm_h = reinterpret_cast<int16_t*>(&dm_value);
idx = imm4 >> 2;
int16_t val = dm_h[idx];
for (int i = 0; i < 8; i++) {
s8d_16[i] = val;
}
} else if ((imm4 & 4) != 0) {
// Format(instr, "vdupw 'qd, 'dm['imm4_vdup]");
int32_t* dm_w = reinterpret_cast<int32_t*>(&dm_value);
idx = imm4 >> 3;
int32_t val = dm_w[idx];
for (int i = 0; i < 4; i++) {
s8d.data_[i].u = val;
}
} else {
UnimplementedInstruction(instr);
}
} else if ((instr->Bits(8, 4) == 1) && (instr->Bit(4) == 0) &&
(instr->Bits(20, 2) == 3) && (instr->Bits(23, 2) == 3) &&
(instr->Bit(7) == 1) && (instr->Bits(16, 4) == 10)) {
// Format(instr, "vzipqw 'qd, 'qm");
get_qregister(qd, &s8d);
// Interleave the elements with the low words in qd, and the high words
// in qm.
simd_value_swap(&s8d, 3, &s8m, 2);
simd_value_swap(&s8d, 3, &s8m, 1);
simd_value_swap(&s8d, 2, &s8m, 0);
simd_value_swap(&s8d, 2, &s8d, 1);
set_qregister(qm, s8m); // Writes both qd and qm.
} else if ((instr->Bits(8, 4) == 8) && (instr->Bit(4) == 1) &&
(instr->Bits(23, 2) == 2)) {
// Format(instr, "vceqq'sz 'qd, 'qn, 'qm");
const int size = instr->Bits(20, 2);
if (size == 0) {
for (int i = 0; i < 16; i++) {
s8d_8[i] = s8n_8[i] == s8m_8[i] ? 0xff : 0;
}
} else if (size == 1) {
for (int i = 0; i < 8; i++) {
s8d_16[i] = s8n_16[i] == s8m_16[i] ? 0xffff : 0;
}
} else if (size == 2) {
for (int i = 0; i < 4; i++) {
s8d.data_[i].u = s8n.data_[i].u == s8m.data_[i].u ? 0xffffffff : 0;
}
} else if (size == 3) {
UnimplementedInstruction(instr);
} else {
UNREACHABLE();
}
} else if ((instr->Bits(8, 4) == 14) && (instr->Bit(4) == 0) &&
(instr->Bits(20, 2) == 0) && (instr->Bits(23, 2) == 0)) {
// Format(instr, "vceqqs 'qd, 'qn, 'qm");
for (int i = 0; i < 4; i++) {
s8d.data_[i].u = s8n.data_[i].f == s8m.data_[i].f ? 0xffffffff : 0;
}
} else if ((instr->Bits(8, 4) == 3) && (instr->Bit(4) == 1) &&
(instr->Bits(23, 2) == 0)) {
// Format(instr, "vcgeq'sz 'qd, 'qn, 'qm");
const int size = instr->Bits(20, 2);
if (size == 0) {
for (int i = 0; i < 16; i++) {
s8d_8[i] = s8n_8[i] >= s8m_8[i] ? 0xff : 0;
}
} else if (size == 1) {
for (int i = 0; i < 8; i++) {
s8d_16[i] = s8n_16[i] >= s8m_16[i] ? 0xffff : 0;
}
} else if (size == 2) {
for (int i = 0; i < 4; i++) {
s8d.data_[i].u = s8n_32[i] >= s8m_32[i] ? 0xffffffff : 0;
}
} else if (size == 3) {
UnimplementedInstruction(instr);
} else {
UNREACHABLE();
}
} else if ((instr->Bits(8, 4) == 3) && (instr->Bit(4) == 1) &&
(instr->Bits(23, 2) == 2)) {
// Format(instr, "vcugeq'sz 'qd, 'qn, 'qm");
const int size = instr->Bits(20, 2);
if (size == 0) {
for (int i = 0; i < 16; i++) {
s8d_8[i] = s8n_u8[i] >= s8m_u8[i] ? 0xff : 0;
}
} else if (size == 1) {
for (int i = 0; i < 8; i++) {
s8d_16[i] = s8n_u16[i] >= s8m_u16[i] ? 0xffff : 0;
}
} else if (size == 2) {
for (int i = 0; i < 4; i++) {
s8d.data_[i].u = s8n.data_[i].u >= s8m.data_[i].u ? 0xffffffff : 0;
}
} else if (size == 3) {
UnimplementedInstruction(instr);
} else {
UNREACHABLE();
}
} else if ((instr->Bits(8, 4) == 14) && (instr->Bit(4) == 0) &&
(instr->Bits(20, 2) == 0) && (instr->Bits(23, 2) == 2)) {
// Format(instr, "vcgeqs 'qd, 'qn, 'qm");
for (int i = 0; i < 4; i++) {
s8d.data_[i].u = s8n.data_[i].f >= s8m.data_[i].f ? 0xffffffff : 0;
}
} else if ((instr->Bits(8, 4) == 3) && (instr->Bit(4) == 0) &&
(instr->Bits(23, 2) == 0)) {
// Format(instr, "vcgtq'sz 'qd, 'qn, 'qm");
const int size = instr->Bits(20, 2);
if (size == 0) {
for (int i = 0; i < 16; i++) {
s8d_8[i] = s8n_8[i] > s8m_8[i] ? 0xff : 0;
}
} else if (size == 1) {
for (int i = 0; i < 8; i++) {
s8d_16[i] = s8n_16[i] > s8m_16[i] ? 0xffff : 0;
}
} else if (size == 2) {
for (int i = 0; i < 4; i++) {
s8d.data_[i].u = s8n_32[i] > s8m_32[i] ? 0xffffffff : 0;
}
} else if (size == 3) {
UnimplementedInstruction(instr);
} else {
UNREACHABLE();
}
} else if ((instr->Bits(8, 4) == 3) && (instr->Bit(4) == 0) &&
(instr->Bits(23, 2) == 2)) {
// Format(instr, "vcugtq'sz 'qd, 'qn, 'qm");
const int size = instr->Bits(20, 2);
if (size == 0) {
for (int i = 0; i < 16; i++) {
s8d_8[i] = s8n_u8[i] > s8m_u8[i] ? 0xff : 0;
}
} else if (size == 1) {
for (int i = 0; i < 8; i++) {
s8d_16[i] = s8n_u16[i] > s8m_u16[i] ? 0xffff : 0;
}
} else if (size == 2) {
for (int i = 0; i < 4; i++) {
s8d.data_[i].u = s8n.data_[i].u > s8m.data_[i].u ? 0xffffffff : 0;
}
} else if (size == 3) {
UnimplementedInstruction(instr);
} else {
UNREACHABLE();
}
} else if ((instr->Bits(8, 4) == 14) && (instr->Bit(4) == 0) &&
(instr->Bits(20, 2) == 2) && (instr->Bits(23, 2) == 2)) {
// Format(instr, "vcgtqs 'qd, 'qn, 'qm");
for (int i = 0; i < 4; i++) {
s8d.data_[i].u = s8n.data_[i].f > s8m.data_[i].f ? 0xffffffff : 0;
}
} else {
UnimplementedInstruction(instr);
}
set_qregister(qd, s8d);
} else {
// Q == 0, Uses 64-bit D registers.
if ((instr->Bits(23, 2) == 3) && (instr->Bits(20, 2) == 3) &&
(instr->Bits(10, 2) == 2) && (instr->Bit(4) == 0)) {
// Format(instr, "vtbl 'dd, 'dtbllist, 'dm");
DRegister dd = instr->DdField();
DRegister dm = instr->DmField();
int reg_count = instr->Bits(8, 2) + 1;
int start = (instr->Bit(7) << 4) | instr->Bits(16, 4);
int64_t table[4];
for (int i = 0; i < reg_count; i++) {
DRegister d = static_cast<DRegister>(start + i);
table[i] = get_dregister_bits(d);
}
for (int i = reg_count; i < 4; i++) {
table[i] = 0;
}
int64_t dm_value = get_dregister_bits(dm);
int64_t result;
int8_t* dm_bytes = reinterpret_cast<int8_t*>(&dm_value);
int8_t* result_bytes = reinterpret_cast<int8_t*>(&result);
int8_t* table_bytes = reinterpret_cast<int8_t*>(&table[0]);
for (int i = 0; i < 8; i++) {
int idx = dm_bytes[i];
if ((idx >= 0) && (idx < 256)) {
result_bytes[i] = table_bytes[idx];
} else {
result_bytes[i] = 0;
}
}
set_dregister_bits(dd, result);
} else {
UnimplementedInstruction(instr);
}
}
}
// Executes the current instruction.
DART_FORCE_INLINE void Simulator::InstructionDecodeImpl(Instr* instr) {
pc_modified_ = false;
if (instr->ConditionField() == kSpecialCondition) {
if (instr->InstructionBits() == static_cast<int32_t>(0xf57ff01f)) {
// Format(instr, "clrex");
ClearExclusive();
} else {
if (instr->IsSIMDDataProcessing()) {
DecodeSIMDDataProcessing(instr);
} else {
UnimplementedInstruction(instr);
}
}
} else if (ConditionallyExecute(instr)) {
switch (instr->TypeField()) {
case 0:
case 1: {
DecodeType01(instr);
break;
}
case 2: {
DecodeType2(instr);
break;
}
case 3: {
DecodeType3(instr);
break;
}
case 4: {
DecodeType4(instr);
break;
}
case 5: {
DecodeType5(instr);
break;
}
case 6: {
DecodeType6(instr);
break;
}
case 7: {
DecodeType7(instr);
break;
}
default: {
// Type field is three bits.
UNREACHABLE();
break;
}
}
}
if (!pc_modified_) {
set_register(PC, reinterpret_cast<int32_t>(instr) + Instr::kInstrSize);
}
}
void Simulator::InstructionDecode(Instr* instr) {
if (IsTracingExecution()) {
THR_Print("%" Pu64 " ", icount_);
const uword start = reinterpret_cast<uword>(instr);
const uword end = start + Instr::kInstrSize;
if (FLAG_support_disassembler) {
Disassembler::Disassemble(start, end);
} else {
THR_Print("Disassembler not supported in this mode.\n");
}
}
InstructionDecodeImpl(instr);
}
void Simulator::Execute() {
// Get the PC to simulate. Cannot use the accessor here as we need the
// raw PC value and not the one used as input to arithmetic instructions.
uword program_counter = get_pc();
if (FLAG_stop_sim_at == ULLONG_MAX && FLAG_trace_sim_after == ULLONG_MAX) {
// Fast version of the dispatch loop without checking whether the simulator
// should be stopping at a particular executed instruction.
while (program_counter != kEndSimulatingPC) {
Instr* instr = reinterpret_cast<Instr*>(program_counter);
icount_++;
if (IsIllegalAddress(program_counter)) {
HandleIllegalAccess(program_counter, instr);
} else {
InstructionDecodeImpl(instr);
}
program_counter = get_pc();
}
} else {
// FLAG_stop_sim_at is at the non-default value. Stop in the debugger when
// we reach the particular instruction count or address.
while (program_counter != kEndSimulatingPC) {
Instr* instr = reinterpret_cast<Instr*>(program_counter);
icount_++;
if (icount_ == FLAG_stop_sim_at) {
SimulatorDebugger dbg(this);
dbg.Stop(instr, "Instruction count reached");
} else if (reinterpret_cast<uint64_t>(instr) == FLAG_stop_sim_at) {
SimulatorDebugger dbg(this);
dbg.Stop(instr, "Instruction address reached");
} else if (IsIllegalAddress(program_counter)) {
HandleIllegalAccess(program_counter, instr);
} else {
InstructionDecode(instr);
}
program_counter = get_pc();
}
}
}
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) {
ASSERT(TargetCPUFeatures::vfp_supported());
set_sregister(S0, bit_cast<float, int32_t>(parameter0));
set_sregister(S1, bit_cast<float, int32_t>(parameter1));
set_sregister(S2, bit_cast<float, int32_t>(parameter2));
set_sregister(S3, bit_cast<float, int32_t>(parameter3));
} else {
set_register(R0, parameter0);
set_register(R1, parameter1);
set_register(R2, parameter2);
set_register(R3, 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_register(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
// the LR the simulation stops when returning to this call point.
set_register(LR, 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 r4_val = get_register(R4);
int32_t r5_val = get_register(R5);
int32_t r6_val = get_register(R6);
int32_t r7_val = get_register(R7);
int32_t r8_val = get_register(R8);
#if !defined(TARGET_OS_MACOS) && !defined(TARGET_OS_MACOS_IOS)
int32_t r9_val = get_register(R9);
#endif
int32_t r10_val = get_register(R10);
int32_t r11_val = get_register(R11);
double d8_val = 0.0;
double d9_val = 0.0;
double d10_val = 0.0;
double d11_val = 0.0;
double d12_val = 0.0;
double d13_val = 0.0;
double d14_val = 0.0;
double d15_val = 0.0;
if (TargetCPUFeatures::vfp_supported()) {
d8_val = get_dregister(D8);
d9_val = get_dregister(D9);
d10_val = get_dregister(D10);
d11_val = get_dregister(D11);
d12_val = get_dregister(D12);
d13_val = get_dregister(D13);
d14_val = get_dregister(D14);
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(R4, callee_saved_value);
set_register(R5, callee_saved_value);
set_register(R6, callee_saved_value);
set_register(R7, callee_saved_value);
set_register(R8, callee_saved_value);
#if !defined(TARGET_OS_MACOS) && !defined(TARGET_OS_MACOS_IOS)
set_register(R9, callee_saved_value);
#endif
set_register(R10, callee_saved_value);
set_register(R11, callee_saved_value);
double callee_saved_dvalue = 0.0;
if (TargetCPUFeatures::vfp_supported()) {
callee_saved_dvalue = static_cast<double>(icount_);
set_dregister(D8, callee_saved_dvalue);
set_dregister(D9, callee_saved_dvalue);
set_dregister(D10, callee_saved_dvalue);
set_dregister(D11, callee_saved_dvalue);
set_dregister(D12, callee_saved_dvalue);
set_dregister(D13, callee_saved_dvalue);
set_dregister(D14, callee_saved_dvalue);
set_dregister(D15, callee_saved_dvalue);
}
// Start the simulation
Execute();
// Check that the callee-saved registers have been preserved.
ASSERT(callee_saved_value == get_register(R4));
ASSERT(callee_saved_value == get_register(R5));
ASSERT(callee_saved_value == get_register(R6));
ASSERT(callee_saved_value == get_register(R7));
ASSERT(callee_saved_value == get_register(R8));
#if !defined(TARGET_OS_MACOS) && !defined(TARGET_OS_MACOS_IOS)
ASSERT(callee_saved_value == get_register(R9));
#endif
ASSERT(callee_saved_value == get_register(R10));
ASSERT(callee_saved_value == get_register(R11));
if (TargetCPUFeatures::vfp_supported()) {
ASSERT(callee_saved_dvalue == get_dregister(D8));
ASSERT(callee_saved_dvalue == get_dregister(D9));
ASSERT(callee_saved_dvalue == get_dregister(D10));
ASSERT(callee_saved_dvalue == get_dregister(D11));
ASSERT(callee_saved_dvalue == get_dregister(D12));
ASSERT(callee_saved_dvalue == get_dregister(D13));
ASSERT(callee_saved_dvalue == get_dregister(D14));
ASSERT(callee_saved_dvalue == get_dregister(D15));
}
// Restore callee-saved registers with the original value.
set_register(R4, r4_val);
set_register(R5, r5_val);
set_register(R6, r6_val);
set_register(R7, r7_val);
set_register(R8, r8_val);
#if !defined(TARGET_OS_MACOS) && !defined(TARGET_OS_MACOS_IOS)
set_register(R9, r9_val);
#endif
set_register(R10, r10_val);
set_register(R11, r11_val);
if (TargetCPUFeatures::vfp_supported()) {
set_dregister(D8, d8_val);
set_dregister(D9, d9_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 R1:R0.
set_register(SP, sp_before_call);
int64_t return_value;
if (fp_return) {
ASSERT(TargetCPUFeatures::vfp_supported());
return_value = bit_cast<int64_t, double>(get_dregister(D0));
} else {
return_value = Utils::LowHighTo64Bits(get_register(R0), get_register(R1));
}
return return_value;
}
void Simulator::JumpToFrame(uword pc, uword sp, uword fp, Thread* thread) {
// 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.
StackResource::Unwind(thread);
// Unwind the C++ stack and continue simulation in the target frame.
set_register(PC, static_cast<int32_t>(pc));
set_register(SP, static_cast<int32_t>(sp));
set_register(FP, static_cast<int32_t>(fp));
set_register(THR, reinterpret_cast<uword>(thread));
// Set the tag.
thread->set_vm_tag(VMTag::kDartCompiledTagId);
// Clear top exit frame.
thread->set_top_exit_frame_info(0);
// Restore pool pointer.
int32_t code =
*reinterpret_cast<int32_t*>(fp + kPcMarkerSlotFromFp * kWordSize);
int32_t pp = (FLAG_precompiled_mode && FLAG_use_bare_instructions)
? reinterpret_cast<int32_t>(thread->global_object_pool())
: *reinterpret_cast<int32_t*>(
(code + Code::object_pool_offset() - kHeapObjectTag));
set_register(CODE_REG, code);
set_register(PP, pp);
buf->Longjmp();
}
} // namespace dart
#endif // defined(USING_SIMULATOR)
#endif // defined TARGET_ARCH_ARM