| Index: runtime/vm/simulator_mips.cc
|
| ===================================================================
|
| --- runtime/vm/simulator_mips.cc (revision 20788)
|
| +++ runtime/vm/simulator_mips.cc (working copy)
|
| @@ -2,6 +2,10 @@
|
| // 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 <math.h> // for isnan.
|
| +#include <setjmp.h>
|
| +#include <stdlib.h>
|
| +
|
| #include "vm/globals.h"
|
| #if defined(TARGET_ARCH_MIPS)
|
|
|
| @@ -13,9 +17,12 @@
|
| #include "vm/assembler.h"
|
| #include "vm/constants_mips.h"
|
| #include "vm/disassembler.h"
|
| +#include "vm/native_arguments.h"
|
| +#include "vm/thread.h"
|
|
|
| namespace dart {
|
|
|
| +DEFINE_FLAG(bool, trace_sim, false, "Trace simulator execution.");
|
| DEFINE_FLAG(int, stop_sim_at, 0, "Address to stop simulator at.");
|
|
|
|
|
| @@ -26,6 +33,46 @@
|
| #define SScanF sscanf // NOLINT
|
|
|
|
|
| +// SimulatorSetjmpBuffer are linked together, and the last created one
|
| +// is referenced by the Simulator. When an exception is thrown, the exception
|
| +// runtime looks at where to jump and finds the corresponding
|
| +// SimulatorSetjmpBuffer based on the stack pointer of the exception handler.
|
| +// The runtime then does a Longjmp on that buffer to return to the simulator.
|
| +class SimulatorSetjmpBuffer {
|
| + public:
|
| + int Setjmp() { return setjmp(buffer_); }
|
| + void Longjmp() {
|
| + // "This" is now the last setjmp buffer.
|
| + simulator_->set_last_setjmp_buffer(this);
|
| + longjmp(buffer_, 1);
|
| + }
|
| +
|
| + explicit SimulatorSetjmpBuffer(Simulator* sim) {
|
| + simulator_ = sim;
|
| + link_ = sim->last_setjmp_buffer();
|
| + sim->set_last_setjmp_buffer(this);
|
| + sp_ = sim->get_register(SP);
|
| + }
|
| +
|
| + ~SimulatorSetjmpBuffer() {
|
| + ASSERT(simulator_->last_setjmp_buffer() == this);
|
| + simulator_->set_last_setjmp_buffer(link_);
|
| + }
|
| +
|
| + SimulatorSetjmpBuffer* link() { return link_; }
|
| +
|
| + int32_t sp() { return sp_; }
|
| +
|
| + private:
|
| + int32_t sp_;
|
| + Simulator* simulator_;
|
| + SimulatorSetjmpBuffer* link_;
|
| + jmp_buf buffer_;
|
| +
|
| + friend class Simulator;
|
| +};
|
| +
|
| +
|
| // The SimulatorDebugger class is used by the simulator while debugging
|
| // simulated MIPS code.
|
| class SimulatorDebugger {
|
| @@ -510,6 +557,68 @@
|
| }
|
|
|
|
|
| +// When the generated code calls an external reference we need to catch that in
|
| +// the simulator. The external reference will be a function compiled for the
|
| +// host architecture. We need to call that function instead of trying to
|
| +// execute it with the simulator. We do that by redirecting the external
|
| +// reference to a break instruction with code 2 that is handled by
|
| +// the simulator. We write the original destination of the jump just at a known
|
| +// offset from the break instruction so the simulator knows what to call.
|
| +class Redirection {
|
| + public:
|
| + uword address_of_break_instruction() {
|
| + return reinterpret_cast<uword>(&break_instruction_);
|
| + }
|
| +
|
| + uword external_function() const { return external_function_; }
|
| +
|
| + Simulator::CallKind call_kind() const { return call_kind_; }
|
| +
|
| + static Redirection* Get(uword external_function,
|
| + Simulator::CallKind call_kind) {
|
| + Redirection* current;
|
| + for (current = list_; current != NULL; current = current->next_) {
|
| + if (current->external_function_ == external_function) return current;
|
| + }
|
| + return new Redirection(external_function, call_kind);
|
| + }
|
| +
|
| + static Redirection* FromBreakInstruction(Instr* break_instruction) {
|
| + char* addr_of_break = reinterpret_cast<char*>(break_instruction);
|
| + char* addr_of_redirection =
|
| + addr_of_break - OFFSET_OF(Redirection, break_instruction_);
|
| + return reinterpret_cast<Redirection*>(addr_of_redirection);
|
| + }
|
| +
|
| + private:
|
| + static const int32_t kRedirectInstruction =
|
| + Instr::kBreakPointInstruction | (Instr::kRedirectCode << kBreakCodeShift);
|
| +
|
| + Redirection(uword external_function, Simulator::CallKind call_kind)
|
| + : external_function_(external_function),
|
| + call_kind_(call_kind),
|
| + break_instruction_(kRedirectInstruction),
|
| + next_(list_) {
|
| + list_ = this;
|
| + }
|
| +
|
| + uword external_function_;
|
| + Simulator::CallKind call_kind_;
|
| + uint32_t break_instruction_;
|
| + Redirection* next_;
|
| + static Redirection* list_;
|
| +};
|
| +
|
| +
|
| +Redirection* Redirection::list_ = NULL;
|
| +
|
| +
|
| +uword Simulator::RedirectExternalReference(uword function, CallKind call_kind) {
|
| + Redirection* redirection = Redirection::Get(function, call_kind);
|
| + return redirection->address_of_break_instruction();
|
| +}
|
| +
|
| +
|
| // Get the active Simulator for the current isolate.
|
| Simulator* Simulator::Current() {
|
| Simulator* simulator = Isolate::Current()->simulator();
|
| @@ -691,6 +800,102 @@
|
| }
|
|
|
|
|
| +// Calls into the Dart runtime are based on this interface.
|
| +typedef void (*SimulatorRuntimeCall)(NativeArguments arguments);
|
| +
|
| +// Calls to leaf Dart runtime functions are based on this interface.
|
| +typedef int32_t (*SimulatorLeafRuntimeCall)(
|
| + int32_t r0, int32_t r1, int32_t r2, int32_t r3);
|
| +
|
| +// Calls to native Dart functions are based on this interface.
|
| +typedef void (*SimulatorNativeCall)(NativeArguments* arguments);
|
| +
|
| +
|
| +void Simulator::DoBreak(Instr *instr) {
|
| + ASSERT(instr->OpcodeField() == SPECIAL);
|
| + ASSERT(instr->FunctionField() == BREAK);
|
| + if (instr->BreakCodeField() == Instr::kStopMessageCode) {
|
| + SimulatorDebugger dbg(this);
|
| + const char* message = *reinterpret_cast<const char**>(
|
| + reinterpret_cast<intptr_t>(instr) - Instr::kInstrSize);
|
| + set_pc(get_pc() + Instr::kInstrSize);
|
| + dbg.Stop(instr, message);
|
| + } else if (instr->BreakCodeField() == Instr::kRedirectCode) {
|
| + SimulatorSetjmpBuffer buffer(this);
|
| +
|
| + if (!setjmp(buffer.buffer_)) {
|
| + int32_t saved_ra = get_register(RA);
|
| + Redirection* redirection = Redirection::FromBreakInstruction(instr);
|
| + uword external = redirection->external_function();
|
| + if (FLAG_trace_sim) {
|
| + OS::Print("Call to host function at 0x%"Pd"\n", external);
|
| + }
|
| + if (redirection->call_kind() == kRuntimeCall) {
|
| + NativeArguments arguments;
|
| + ASSERT(sizeof(NativeArguments) == 4*kWordSize);
|
| + arguments.isolate_ = reinterpret_cast<Isolate*>(get_register(A0));
|
| + arguments.argc_tag_ = get_register(A1);
|
| + arguments.argv_ = reinterpret_cast<RawObject*(*)[]>(get_register(A2));
|
| + arguments.retval_ = reinterpret_cast<RawObject**>(get_register(A3));
|
| + SimulatorRuntimeCall target =
|
| + reinterpret_cast<SimulatorRuntimeCall>(external);
|
| + target(arguments);
|
| + set_register(V0, icount_); // Zap result register from void function.
|
| + } else if (redirection->call_kind() == kLeafRuntimeCall) {
|
| + int32_t a0 = get_register(A0);
|
| + int32_t a1 = get_register(A1);
|
| + int32_t a2 = get_register(A2);
|
| + int32_t a3 = get_register(A3);
|
| + SimulatorLeafRuntimeCall target =
|
| + reinterpret_cast<SimulatorLeafRuntimeCall>(external);
|
| + a0 = target(a0, a1, a2, a3);
|
| + set_register(V0, a0); // Set returned result from function.
|
| + } else {
|
| + ASSERT(redirection->call_kind() == kNativeCall);
|
| + NativeArguments* arguments;
|
| + arguments = reinterpret_cast<NativeArguments*>(get_register(A0));
|
| + SimulatorNativeCall target =
|
| + reinterpret_cast<SimulatorNativeCall>(external);
|
| + target(arguments);
|
| + set_register(V0, icount_); // Zap result register from void function.
|
| + }
|
| +
|
| + // Zap caller-saved registers, since the actual runtime call could have
|
| + // used them.
|
| + set_register(T0, icount_);
|
| + set_register(T1, icount_);
|
| + set_register(T2, icount_);
|
| + set_register(T3, icount_);
|
| + set_register(T4, icount_);
|
| + set_register(T5, icount_);
|
| + set_register(T6, icount_);
|
| + set_register(T7, icount_);
|
| + set_register(T8, icount_);
|
| + set_register(T9, icount_);
|
| +
|
| + set_register(A0, icount_);
|
| + set_register(A1, icount_);
|
| + set_register(A2, icount_);
|
| + set_register(A3, icount_);
|
| + set_register(TMP, icount_);
|
| + set_register(RA, icount_);
|
| +
|
| + // Zap floating point registers.
|
| + double zap_dvalue = static_cast<double>(icount_);
|
| + for (int i = F0; i <= F31; i++) {
|
| + set_fregister(static_cast<FRegister>(i), zap_dvalue);
|
| + }
|
| +
|
| + // Return.
|
| + set_pc(saved_ra);
|
| + }
|
| + } else {
|
| + SimulatorDebugger dbg(this);
|
| + dbg.Stop(instr, "breakpoint");
|
| + }
|
| +}
|
| +
|
| +
|
| void Simulator::DecodeSpecial(Instr* instr) {
|
| ASSERT(instr->OpcodeField() == SPECIAL);
|
| switch (instr->FunctionField()) {
|
| @@ -711,16 +916,7 @@
|
| break;
|
| }
|
| case BREAK: {
|
| - if (instr->BreakCodeField() == Instr::kStopMessageCode) {
|
| - SimulatorDebugger dbg(this);
|
| - const char* message = *reinterpret_cast<const char**>(
|
| - reinterpret_cast<intptr_t>(instr) - Instr::kInstrSize);
|
| - set_pc(get_pc() + Instr::kInstrSize);
|
| - dbg.Stop(instr, message);
|
| - } else {
|
| - SimulatorDebugger dbg(this);
|
| - dbg.Stop(instr, "breakpoint");
|
| - }
|
| + DoBreak(instr);
|
| break;
|
| }
|
| case DIV: {
|
|
|