Implement long jump in ARM and MIPS simulators.

Implement error and exception handler stubs on ARM.
Hook up simulator for object tests.
Enable codegen and object tests on ARM.

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

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@21680 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
regis@google.com
2013-04-18 16:23:25 +00:00
parent 6f81bd5427
commit d79c7c1969
16 changed files with 269 additions and 77 deletions
+1 -1
View File
@@ -1262,7 +1262,7 @@ void Assembler::LoadWordFromPoolOffset(Register rd, int32_t offset) {
void Assembler::LoadObject(Register rd, const Object& object) {
// Smi's and VM heap objects are never relocated; do not use object pool.
// Smis and VM heap objects are never relocated; do not use object pool.
if (object.IsSmi()) {
LoadImmediate(rd, reinterpret_cast<int32_t>(object.raw()));
} else if (object.InVMHeap()) {
+1 -1
View File
@@ -157,7 +157,7 @@ void Assembler::SubuDetectOverflow(Register rd, Register rs, Register rt,
void Assembler::LoadObject(Register rd, const Object& object) {
// Smi's and VM heap objects are never relocated; do not use object pool.
// Smis and VM heap objects are never relocated; do not use object pool.
if (object.IsSmi()) {
LoadImmediate(rd, reinterpret_cast<int32_t>(object.raw()));
} else if (object.InVMHeap()) {
+4 -2
View File
@@ -43,7 +43,9 @@ CODEGEN_TEST2_GENERATE(SimpleStaticCallCodegen, function, test) {
CODEGEN_TEST2_RUN(SimpleStaticCallCodegen, SmiReturnCodegen, Smi::New(3))
#if defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_X64)
#if defined(TARGET_ARCH_IA32) || \
defined(TARGET_ARCH_X64) || \
defined(TARGET_ARCH_ARM)
// Helper to allocate and return a LocalVariable.
@@ -561,6 +563,6 @@ CODEGEN_TEST_RAW_RUN(AllocateNewObjectCodegen, function) {
EXPECT_EQ(cls.raw(), result.clazz());
}
#endif // defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_X64)
#endif // TARGET_ARCH_IA32 || TARGET_ARCH_X64 || TARGET_ARCH_ARM
} // namespace dart
+5 -2
View File
@@ -143,10 +143,13 @@ const Register PP = R10; // Caches object pool pointer in generated code.
const Register SPREG = SP; // Stack pointer register.
const Register FPREG = FP; // Frame pointer register.
// Exception object is passed in this register to the catch handlers when an
// exception is thrown.
const Register kExceptionObjectReg = R0; // Unimplemented.
const Register kExceptionObjectReg = R0;
// Stack trace object is passed in this register to the catch handlers when
// an exception is thrown.
const Register kStackTraceObjectReg = R1;
// List of registers used in load/store multiple.
+9
View File
@@ -150,6 +150,15 @@ const Register FPREG = FP; // Frame pointer register.
// CMPRES is used for the result of the comparison.
const Register CMPRES = T8;
// Exception object is passed in this register to the catch handlers when an
// exception is thrown.
const Register kExceptionObjectReg = A0;
// Stack trace object is passed in this register to the catch handlers when
// an exception is thrown.
const Register kStackTraceObjectReg = A1;
typedef uint32_t RegList;
const RegList kAllCpuRegistersList = 0xFFFFFFFF;
+64 -34
View File
@@ -207,17 +207,22 @@ static void FindErrorHandler(uword* handler_pc,
}
void JumpToExceptionHandler(uword program_counter,
uword stack_pointer,
uword frame_pointer,
const Instance& exception_object,
const Instance& stacktrace_object) {
// The no_gc StackResource is unwound through the tear down of
// stack resources below.
NoGCScope no_gc;
RawInstance* exception = exception_object.raw();
RawInstance* stacktrace = stacktrace_object.raw();
static void JumpToHandler(uword program_counter,
uword stack_pointer,
uword frame_pointer,
RawObject* raw_exception,
RawObject* raw_stacktrace) {
#if defined(USING_SIMULATOR)
// Unwinding of the C++ frames and destroying of their stack resources is done
// by the simulator, because the target stack_pointer is a simulated stack
// pointer and not the C++ stack pointer.
// Continue simulating at the given pc in the given frame after setting up the
// exception object in the kExceptionObjectReg register and the stacktrace
// object (if not NULL) in the kStackTraceObjectReg register.
Simulator::Current()->Longjmp(program_counter, stack_pointer, frame_pointer,
raw_exception, raw_stacktrace);
#else
// Prepare for unwinding frames by destroying all the stack resources
// in the previous frames.
Isolate* isolate = Isolate::Current();
@@ -226,39 +231,64 @@ void JumpToExceptionHandler(uword program_counter,
isolate->top_resource()->~StackResource();
}
// Set up the appropriate register state and jump to the handler.
typedef void (*ExcpHandler)(uword, uword, uword, RawInstance*, RawInstance*);
ExcpHandler func = reinterpret_cast<ExcpHandler>(
StubCode::JumpToExceptionHandlerEntryPoint());
func(program_counter, stack_pointer, frame_pointer, exception, stacktrace);
// TODO(regis): Can we safely merge both stubs and pass NULL as
// stacktrace_object in the case of an error handler?
// A NULL raw_stacktrace indicates that we are jumping to an ErrorHandler,
// rather than to an ExceptionHandler. TODO(regis): Merge these two cases.
if (raw_stacktrace == NULL) {
// Call a stub to set up the error object in kExceptionObjectReg and to
// continue execution at the given pc in the given frame.
typedef void (*ErrorHandler)(uword, uword, uword, RawObject*);
ErrorHandler func = reinterpret_cast<ErrorHandler>(
StubCode::JumpToErrorHandlerEntryPoint());
func(program_counter, stack_pointer, frame_pointer, raw_exception);
} else {
// Call a stub to set up the exception object in kExceptionObjectReg,
// to set up the stacktrace object in kStackTraceObjectReg, and to
// continue execution at the given pc in the given frame.
typedef void (*ExcpHandler)(uword, uword, uword, RawObject*, RawObject*);
ExcpHandler func = reinterpret_cast<ExcpHandler>(
StubCode::JumpToExceptionHandlerEntryPoint());
func(program_counter, stack_pointer, frame_pointer,
raw_exception, raw_stacktrace);
}
#endif
UNREACHABLE();
}
void JumpToErrorHandler(uword program_counter,
uword stack_pointer,
uword frame_pointer,
const Error& error) {
static void JumpToExceptionHandler(uword program_counter,
uword stack_pointer,
uword frame_pointer,
const Instance& exception_object,
const Instance& stacktrace_object) {
// The no_gc StackResource is unwound through the tear down of
// stack resources below.
NoGCScope no_gc;
ASSERT(!error.IsNull());
RawError* raw_error = error.raw();
RawObject* raw_exception = exception_object.raw();
RawObject* raw_stacktrace = stacktrace_object.raw();
// Prepare for unwinding frames by destroying all the stack resources
// in the previous frames.
Isolate* isolate = Isolate::Current();
while (isolate->top_resource() != NULL &&
(reinterpret_cast<uword>(isolate->top_resource()) < stack_pointer)) {
isolate->top_resource()->~StackResource();
}
JumpToHandler(program_counter, stack_pointer, frame_pointer,
raw_exception, raw_stacktrace);
UNREACHABLE();
}
static void JumpToErrorHandler(uword program_counter,
uword stack_pointer,
uword frame_pointer,
const Error& error_object) {
// The no_gc StackResource is unwound through the tear down of
// stack resources below.
NoGCScope no_gc;
ASSERT(!error_object.IsNull());
RawObject* raw_error = error_object.raw();
JumpToHandler(
program_counter, stack_pointer, frame_pointer, raw_error, NULL);
// Set up the error object as the return value in EAX and continue
// from the invocation stub.
typedef void (*ErrorHandler)(uword, uword, uword, RawError*);
ErrorHandler func = reinterpret_cast<ErrorHandler>(
StubCode::JumpToErrorHandlerEntryPoint());
func(program_counter, stack_pointer, frame_pointer, raw_error);
UNREACHABLE();
}
+12 -3
View File
@@ -2243,13 +2243,22 @@ void StrictCompareInstr::EmitBranchCode(FlowGraphCompiler* compiler,
LocationSummary* BooleanNegateInstr::MakeLocationSummary() const {
UNIMPLEMENTED();
return NULL;
return LocationSummary::Make(1,
Location::RequiresRegister(),
LocationSummary::kNoCall);
}
void BooleanNegateInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
UNIMPLEMENTED();
Register value = locs()->in(0).reg();
Register result = locs()->out().reg();
Label done;
__ LoadObject(result, Bool::True());
__ cmp(result, ShifterOperand(value));
__ b(&done, NE);
__ LoadObject(result, Bool::False());
__ Bind(&done);
}
+20 -3
View File
@@ -18,21 +18,38 @@ namespace dart {
// Generate a simple dart code sequence.
// This is used to test Code and Instruction object creation.
void GenerateIncrement(Assembler* assembler) {
__ Unimplemented("GenerateIncrement");
__ LoadImmediate(R0, 0);
__ Push(R0);
__ ldr(IP, Address(SP, 0));
__ add(IP, IP, ShifterOperand(1));
__ str(IP, Address(SP, 0));
__ ldr(IP, Address(SP, 0));
__ add(IP, IP, ShifterOperand(1));
__ Pop(R0);
__ mov(R0, ShifterOperand(IP));
__ Ret();
}
// Generate a dart code sequence that embeds a string object in it.
// This is used to test Embedded String objects in the instructions.
void GenerateEmbedStringInCode(Assembler* assembler, const char* str) {
__ Unimplemented("GenerateEmbedStringInCode");
__ EnterDartFrame(0); // To setup pp.
const String& string_object =
String::ZoneHandle(String::New(str, Heap::kOld));
__ LoadObject(R0, string_object);
__ LeaveDartFrame();
__ Ret();
}
// Generate a dart code sequence that embeds a smi object in it.
// This is used to test Embedded Smi objects in the instructions.
void GenerateEmbedSmiInCode(Assembler* assembler, intptr_t value) {
__ Unimplemented("GenerateEmbedSmiInCode");
// No need to setup pp, since Smis are not stored in the object pool.
const Smi& smi_object = Smi::ZoneHandle(Smi::New(value));
__ LoadObject(R0, smi_object);
__ Ret();
}
} // namespace dart
+36 -10
View File
@@ -9,6 +9,7 @@
#include "vm/isolate.h"
#include "vm/object.h"
#include "vm/object_store.h"
#include "vm/simulator.h"
#include "vm/symbols.h"
#include "vm/unit_test.h"
@@ -2236,8 +2237,10 @@ TEST_CASE(CheckedHandle) {
}
// only ia32 and x64 can run execution tests.
#if defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_X64)
// Only ia32, x64, and arm can run execution tests.
#if defined(TARGET_ARCH_IA32) || \
defined(TARGET_ARCH_X64) || \
defined(TARGET_ARCH_ARM)
static Function* CreateFunction(const char* name) {
const String& class_name = String::Handle(Symbols::New("ownerClass"));
@@ -2259,9 +2262,16 @@ TEST_CASE(Code) {
Code& code = Code::Handle(Code::FinalizeCode(
*CreateFunction("Test_Code"), &_assembler_));
Instructions& instructions = Instructions::Handle(code.instructions());
typedef int (*IncrementCode)();
EXPECT_EQ(2, reinterpret_cast<IncrementCode>(instructions.EntryPoint())());
uword entry_point = instructions.EntryPoint();
intptr_t retval = 0;
#if defined(USING_SIMULATOR)
retval = bit_copy<intptr_t, int64_t>(Simulator::Current()->Call(
static_cast<int32_t>(entry_point), 0, 0, 0, 0));
#else
typedef intptr_t (*IncrementCode)();
retval = reinterpret_cast<IncrementCode>(entry_point)();
#endif
EXPECT_EQ(2, retval);
EXPECT_EQ(instructions.raw(), Instructions::FromEntryPoint(entry_point));
}
@@ -2276,8 +2286,14 @@ TEST_CASE(EmbedStringInCode) {
Code& code = Code::Handle(Code::FinalizeCode(
*CreateFunction("Test_EmbedStringInCode"), &_assembler_));
Instructions& instructions = Instructions::Handle(code.instructions());
uword retval = 0;
#if defined(USING_SIMULATOR)
retval = bit_copy<uword, int64_t>(Simulator::Current()->Call(
static_cast<int32_t>(instructions.EntryPoint()), 0, 0, 0, 0));
#else
typedef uword (*EmbedStringCode)();
uword retval = reinterpret_cast<EmbedStringCode>(instructions.EntryPoint())();
retval = reinterpret_cast<EmbedStringCode>(instructions.EntryPoint())();
#endif
EXPECT((retval & kSmiTagMask) == kHeapObjectTag);
String& string_object = String::Handle();
string_object ^= reinterpret_cast<RawInstructions*>(retval);
@@ -2297,9 +2313,14 @@ TEST_CASE(EmbedSmiInCode) {
Code& code = Code::Handle(Code::FinalizeCode(
*CreateFunction("Test_EmbedSmiInCode"), &_assembler_));
Instructions& instructions = Instructions::Handle(code.instructions());
intptr_t retval = 0;
#if defined(USING_SIMULATOR)
retval = bit_copy<intptr_t, int64_t>(Simulator::Current()->Call(
static_cast<int32_t>(instructions.EntryPoint()), 0, 0, 0, 0));
#else
typedef intptr_t (*EmbedSmiCode)();
intptr_t retval =
reinterpret_cast<EmbedSmiCode>(instructions.EntryPoint())();
retval = reinterpret_cast<EmbedSmiCode>(instructions.EntryPoint())();
#endif
EXPECT((retval >> kSmiTagShift) == kSmiTestValue);
}
@@ -2314,9 +2335,14 @@ TEST_CASE(EmbedSmiIn64BitCode) {
Code& code = Code::Handle(Code::FinalizeCode(
*CreateFunction("Test_EmbedSmiIn64BitCode"), &_assembler_));
Instructions& instructions = Instructions::Handle(code.instructions());
intptr_t retval = 0;
#if defined(USING_SIMULATOR)
retval = bit_copy<intptr_t, int64_t>(Simulator::Current()->Call(
static_cast<int32_t>(instructions.EntryPoint()), 0, 0, 0, 0));
#else
typedef intptr_t (*EmbedSmiCode)();
intptr_t retval =
reinterpret_cast<EmbedSmiCode>(instructions.EntryPoint())();
retval = reinterpret_cast<EmbedSmiCode>(instructions.EntryPoint())();
#endif
EXPECT((retval >> kSmiTagShift) == kSmiTestValue);
}
#endif
@@ -3182,6 +3208,6 @@ TEST_CASE(FunctionSourceFingerprint) {
EXPECT_EQ(test6.SourceFingerprint(), test7.SourceFingerprint());
}
#endif // defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_X64).
#endif // TARGET_ARCH_IA32 || TARGET_ARCH_X64 || TARGET_ARCH_ARM
} // namespace dart
+32 -13
View File
@@ -64,7 +64,8 @@ class SimulatorSetjmpBuffer {
simulator_ = sim;
link_ = sim->last_setjmp_buffer();
sim->set_last_setjmp_buffer(this);
sp_ = sim->get_register(SP);
sp_ = static_cast<uword>(sim->get_register(SP));
native_sp_ = reinterpret_cast<uword>(&sim); // Current C++ stack pointer.
}
~SimulatorSetjmpBuffer() {
@@ -74,10 +75,12 @@ class SimulatorSetjmpBuffer {
SimulatorSetjmpBuffer* link() { return link_; }
int32_t sp() { return sp_; }
uword sp() { return sp_; }
uword native_sp() { return native_sp_; }
private:
int32_t sp_;
uword sp_;
uword native_sp_;
Simulator* simulator_;
SimulatorSetjmpBuffer* link_;
jmp_buf buffer_;
@@ -2863,22 +2866,38 @@ int64_t Simulator::Call(int32_t entry,
}
void Simulator::Longjmp(
int32_t pc, int32_t sp, int32_t fp, const Instance& object) {
set_register(SP, sp);
set_register(FP, fp);
set_register(PC, pc);
SimulatorSetjmpBuffer* buf = last_setjmp_buffer();
void Simulator::Longjmp(uword pc,
uword sp,
uword fp,
RawObject* raw_exception,
RawObject* raw_stacktrace) {
// Walk over all setjmp buffers (simulated --> C++ transitions)
// and try to find the setjmp associated with the stack pointer.
// 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 caller has already cleaned up the stack memory of C++ frames.
set_register(kExceptionObjectReg, bit_cast<int32_t>(object.raw()));
// The C++ caller has not cleaned up the stack memory of C++ frames.
// Prepare for unwinding frames by destroying all the stack resources
// in the previous C++ frames.
uword native_sp = buf->native_sp();
Isolate* isolate = Isolate::Current();
while (isolate->top_resource() != NULL &&
(reinterpret_cast<uword>(isolate->top_resource()) < native_sp)) {
isolate->top_resource()->~StackResource();
}
// Unwind the C++ stack and continue simulation in the target frame.
set_register(PC, static_cast<int32_t>(pc));
set_register(SP, static_cast<int32_t>(sp));
set_register(FP, static_cast<int32_t>(fp));
ASSERT(raw_exception != NULL);
set_register(kExceptionObjectReg, bit_cast<int32_t>(raw_exception));
if (raw_stacktrace != NULL) {
set_register(kStackTraceObjectReg, bit_cast<int32_t>(raw_stacktrace));
}
buf->Longjmp();
}
+5 -1
View File
@@ -86,7 +86,11 @@ class Simulator {
};
static uword RedirectExternalReference(uword function, CallKind call_kind);
void Longjmp(int32_t pc, int32_t sp, int32_t fp, const Instance& object);
void Longjmp(uword pc,
uword sp,
uword fp,
RawObject* raw_exception,
RawObject* raw_stacktrace);
private:
// Known bad pc value to ensure that the simulator does not execute
+42 -3
View File
@@ -51,7 +51,8 @@ class SimulatorSetjmpBuffer {
simulator_ = sim;
link_ = sim->last_setjmp_buffer();
sim->set_last_setjmp_buffer(this);
sp_ = sim->get_register(SP);
sp_ = static_cast<uword>(sim->get_register(SP));
native_sp_ = reinterpret_cast<uword>(&sim); // Current C++ stack pointer.
}
~SimulatorSetjmpBuffer() {
@@ -61,10 +62,12 @@ class SimulatorSetjmpBuffer {
SimulatorSetjmpBuffer* link() { return link_; }
int32_t sp() { return sp_; }
uword sp() { return sp_; }
uword native_sp() { return native_sp_; }
private:
int32_t sp_;
uword sp_;
uword native_sp_;
Simulator* simulator_;
SimulatorSetjmpBuffer* link_;
jmp_buf buffer_;
@@ -1829,6 +1832,42 @@ int64_t Simulator::Call(int32_t entry,
return Utils::LowHighTo64Bits(get_register(V0), get_register(V1));
}
void Simulator::Longjmp(uword pc,
uword sp,
uword fp,
RawObject* raw_exception,
RawObject* raw_stacktrace) {
// Walk over all setjmp buffers (simulated --> C++ transitions)
// and try to find the setjmp associated with the simulated stack pointer.
SimulatorSetjmpBuffer* buf = last_setjmp_buffer();
while (buf->link() != NULL && buf->link()->sp() <= sp) {
buf = buf->link();
}
ASSERT(buf != NULL);
// The C++ caller has not cleaned up the stack memory of C++ frames.
// Prepare for unwinding frames by destroying all the stack resources
// in the previous C++ frames.
uword native_sp = buf->native_sp();
Isolate* isolate = Isolate::Current();
while (isolate->top_resource() != NULL &&
(reinterpret_cast<uword>(isolate->top_resource()) < native_sp)) {
isolate->top_resource()->~StackResource();
}
// Unwind the C++ stack and continue simulation in the target frame.
set_pc(static_cast<int32_t>(pc));
set_register(SP, static_cast<int32_t>(sp));
set_register(FP, static_cast<int32_t>(fp));
ASSERT(raw_exception != NULL);
set_register(kExceptionObjectReg, bit_cast<int32_t>(raw_exception));
if (raw_stacktrace != NULL) {
set_register(kStackTraceObjectReg, bit_cast<int32_t>(raw_stacktrace));
}
buf->Longjmp();
}
} // namespace dart
#endif // !defined(HOST_ARCH_MIPS)
+8
View File
@@ -17,6 +17,7 @@
#endif
#include "vm/constants_mips.h"
#include "vm/object.h"
namespace dart {
@@ -89,6 +90,12 @@ class Simulator {
};
static uword RedirectExternalReference(uword function, CallKind call_kind);
void Longjmp(uword pc,
uword sp,
uword fp,
RawObject* raw_exception,
RawObject* raw_stacktrace);
private:
// A pc value used to signal the simulator to stop execution. Generally
// the ra is set to this value on transition from native C code to
@@ -171,6 +178,7 @@ class Simulator {
friend class SimulatorDebugger;
friend class SimulatorSetjmpBuffer;
DISALLOW_COPY_AND_ASSIGN(Simulator);
};
} // namespace dart
+28 -4
View File
@@ -1474,16 +1474,40 @@ void StubCode::GenerateGetStackPointerStub(Assembler* assembler) {
// Jump to the exception handler.
// No Result.
// LR: return address.
// R0: program_counter.
// R1: stack_pointer.
// R2: frame_pointer.
// R3: error object.
// SP: address of stacktrace object.
// Does not return.
void StubCode::GenerateJumpToExceptionHandlerStub(Assembler* assembler) {
__ Unimplemented("JumpToExceptionHandler Stub");
ASSERT(kExceptionObjectReg == R0);
ASSERT(kStackTraceObjectReg == R1);
__ mov(IP, ShifterOperand(R1)); // Stack pointer.
__ mov(LR, ShifterOperand(R0)); // Program counter.
__ mov(R0, ShifterOperand(R3)); // Exception object.
__ ldr(R1, Address(SP, 0)); // StackTrace object.
__ mov(FP, ShifterOperand(R2)); // Frame_pointer.
__ mov(SP, ShifterOperand(IP)); // Stack pointer.
__ bx(LR); // Jump to the exception handler code.
}
// Jump to the error handler.
// No Result.
// LR: return address.
// R0: program_counter.
// R1: stack_pointer.
// R2: frame_pointer.
// R3: error object.
// Does not return.
void StubCode::GenerateJumpToErrorHandlerStub(Assembler* assembler) {
__ Unimplemented("JumpToErrorHandler Stub");
ASSERT(kExceptionObjectReg == R0);
__ mov(LR, ShifterOperand(R0)); // Program counter.
__ mov(R0, ShifterOperand(R3)); // Error object.
__ mov(FP, ShifterOperand(R2)); // Frame_pointer.
__ mov(SP, ShifterOperand(R1)); // Stack pointer.
__ bx(LR); // Jump to the exception handler code.
}
+1
View File
@@ -1907,6 +1907,7 @@ void StubCode::GenerateJumpToExceptionHandlerStub(Assembler* assembler) {
// TOS + 4: error object
// No Result.
void StubCode::GenerateJumpToErrorHandlerStub(Assembler* assembler) {
ASSERT(kExceptionObjectReg == EAX);
__ movl(EAX, Address(ESP, 4 * kWordSize)); // Load error object.
__ movl(EBP, Address(ESP, 3 * kWordSize)); // Load target frame_pointer.
__ movl(EBX, Address(ESP, 1 * kWordSize)); // Load target PC into EBX.
+1
View File
@@ -1874,6 +1874,7 @@ void StubCode::GenerateJumpToExceptionHandlerStub(Assembler* assembler) {
// RCX: error object
// No Result.
void StubCode::GenerateJumpToErrorHandlerStub(Assembler* assembler) {
ASSERT(kExceptionObjectReg == RAX);
__ movq(RAX, RCX); // error object.
__ movq(RBP, RDX); // target frame_pointer.
__ movq(RSP, RSI); // target stack_pointer.