Commit 13e548af authored by lrn@chromium.org's avatar lrn@chromium.org

X64: Implement CEntryStub and JSEntryTrampoline.

Still some supporting functions missing.

Review URL: http://codereview.chromium.org/114085


git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@2130 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 94a12229
......@@ -285,7 +285,7 @@ class MacroAssembler: public Assembler {
List<Unresolved> unresolved_;
bool generating_stub_;
bool allow_stub_calls_;
Handle<Object> code_object_; // This handle will be patched with the code
Handle<Object> code_object_; // This handle will be patched with the
// code object on installation.
// Helper functions for generating invokes.
......
......@@ -249,24 +249,6 @@ Object** RelocInfo::call_object_address() {
// -----------------------------------------------------------------------------
// Implementation of Operand
Operand::Operand(Register base, int32_t disp) {
len_ = 1;
if (base.is(rsp) || base.is(r12)) {
// SIB byte is needed to encode (rsp + offset) or (r12 + offset).
set_sib(kTimes1, rsp, base);
}
if (disp == 0 && !base.is(rbp) && !base.is(r13)) {
set_modrm(0, rsp);
} else if (is_int8(disp)) {
set_modrm(1, base);
set_disp8(disp);
} else {
set_modrm(2, base);
set_disp32(disp);
}
}
void Operand::set_modrm(int mod, Register rm) {
ASSERT((mod & -4) == 0);
buf_[0] = mod << 6 | (rm.code() & 0x7);
......
......@@ -72,7 +72,49 @@ XMMRegister xmm13 = { 13 };
XMMRegister xmm14 = { 14 };
XMMRegister xmm15 = { 15 };
Operand::Operand(Register base, int32_t disp) {
len_ = 1;
if (base.is(rsp) || base.is(r12)) {
// SIB byte is needed to encode (rsp + offset) or (r12 + offset).
set_sib(kTimes1, rsp, base);
}
if (disp == 0 && !base.is(rbp) && !base.is(r13)) {
set_modrm(0, rsp);
} else if (is_int8(disp)) {
set_modrm(1, base);
set_disp8(disp);
} else {
set_modrm(2, base);
set_disp32(disp);
}
}
Operand::Operand(Register base,
Register index,
ScaleFactor scale,
int32_t disp) {
ASSERT(!index.is(rsp) && !index.is(r12));
len_ = 1;
set_sib(scale, index, base);
if (disp == 0 && !base.is(rbp) && !base.is(r13)) {
// The call to set_modrm doesn't overwrite the REX.B bit possibly set
// by set_sib.
set_modrm(0, rsp);
} else if (is_int8(disp)) {
set_modrm(1, rsp);
set_disp8(disp);
} else {
set_modrm(2, rsp);
set_disp32(disp);
}
}
// Safe default is no features.
// TODO(X64): Safe defaults include SSE2 for X64.
uint64_t CpuFeatures::supported_ = 0;
uint64_t CpuFeatures::enabled_ = 0;
......@@ -487,6 +529,7 @@ void Assembler::call(Register adr) {
emit_modrm(0x2, adr);
}
void Assembler::cpuid() {
ASSERT(CpuFeatures::IsEnabled(CpuFeatures::CPUID));
EnsureSpace ensure_space(this);
......@@ -844,6 +887,31 @@ void Assembler::movq(Register dst, ExternalReference ref) {
}
void Assembler::movq(const Operand& dst, Immediate value) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
emit_rex_64(dst);
emit(0xC7);
emit_operand(0, dst);
emit(value);
}
void Assembler::movq(Register dst, Handle<Object> value, RelocInfo::Mode mode) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
ASSERT(!Heap::InNewSpace(*value));
emit_rex_64(dst);
emit(0xB8 | dst.code() & 0x7);
if (value->IsHeapObject()) {
emitq(reinterpret_cast<uintptr_t>(value.location()), mode);
} else {
ASSERT_EQ(RelocInfo::NONE, mode);
emitq(reinterpret_cast<uintptr_t>(*value), RelocInfo::NONE);
}
}
void Assembler::mul(Register src) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
......@@ -1129,6 +1197,7 @@ void Assembler::store_rax(ExternalReference ref) {
void Assembler::testb(Register reg, Immediate mask) {
ASSERT(is_int8(mask.value_));
EnsureSpace ensure_space(this);
last_pc_ = pc_;
if (reg.is(rax)) {
......@@ -1147,6 +1216,7 @@ void Assembler::testb(Register reg, Immediate mask) {
void Assembler::testb(const Operand& op, Immediate mask) {
ASSERT(is_int8(mask.value_));
EnsureSpace ensure_space(this);
last_pc_ = pc_;
emit_optional_rex_32(rax, op);
......@@ -1199,6 +1269,22 @@ void Assembler::testq(Register dst, Register src) {
}
void Assembler::testq(Register dst, Immediate mask) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
if (dst.is(rax)) {
emit_rex_64();
emit(0xA9);
emit(mask);
} else {
emit_rex_64(dst);
emit(0xF7);
emit_modrm(0, dst);
emit(mask);
}
}
// Relocation information implementations
void Assembler::RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data) {
......
......@@ -77,7 +77,7 @@ static inline bool is_int32(int64_t x) {
struct Register {
static Register toRegister(int code) {
Register r = {code};
Register r = { code };
return r;
}
bool is_valid() const { return 0 <= code_ && code_ < 16; }
......@@ -89,11 +89,11 @@ struct Register {
return code_;
}
int bit() const {
UNIMPLEMENTED();
return 0;
return 1 << code_;
}
// (unfortunately we can't make this private in a struct)
// (unfortunately we can't make this private in a struct when initializing
// by assignment.)
int code_;
};
......@@ -250,7 +250,7 @@ enum ScaleFactor {
class Operand BASE_EMBEDDED {
public:
// [base + disp/r]
INLINE(Operand(Register base, int32_t disp));
Operand(Register base, int32_t disp);
// [base + index*scale + disp/r]
Operand(Register base,
......@@ -434,7 +434,8 @@ class Assembler : public Malloced {
// Move 64 bit register value to 64-bit memory location.
void movq(const Operand& dst, Register src);
// Move sign extended immediate to memory location.
void movq(const Operand& dst, Immediate value);
// New x64 instructions to load a 64-bit immediate into a register.
// All 64-bit immediates must have a relocation mode.
void movq(Register dst, void* ptr, RelocInfo::Mode rmode);
......@@ -444,7 +445,6 @@ class Assembler : public Malloced {
void movq(Register dst, ExternalReference ext);
void movq(Register dst, Handle<Object> handle, RelocInfo::Mode rmode);
// New x64 instruction to load from an immediate 64-bit pointer into RAX.
void load_rax(void* ptr, RelocInfo::Mode rmode);
void load_rax(ExternalReference ext);
......@@ -647,6 +647,7 @@ class Assembler : public Malloced {
void testl(const Operand& op, Immediate mask);
void testq(const Operand& op, Register reg);
void testq(Register dst, Register src);
void testq(Register dst, Immediate mask);
void xor_(Register dst, Register src) {
arithmetic_op(0x33, dst, src);
......
......@@ -27,10 +27,13 @@
#include "v8.h"
#include "codegen-inl.h"
#include "macro-assembler.h"
namespace v8 {
namespace internal {
#define __ ACCESS_MASM(masm)
void Builtins::Generate_Adaptor(MacroAssembler* masm,
Builtins::CFunctionId id) {
masm->int3(); // UNIMPLEMENTED.
......@@ -52,12 +55,125 @@ void Builtins::Generate_JSConstructCall(MacroAssembler* masm) {
masm->int3(); // UNIMPLEMENTED.
}
void Builtins::Generate_JSConstructEntryTrampoline(MacroAssembler* masm) {
masm->int3(); // UNIMPLEMENTED.
static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm,
bool is_construct) {
// Expects five C++ function parameters.
// - Address entry (ignored)
// - JSFunction* function (
// - Object* receiver
// - int argc
// - Object*** argv
// (see Handle::Invoke in execution.cc).
// Platform specific argument handling. After this, the stack contains
// an internal frame and the pushed function and receiver, and
// register rax and rbx holds the argument count and argument array,
// while rdi holds the function pointer and rsi the context.
#ifdef __MSVC__
// MSVC parameters in:
// rcx : entry (ignored)
// rdx : function
// r8 : receiver
// r9 : argc
// [rsp+0x20] : argv
// Clear the context before we push it when entering the JS frame.
__ xor_(rsi, rsi);
// Enter an internal frame.
__ EnterInternalFrame();
// Load the function context into rsi.
__ movq(rsi, FieldOperand(rdx, JSFunction::kContextOffset));
// Push the function and the receiver onto the stack.
__ push(rdx);
__ push(r8);
// Load the number of arguments and setup pointer to the arguments.
__ movq(rax, r9);
// Load the previous frame pointer to access C argument on stack
__ movq(kScratchRegister, Operand(rbp, 0));
__ movq(rbx, Operand(kScratchRegister, EntryFrameConstants::kArgvOffset));
// Load the function pointer into rdi.
__ movq(rdi, rdx);
#else // !defined(__MSVC__)
// GCC parameters in:
// rdi : entry (ignored)
// rsi : function
// rdx : receiver
// rcx : argc
// r8 : argv
__ movq(rdi, rsi);
// rdi : function
// Clear the context before we push it when entering the JS frame.
__ xor_(rsi, rsi);
// Enter an internal frame.
__ EnterInternalFrame();
// Push the function and receiver and setup the context.
__ push(rdi);
__ push(rdx);
__ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset));
// Load the number of arguments and setup pointer to the arguments.
__ movq(rax, rcx);
__ movq(rbx, r8);
#endif // __MSVC__
// Current stack contents:
// [rsp + 2 * kPointerSize ... ]: Internal frame
// [rsp + kPointerSize] : function
// [rsp] : receiver
// Current register contents:
// rax : argc
// rbx : argv
// rsi : context
// rdi : function
// Copy arguments to the stack in a loop.
// Register rbx points to array of pointers to handle locations.
// Push the values of these handles.
Label loop, entry;
__ xor_(rcx, rcx); // Set loop variable to 0.
__ jmp(&entry);
__ bind(&loop);
__ movq(kScratchRegister, Operand(rbx, rcx, kTimesPointerSize, 0));
__ push(Operand(kScratchRegister, 0)); // dereference handle
__ add(rcx, Immediate(1));
__ bind(&entry);
__ cmp(rcx, rax);
__ j(not_equal, &loop);
// Invoke the code.
if (is_construct) {
// Expects rdi to hold function pointer.
__ movq(kScratchRegister,
Handle<Code>(Builtins::builtin(Builtins::JSConstructCall)),
RelocInfo::CODE_TARGET);
__ call(kScratchRegister);
} else {
ParameterCount actual(rax);
__ InvokeFunction(rdi, actual, CALL_FUNCTION);
}
// Exit the JS frame. Notice that this also removes the empty
// context and the function left on the stack by the code
// invocation.
__ LeaveInternalFrame();
// TODO(X64): Is argument correct? Is there a receiver to remove?
__ ret(1 * kPointerSize); // remove receiver
}
void Builtins::Generate_JSEntryTrampoline(MacroAssembler* masm) {
masm->int3(); // UNIMPLEMENTED.
Generate_JSEntryTrampolineHelper(masm, false);
}
void Builtins::Generate_JSConstructEntryTrampoline(MacroAssembler* masm) {
Generate_JSEntryTrampolineHelper(masm, true);
}
} } // namespace v8::internal
......
......@@ -58,7 +58,7 @@ CodeGenerator::CodeGenerator(int buffer_size,
in_spilled_code_(false) {
}
#define __ masm->
#define __ ACCESS_MASM(masm)
void CodeGenerator::DeclareGlobals(Handle<FixedArray> a) {
......@@ -236,8 +236,250 @@ void CodeGenerator::VisitThisFunction(ThisFunction* a) {
}
void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) {
// Check that stack should contain frame pointer, code pointer, state and
// return address in that order.
ASSERT_EQ(StackHandlerConstants::kFPOffset + kPointerSize,
StackHandlerConstants::kStateOffset);
ASSERT_EQ(StackHandlerConstants::kStateOffset + kPointerSize,
StackHandlerConstants::kPCOffset);
ExternalReference handler_address(Top::k_handler_address);
__ movq(kScratchRegister, handler_address);
__ movq(rdx, Operand(kScratchRegister, 0));
// get next in chain
__ movq(rcx, Operand(rdx, 0));
__ movq(Operand(kScratchRegister, 0), rcx);
__ movq(rsp, rdx);
__ pop(rbp); // pop frame pointer
__ pop(rdx); // remove code pointer
__ pop(rdx); // remove state
// Before returning we restore the context from the frame pointer if not NULL.
// The frame pointer is NULL in the exception handler of a JS entry frame.
__ xor_(rsi, rsi); // tentatively set context pointer to NULL
Label skip;
__ cmp(rbp, Immediate(0));
__ j(equal, &skip);
__ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
__ bind(&skip);
__ ret(0);
}
void CEntryStub::GenerateCore(MacroAssembler* masm,
Label* throw_normal_exception,
Label* throw_out_of_memory_exception,
StackFrame::Type frame_type,
bool do_gc,
bool always_allocate_scope) {
// rax: result parameter for PerformGC, if any
// rbx: pointer to C function (C callee-saved)
// rbp: frame pointer (restored after C call)
// rsp: stack pointer (restored after C call)
// rdi: number of arguments including receiver (C callee-saved)
// rsi: pointer to the first argument (C callee-saved)
if (do_gc) {
__ movq(Operand(rsp, 0), rax); // Result.
__ movq(kScratchRegister,
FUNCTION_ADDR(Runtime::PerformGC),
RelocInfo::RUNTIME_ENTRY);
__ call(kScratchRegister);
}
ExternalReference scope_depth =
ExternalReference::heap_always_allocate_scope_depth();
if (always_allocate_scope) {
__ movq(kScratchRegister, scope_depth);
__ inc(Operand(kScratchRegister, 0));
}
// Call C function.
#ifdef __MSVC__
// MSVC passes arguments in rcx, rdx, r8, r9
__ movq(rcx, rdi); // argc.
__ movq(rdx, rsi); // argv.
#else // ! defined(__MSVC__)
// GCC passes arguments in rdi, rsi, rdx, rcx, r8, r9.
// First two arguments are already in rdi, rsi.
#endif
__ call(rbx);
// Result is in rax - do not destroy this register!
if (always_allocate_scope) {
__ movq(kScratchRegister, scope_depth);
__ dec(Operand(kScratchRegister, 0));
}
// Check for failure result.
Label failure_returned;
ASSERT(((kFailureTag + 1) & kFailureTagMask) == 0);
__ lea(rcx, Operand(rax, 1));
// Lower 2 bits of rcx are 0 iff rax has failure tag.
__ testl(rcx, Immediate(kFailureTagMask));
__ j(zero, &failure_returned);
// Exit the JavaScript to C++ exit frame.
__ LeaveExitFrame(frame_type);
__ ret(0);
// Handling of failure.
__ bind(&failure_returned);
Label retry;
// If the returned exception is RETRY_AFTER_GC continue at retry label
ASSERT(Failure::RETRY_AFTER_GC == 0);
__ testq(rax, Immediate(((1 << kFailureTypeTagSize) - 1) << kFailureTagSize));
__ j(zero, &retry);
Label continue_exception;
// If the returned failure is EXCEPTION then promote Top::pending_exception().
__ movq(kScratchRegister, Failure::Exception(), RelocInfo::NONE);
__ cmp(rax, kScratchRegister);
__ j(not_equal, &continue_exception);
// Retrieve the pending exception and clear the variable.
ExternalReference pending_exception_address(Top::k_pending_exception_address);
__ movq(kScratchRegister, pending_exception_address);
__ movq(rax, Operand(kScratchRegister, 0));
__ movq(rdx, ExternalReference::the_hole_value_location());
__ movq(rdx, Operand(rdx, 0));
__ movq(Operand(kScratchRegister, 0), rdx);
__ bind(&continue_exception);
// Special handling of out of memory exception.
__ movq(kScratchRegister, Failure::OutOfMemoryException(), RelocInfo::NONE);
__ cmp(rax, kScratchRegister);
__ j(equal, throw_out_of_memory_exception);
// Handle normal exception.
__ jmp(throw_normal_exception);
// Retry.
__ bind(&retry);
}
void CEntryStub::GenerateThrowOutOfMemory(MacroAssembler* masm) {
// Fetch top stack handler.
ExternalReference handler_address(Top::k_handler_address);
__ movq(kScratchRegister, handler_address);
__ movq(rdx, Operand(kScratchRegister, 0));
// Unwind the handlers until the ENTRY handler is found.
Label loop, done;
__ bind(&loop);
// Load the type of the current stack handler.
__ cmp(Operand(rdx, StackHandlerConstants::kStateOffset),
Immediate(StackHandler::ENTRY));
__ j(equal, &done);
// Fetch the next handler in the list.
__ movq(rdx, Operand(rdx, StackHandlerConstants::kNextOffset));
__ jmp(&loop);
__ bind(&done);
// Set the top handler address to next handler past the current ENTRY handler.
__ movq(rax, Operand(rdx, StackHandlerConstants::kNextOffset));
__ store_rax(handler_address);
// Set external caught exception to false.
__ movq(rax, Immediate(false));
ExternalReference external_caught(Top::k_external_caught_exception_address);
__ store_rax(external_caught);
// Set pending exception and rax to out of memory exception.
__ movq(rax, Failure::OutOfMemoryException(), RelocInfo::NONE);
ExternalReference pending_exception(Top::k_pending_exception_address);
__ store_rax(pending_exception);
// Restore the stack to the address of the ENTRY handler
__ movq(rsp, rdx);
// Clear the context pointer;
__ xor_(rsi, rsi);
// Restore registers from handler.
__ pop(rbp); // FP
ASSERT_EQ(StackHandlerConstants::kFPOffset + kPointerSize,
StackHandlerConstants::kStateOffset);
__ pop(rdx); // State
ASSERT_EQ(StackHandlerConstants::kStateOffset + kPointerSize,
StackHandlerConstants::kPCOffset);
__ ret(0);
}
void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) {
masm->int3(); // TODO(X64): UNIMPLEMENTED.
// rax: number of arguments including receiver
// rbx: pointer to C function (C callee-saved)
// rbp: frame pointer (restored after C call)
// rsp: stack pointer (restored after C call)
// rsi: current context (C callee-saved)
// rdi: caller's parameter pointer pp (C callee-saved)
// NOTE: Invocations of builtins may return failure objects
// instead of a proper result. The builtin entry handles
// this by performing a garbage collection and retrying the
// builtin once.
StackFrame::Type frame_type = is_debug_break ?
StackFrame::EXIT_DEBUG :
StackFrame::EXIT;
// Enter the exit frame that transitions from JavaScript to C++.
__ EnterExitFrame(frame_type);
// rax: result parameter for PerformGC, if any (setup below)
// rbx: pointer to builtin function (C callee-saved)
// rbp: frame pointer (restored after C call)
// rsp: stack pointer (restored after C call)
// rdi: number of arguments including receiver (C callee-saved)
// rsi: argv pointer (C callee-saved)
Label throw_out_of_memory_exception;
Label throw_normal_exception;
// Call into the runtime system. Collect garbage before the call if
// running with --gc-greedy set.
if (FLAG_gc_greedy) {
Failure* failure = Failure::RetryAfterGC(0);
__ movq(rax, failure, RelocInfo::NONE);
}
GenerateCore(masm, &throw_normal_exception,
&throw_out_of_memory_exception,
frame_type,
FLAG_gc_greedy,
false);
// Do space-specific GC and retry runtime call.
GenerateCore(masm,
&throw_normal_exception,
&throw_out_of_memory_exception,
frame_type,
true,
false);
// Do full GC and retry runtime call one final time.
Failure* failure = Failure::InternalError();
__ movq(rax, failure, RelocInfo::NONE);
GenerateCore(masm,
&throw_normal_exception,
&throw_out_of_memory_exception,
frame_type,
true,
true);
__ bind(&throw_out_of_memory_exception);
GenerateThrowOutOfMemory(masm);
// control flow for generated will not return.
__ bind(&throw_normal_exception);
GenerateThrowTOS(masm);
}
......
......@@ -32,11 +32,18 @@ namespace v8 {
namespace internal {
// TODO(x64): This is a stub, mostly just a copy of the ia32 bit version.
// This will all need to change to be correct for x64.
// This might all need to change to be correct for x64.
static const int kNumRegs = 8;
static const RegList kJSCallerSaved = 0;
static const RegList kJSCallerSaved =
1 << 0 | // rax
1 << 1 | // rcx
1 << 2 | // rdx
1 << 3 | // rbx - used as a caller-saved register in JavaScript code
1 << 7; // rdi - callee function
static const int kNumJSCallerSaved = 5;
typedef Object* JSCallerSavedBuffer[kNumJSCallerSaved];
class StackHandlerConstants : public AllStatic {
......@@ -52,24 +59,26 @@ class StackHandlerConstants : public AllStatic {
class EntryFrameConstants : public AllStatic {
public:
static const int kCallerFPOffset = -1 * kPointerSize;
static const int kCallerFPOffset = 0 * kPointerSize;
static const int kFunctionArgOffset = -1 * kPointerSize;
static const int kReceiverArgOffset = -1 * kPointerSize;
static const int kArgcOffset = -1 * kPointerSize;
static const int kArgvOffset = -1 * kPointerSize;
static const int kFunctionArgOffset = 1 * kPointerSize;
static const int kReceiverArgOffset = 2 * kPointerSize;
static const int kArgcOffset = 3 * kPointerSize;
static const int kArgvOffset = 4 * kPointerSize;
};
class ExitFrameConstants : public AllStatic {
public:
static const int kDebugMarkOffset = -1 * kPointerSize;
static const int kDebugMarkOffset = -2 * kPointerSize;
static const int kSPOffset = -1 * kPointerSize;
static const int kPPDisplacement = -1 * kPointerSize;
// TODO(X64): Remove usage of PP in exit frames?
// Still used though StackFrame::pp()
static const int kPPDisplacement = +2 * kPointerSize;
static const int kCallerFPOffset = -1 * kPointerSize;
static const int kCallerPCOffset = -1 * kPointerSize;
static const int kCallerFPOffset = +0 * kPointerSize;
static const int kCallerPCOffset = +1 * kPointerSize;
};
......
......@@ -29,7 +29,9 @@
#include "bootstrapper.h"
#include "codegen-inl.h"
#include "assembler-x64.h"
#include "macro-assembler-x64.h"
#include "debug.h"
namespace v8 {
namespace internal {
......@@ -43,6 +45,53 @@ MacroAssembler::MacroAssembler(void* buffer, int size)
}
void MacroAssembler::Assert(Condition cc, const char* msg) {
if (FLAG_debug_code) Check(cc, msg);
}
void MacroAssembler::Check(Condition cc, const char* msg) {
Label L;
j(cc, &L);
Abort(msg);
// will not return here
bind(&L);
}
void MacroAssembler::Abort(const char* msg) {
// We want to pass the msg string like a smi to avoid GC
// problems, however msg is not guaranteed to be aligned
// properly. Instead, we pass an aligned pointer that is
// a proper v8 smi, but also pass the alignment difference
// from the real pointer as a smi.
intptr_t p1 = reinterpret_cast<intptr_t>(msg);
intptr_t p0 = (p1 & ~kSmiTagMask) + kSmiTag;
// Note: p0 might not be a valid Smi *value*, but it has a valid Smi tag.
ASSERT(reinterpret_cast<Object*>(p0)->IsSmi());
#ifdef DEBUG
if (msg != NULL) {
RecordComment("Abort message: ");
RecordComment(msg);
}
#endif
push(rax);
movq(kScratchRegister, p0, RelocInfo::NONE);
push(kScratchRegister);
movq(kScratchRegister,
reinterpret_cast<intptr_t>(Smi::FromInt(p1 - p0)),
RelocInfo::NONE);
push(kScratchRegister);
CallRuntime(Runtime::kAbort, 2);
// will not return here
}
void MacroAssembler::CallRuntime(Runtime::FunctionId id, int argc) {
UNIMPLEMENTED();
}
void MacroAssembler::TailCallRuntime(ExternalReference const& a, int b) {
UNIMPLEMENTED();
}
......@@ -109,4 +158,228 @@ void MacroAssembler::PushTryHandler(CodeLocation try_location,
}
#ifdef ENABLE_DEBUGGER_SUPPORT
void MacroAssembler::PushRegistersFromMemory(RegList regs) {
ASSERT((regs & ~kJSCallerSaved) == 0);
// Push the content of the memory location to the stack.
for (int i = 0; i < kNumJSCallerSaved; i++) {
int r = JSCallerSavedCode(i);
if ((regs & (1 << r)) != 0) {
ExternalReference reg_addr =
ExternalReference(Debug_Address::Register(i));
movq(kScratchRegister, reg_addr);
push(Operand(kScratchRegister, 0));
}
}
}
void MacroAssembler::SaveRegistersToMemory(RegList regs) {
ASSERT((regs & ~kJSCallerSaved) == 0);
// Copy the content of registers to memory location.
for (int i = 0; i < kNumJSCallerSaved; i++) {
int r = JSCallerSavedCode(i);
if ((regs & (1 << r)) != 0) {
Register reg = { r };
ExternalReference reg_addr =
ExternalReference(Debug_Address::Register(i));
movq(kScratchRegister, reg_addr);
movq(Operand(kScratchRegister, 0), reg);
}
}
}
void MacroAssembler::RestoreRegistersFromMemory(RegList regs) {
ASSERT((regs & ~kJSCallerSaved) == 0);
// Copy the content of memory location to registers.
for (int i = kNumJSCallerSaved - 1; i >= 0; i--) {
int r = JSCallerSavedCode(i);
if ((regs & (1 << r)) != 0) {
Register reg = { r };
ExternalReference reg_addr =
ExternalReference(Debug_Address::Register(i));
movq(kScratchRegister, reg_addr);
movq(reg, Operand(kScratchRegister, 0));
}
}
}
void MacroAssembler::PopRegistersToMemory(RegList regs) {
ASSERT((regs & ~kJSCallerSaved) == 0);
// Pop the content from the stack to the memory location.
for (int i = kNumJSCallerSaved - 1; i >= 0; i--) {
int r = JSCallerSavedCode(i);
if ((regs & (1 << r)) != 0) {
ExternalReference reg_addr =
ExternalReference(Debug_Address::Register(i));
movq(kScratchRegister, reg_addr);
pop(Operand(kScratchRegister, 0));
}
}
}
void MacroAssembler::CopyRegistersFromStackToMemory(Register base,
Register scratch,
RegList regs) {
ASSERT(!scratch.is(kScratchRegister));
ASSERT(!base.is(kScratchRegister));
ASSERT(!base.is(scratch));
ASSERT((regs & ~kJSCallerSaved) == 0);
// Copy the content of the stack to the memory location and adjust base.
for (int i = kNumJSCallerSaved - 1; i >= 0; i--) {
int r = JSCallerSavedCode(i);
if ((regs & (1 << r)) != 0) {
movq(scratch, Operand(base, 0));
ExternalReference reg_addr =
ExternalReference(Debug_Address::Register(i));
movq(kScratchRegister, reg_addr);
movq(Operand(kScratchRegister, 0), scratch);
lea(base, Operand(base, kPointerSize));
}
}
}
#endif // ENABLE_DEBUGGER_SUPPORT
void MacroAssembler::InvokeFunction(Register fun,
const ParameterCount& actual,
InvokeFlag flag) {
UNIMPLEMENTED();
}
void MacroAssembler::EnterFrame(StackFrame::Type type) {
push(rbp);
movq(rbp, rsp);
push(rsi); // Context.
push(Immediate(Smi::FromInt(type)));
movq(kScratchRegister, CodeObject(), RelocInfo::EMBEDDED_OBJECT);
push(kScratchRegister);
if (FLAG_debug_code) {
movq(kScratchRegister,
Factory::undefined_value(),
RelocInfo::EMBEDDED_OBJECT);
cmp(Operand(rsp, 0), kScratchRegister);
Check(not_equal, "code object not properly patched");
}
}
void MacroAssembler::LeaveFrame(StackFrame::Type type) {
if (FLAG_debug_code) {
movq(kScratchRegister, Immediate(Smi::FromInt(type)));
cmp(Operand(rbp, StandardFrameConstants::kMarkerOffset), kScratchRegister);
Check(equal, "stack frame types must match");
}
movq(rsp, rbp);
pop(rbp);
}
void MacroAssembler::EnterExitFrame(StackFrame::Type type) {
ASSERT(type == StackFrame::EXIT || type == StackFrame::EXIT_DEBUG);
// Setup the frame structure on the stack.
ASSERT(ExitFrameConstants::kPPDisplacement == +2 * kPointerSize);
ASSERT(ExitFrameConstants::kCallerPCOffset == +1 * kPointerSize);
ASSERT(ExitFrameConstants::kCallerFPOffset == 0 * kPointerSize);
push(rbp);
movq(rbp, rsp);
// Reserve room for entry stack pointer and push the debug marker.
ASSERT(ExitFrameConstants::kSPOffset == -1 * kPointerSize);
push(Immediate(0)); // saved entry sp, patched before call
push(Immediate(type == StackFrame::EXIT_DEBUG ? 1 : 0));
// Save the frame pointer and the context in top.
ExternalReference c_entry_fp_address(Top::k_c_entry_fp_address);
ExternalReference context_address(Top::k_context_address);
movq(kScratchRegister, rax);
movq(rax, rbp);
store_rax(c_entry_fp_address);
movq(rax, rsi);
store_rax(context_address);
movq(rax, kScratchRegister);
// Setup argc and argv in callee-saved registers.
int offset = StandardFrameConstants::kCallerSPOffset - kPointerSize;
movq(rdi, rax);
lea(rsi, Operand(rbp, rax, kTimesPointerSize, offset));
#ifdef ENABLE_DEBUGGER_SUPPORT
// Save the state of all registers to the stack from the memory
// location. This is needed to allow nested break points.
if (type == StackFrame::EXIT_DEBUG) {
// TODO(1243899): This should be symmetric to
// CopyRegistersFromStackToMemory() but it isn't! esp is assumed
// correct here, but computed for the other call. Very error
// prone! FIX THIS. Actually there are deeper problems with
// register saving than this asymmetry (see the bug report
// associated with this issue).
PushRegistersFromMemory(kJSCallerSaved);
}
#endif
// Reserve space for two arguments: argc and argv.
sub(rsp, Immediate(2 * kPointerSize));
// Get the required frame alignment for the OS.
static const int kFrameAlignment = OS::ActivationFrameAlignment();
if (kFrameAlignment > 0) {
ASSERT(IsPowerOf2(kFrameAlignment));
movq(r10, Immediate(-kFrameAlignment));
and_(rsp, r10);
}
// Patch the saved entry sp.
movq(Operand(rbp, ExitFrameConstants::kSPOffset), rsp);
}
void MacroAssembler::LeaveExitFrame(StackFrame::Type type) {
#ifdef ENABLE_DEBUGGER_SUPPORT
// Restore the memory copy of the registers by digging them out from
// the stack. This is needed to allow nested break points.
if (type == StackFrame::EXIT_DEBUG) {
// It's okay to clobber register ebx below because we don't need
// the function pointer after this.
const int kCallerSavedSize = kNumJSCallerSaved * kPointerSize;
int kOffset = ExitFrameConstants::kDebugMarkOffset - kCallerSavedSize;
lea(rbx, Operand(rbp, kOffset));
CopyRegistersFromStackToMemory(rbx, rcx, kJSCallerSaved);
}
#endif
// Get the return address from the stack and restore the frame pointer.
movq(rcx, Operand(rbp, 1 * kPointerSize));
movq(rbp, Operand(rbp, 0 * kPointerSize));
// Pop the arguments and the receiver from the caller stack.
lea(rsp, Operand(rsi, 1 * kPointerSize));
// Restore current context from top and clear it in debug mode.
ExternalReference context_address(Top::k_context_address);
movq(kScratchRegister, context_address);
movq(rsi, Operand(kScratchRegister, 0));
#ifdef DEBUG
movq(Operand(kScratchRegister, 0), Immediate(0));
#endif
// Push the return address to get ready to return.
push(rcx);
// Clear the top frame.
ExternalReference c_entry_fp_address(Top::k_c_entry_fp_address);
movq(kScratchRegister, c_entry_fp_address);
movq(Operand(kScratchRegister, 0), Immediate(0));
}
} } // namespace v8::internal
......@@ -31,6 +31,7 @@
// Since there is no simulator for the ia32 architecture the only thing we can
// do is to call the entry directly.
// TODO(X64): Don't pass p0, since it isn't used?
#define CALL_GENERATED_CODE(entry, p0, p1, p2, p3, p4) \
entry(p0, p1, p2, p3, p4);
......
......@@ -248,4 +248,5 @@ TEST(AssemblerX64LoopImmediates) {
int result = FUNCTION_CAST<F0>(buffer)();
CHECK_EQ(1, result);
}
#undef __
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment