Author: l...@chromium.org Date: Wed Jun 10 02:48:15 2009 New Revision: 2130
Modified: branches/bleeding_edge/src/ia32/macro-assembler-ia32.h branches/bleeding_edge/src/x64/assembler-x64-inl.h branches/bleeding_edge/src/x64/assembler-x64.cc branches/bleeding_edge/src/x64/assembler-x64.h branches/bleeding_edge/src/x64/builtins-x64.cc branches/bleeding_edge/src/x64/codegen-x64.cc branches/bleeding_edge/src/x64/frames-x64.h branches/bleeding_edge/src/x64/macro-assembler-x64.cc branches/bleeding_edge/src/x64/simulator-x64.h branches/bleeding_edge/test/cctest/test-assembler-x64.cc Log: X64: Implement CEntryStub and JSEntryTrampoline. Still some supporting functions missing. Review URL: http://codereview.chromium.org/114085 Modified: branches/bleeding_edge/src/ia32/macro-assembler-ia32.h ============================================================================== --- branches/bleeding_edge/src/ia32/macro-assembler-ia32.h (original) +++ branches/bleeding_edge/src/ia32/macro-assembler-ia32.h Wed Jun 10 02:48:15 2009 @@ -285,7 +285,7 @@ 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. Modified: branches/bleeding_edge/src/x64/assembler-x64-inl.h ============================================================================== --- branches/bleeding_edge/src/x64/assembler-x64-inl.h (original) +++ branches/bleeding_edge/src/x64/assembler-x64-inl.h Wed Jun 10 02:48:15 2009 @@ -249,24 +249,6 @@ // ----------------------------------------------------------------------------- // 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); Modified: branches/bleeding_edge/src/x64/assembler-x64.cc ============================================================================== --- branches/bleeding_edge/src/x64/assembler-x64.cc (original) +++ branches/bleeding_edge/src/x64/assembler-x64.cc Wed Jun 10 02:48:15 2009 @@ -72,7 +72,49 @@ 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 @@ emit_modrm(0x2, adr); } + void Assembler::cpuid() { ASSERT(CpuFeatures::IsEnabled(CpuFeatures::CPUID)); EnsureSpace ensure_space(this); @@ -844,6 +887,31 @@ } +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::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(const Operand& op, Immediate mask) { + ASSERT(is_int8(mask.value_)); EnsureSpace ensure_space(this); last_pc_ = pc_; emit_optional_rex_32(rax, op); @@ -1196,6 +1266,22 @@ emit_rex_64(dst, src); emit(0x85); emit_modrm(dst, 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); + } } Modified: branches/bleeding_edge/src/x64/assembler-x64.h ============================================================================== --- branches/bleeding_edge/src/x64/assembler-x64.h (original) +++ branches/bleeding_edge/src/x64/assembler-x64.h Wed Jun 10 02:48:15 2009 @@ -77,7 +77,7 @@ 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 @@ 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 @@ 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 @@ // 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 @@ 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 @@ 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); Modified: branches/bleeding_edge/src/x64/builtins-x64.cc ============================================================================== --- branches/bleeding_edge/src/x64/builtins-x64.cc (original) +++ branches/bleeding_edge/src/x64/builtins-x64.cc Wed Jun 10 02:48:15 2009 @@ -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 @@ 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 Modified: branches/bleeding_edge/src/x64/codegen-x64.cc ============================================================================== --- branches/bleeding_edge/src/x64/codegen-x64.cc (original) +++ branches/bleeding_edge/src/x64/codegen-x64.cc Wed Jun 10 02:48:15 2009 @@ -58,7 +58,7 @@ in_spilled_code_(false) { } -#define __ masm-> +#define __ ACCESS_MASM(masm) void CodeGenerator::DeclareGlobals(Handle<FixedArray> a) { @@ -236,8 +236,250 @@ } +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); } Modified: branches/bleeding_edge/src/x64/frames-x64.h ============================================================================== --- branches/bleeding_edge/src/x64/frames-x64.h (original) +++ branches/bleeding_edge/src/x64/frames-x64.h Wed Jun 10 02:48:15 2009 @@ -32,11 +32,18 @@ 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 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; }; Modified: branches/bleeding_edge/src/x64/macro-assembler-x64.cc ============================================================================== --- branches/bleeding_edge/src/x64/macro-assembler-x64.cc (original) +++ branches/bleeding_edge/src/x64/macro-assembler-x64.cc Wed Jun 10 02:48:15 2009 @@ -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 @@ } +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(); } @@ -106,6 +155,230 @@ push(Operand(kScratchRegister, 0)); // Link this handler. movq(Operand(kScratchRegister, 0), rsp); +} + + +#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)); } Modified: branches/bleeding_edge/src/x64/simulator-x64.h ============================================================================== --- branches/bleeding_edge/src/x64/simulator-x64.h (original) +++ branches/bleeding_edge/src/x64/simulator-x64.h Wed Jun 10 02:48:15 2009 @@ -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); Modified: branches/bleeding_edge/test/cctest/test-assembler-x64.cc ============================================================================== --- branches/bleeding_edge/test/cctest/test-assembler-x64.cc (original) +++ branches/bleeding_edge/test/cctest/test-assembler-x64.cc Wed Jun 10 02:48:15 2009 @@ -248,4 +248,5 @@ int result = FUNCTION_CAST<F0>(buffer)(); CHECK_EQ(1, result); } + #undef __ --~--~---------~--~----~------------~-------~--~----~ v8-dev mailing list v8-dev@googlegroups.com http://groups.google.com/group/v8-dev -~----------~----~----~----~------~----~------~--~---