From 9d6fd08276833453a3fd165323cd90aef735fa2c Mon Sep 17 00:00:00 2001 From: "antonm@chromium.org" Date: Wed, 28 Jul 2010 09:36:53 +0000 Subject: [PATCH] Port faster callbacks invocation to x64. It's a port of http://code.google.com/p/v8/source/detail?r=3209 to x64 platform. That allows invocation of callbacks without going into runtime. Review URL: http://codereview.chromium.org/2801008 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@5141 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/ia32/codegen-ia32.cc | 14 ++-- src/ia32/macro-assembler-ia32.cc | 31 ++++--- src/ia32/macro-assembler-ia32.h | 8 +- src/ia32/stub-cache-ia32.cc | 21 +++-- src/x64/assembler-x64.cc | 8 +- src/x64/assembler-x64.h | 1 - src/x64/codegen-x64.cc | 43 +++++++++- src/x64/macro-assembler-x64.cc | 172 +++++++++++++++++++++++++++++++++++++-- src/x64/macro-assembler-x64.h | 43 ++++++++++ src/x64/stub-cache-x64.cc | 81 +++++++++++++++--- 10 files changed, 355 insertions(+), 67 deletions(-) diff --git a/src/ia32/codegen-ia32.cc b/src/ia32/codegen-ia32.cc index 9a11075..fd064c1 100644 --- a/src/ia32/codegen-ia32.cc +++ b/src/ia32/codegen-ia32.cc @@ -12275,7 +12275,7 @@ static const bool kPassHandlesDirectly = false; void ApiGetterEntryStub::Generate(MacroAssembler* masm) { - Label get_result; + Label empty_handle; Label prologue; Label promote_scheduled_exception; __ EnterApiExitFrame(ExitFrame::MODE_NORMAL, kStackSpace, kArgc); @@ -12318,20 +12318,20 @@ void ApiGetterEntryStub::Generate(MacroAssembler* masm) { // Dereference this to get to the location. __ mov(eax, Operand(eax, 0)); } - // Check if the result handle holds 0 + // Check if the result handle holds 0. __ test(eax, Operand(eax)); - __ j(not_zero, &get_result, taken); - // It was zero; the result is undefined. - __ mov(eax, Factory::undefined_value()); - __ jmp(&prologue); + __ j(zero, &empty_handle, not_taken); // It was non-zero. Dereference to get the result value. - __ bind(&get_result); __ mov(eax, Operand(eax, 0)); __ bind(&prologue); __ LeaveExitFrame(ExitFrame::MODE_NORMAL); __ ret(0); __ bind(&promote_scheduled_exception); __ TailCallRuntime(Runtime::kPromoteScheduledException, 0, 1); + __ bind(&empty_handle); + // It was zero; the result is undefined. + __ mov(eax, Factory::undefined_value()); + __ jmp(&prologue); } diff --git a/src/ia32/macro-assembler-ia32.cc b/src/ia32/macro-assembler-ia32.cc index bb613ed..697aa60 100644 --- a/src/ia32/macro-assembler-ia32.cc +++ b/src/ia32/macro-assembler-ia32.cc @@ -1052,16 +1052,6 @@ void MacroAssembler::CallRuntime(Runtime::Function* f, int num_arguments) { } -void MacroAssembler::CallExternalReference(ExternalReference ref, - int num_arguments) { - mov(eax, Immediate(num_arguments)); - mov(ebx, Immediate(ref)); - - CEntryStub stub(1); - CallStub(&stub); -} - - Object* MacroAssembler::TryCallRuntime(Runtime::Function* f, int num_arguments) { if (f->nargs >= 0 && f->nargs != num_arguments) { @@ -1082,6 +1072,16 @@ Object* MacroAssembler::TryCallRuntime(Runtime::Function* f, } +void MacroAssembler::CallExternalReference(ExternalReference ref, + int num_arguments) { + mov(eax, Immediate(num_arguments)); + mov(ebx, Immediate(ref)); + + CEntryStub stub(1); + CallStub(&stub); +} + + void MacroAssembler::TailCallExternalReference(const ExternalReference& ext, int num_arguments, int result_size) { @@ -1106,8 +1106,7 @@ void MacroAssembler::PushHandleScope(Register scratch) { ExternalReference extensions_address = ExternalReference::handle_scope_extensions_address(); mov(scratch, Operand::StaticVariable(extensions_address)); - ASSERT_EQ(0, kSmiTag); - shl(scratch, kSmiTagSize); + SmiTag(scratch); push(scratch); mov(Operand::StaticVariable(extensions_address), Immediate(0)); // Push next and limit pointers which will be wordsize aligned and @@ -1131,16 +1130,14 @@ Object* MacroAssembler::PopHandleScopeHelper(Register saved, mov(scratch, Operand::StaticVariable(extensions_address)); cmp(Operand(scratch), Immediate(0)); j(equal, &write_back); - // Calling a runtime function messes with registers so we save and - // restore any one we're asked not to change - if (saved.is_valid()) push(saved); + push(saved); if (gc_allowed) { CallRuntime(Runtime::kDeleteHandleScopeExtensions, 0); } else { result = TryCallRuntime(Runtime::kDeleteHandleScopeExtensions, 0); if (result->IsFailure()) return result; } - if (saved.is_valid()) pop(saved); + pop(saved); bind(&write_back); ExternalReference limit_address = @@ -1150,7 +1147,7 @@ Object* MacroAssembler::PopHandleScopeHelper(Register saved, ExternalReference::handle_scope_next_address(); pop(Operand::StaticVariable(next_address)); pop(scratch); - shr(scratch, kSmiTagSize); + SmiUntag(scratch); mov(Operand::StaticVariable(extensions_address), scratch); return result; diff --git a/src/ia32/macro-assembler-ia32.h b/src/ia32/macro-assembler-ia32.h index 02cfd4d..a17a2b4 100644 --- a/src/ia32/macro-assembler-ia32.h +++ b/src/ia32/macro-assembler-ia32.h @@ -393,12 +393,12 @@ class MacroAssembler: public Assembler { // Convenience function: Same as above, but takes the fid instead. void CallRuntime(Runtime::FunctionId id, int num_arguments); - // Convenience function: call an external reference. - void CallExternalReference(ExternalReference ref, int num_arguments); - // Convenience function: Same as above, but takes the fid instead. Object* TryCallRuntime(Runtime::FunctionId id, int num_arguments); + // Convenience function: call an external reference. + void CallExternalReference(ExternalReference ref, int num_arguments); + // Tail call of a runtime routine (jump). // Like JumpToExternalReference, but also takes care of passing the number // of parameters. @@ -431,7 +431,7 @@ class MacroAssembler: public Assembler { void PushHandleScope(Register scratch); // Pops a handle scope using the specified scratch register and - // ensuring that saved register, it is not no_reg, is left unchanged. + // ensuring that saved register is left unchanged. void PopHandleScope(Register saved, Register scratch); // As PopHandleScope, but does not perform a GC. Instead, returns a diff --git a/src/ia32/stub-cache-ia32.cc b/src/ia32/stub-cache-ia32.cc index e81fbc7..48f08a9 100644 --- a/src/ia32/stub-cache-ia32.cc +++ b/src/ia32/stub-cache-ia32.cc @@ -1033,24 +1033,23 @@ bool StubCompiler::GenerateLoadCallback(JSObject* object, // Check that the maps haven't changed. Register reg = - CheckPrototypes(object, receiver, holder, - scratch1, scratch2, scratch3, name, miss); + CheckPrototypes(object, receiver, holder, scratch1, + scratch2, scratch3, name, miss); Handle callback_handle(callback); - Register other = reg.is(scratch1) ? scratch2 : scratch1; __ EnterInternalFrame(); - __ PushHandleScope(other); - // Push the stack address where the list of arguments ends - __ mov(other, esp); - __ sub(Operand(other), Immediate(2 * kPointerSize)); - __ push(other); + __ PushHandleScope(scratch2); + // Push the stack address where the list of arguments ends. + __ mov(scratch2, esp); + __ sub(Operand(scratch2), Immediate(2 * kPointerSize)); + __ push(scratch2); __ push(receiver); // receiver __ push(reg); // holder // Push data from AccessorInfo. if (Heap::InNewSpace(callback_handle->data())) { - __ mov(other, Immediate(callback_handle)); - __ push(FieldOperand(other, AccessorInfo::kDataOffset)); + __ mov(scratch2, Immediate(callback_handle)); + __ push(FieldOperand(scratch2, AccessorInfo::kDataOffset)); } else { __ push(Immediate(Handle(callback_handle->data()))); } @@ -1077,7 +1076,7 @@ bool StubCompiler::GenerateLoadCallback(JSObject* object, } // We need to avoid using eax since that now holds the result. - Register tmp = other.is(eax) ? reg : other; + Register tmp = scratch2.is(eax) ? reg : scratch2; // Emitting PopHandleScope may try to allocate. Do not allow the // assembler to perform a garbage collection but instead return a // failure object. diff --git a/src/x64/assembler-x64.cc b/src/x64/assembler-x64.cc index 40899ab..b62db14 100644 --- a/src/x64/assembler-x64.cc +++ b/src/x64/assembler-x64.cc @@ -1496,12 +1496,8 @@ void Assembler::movq(Register dst, int64_t value, RelocInfo::Mode rmode) { void Assembler::movq(Register dst, ExternalReference ref) { - EnsureSpace ensure_space(this); - last_pc_ = pc_; - emit_rex_64(dst); - emit(0xB8 | dst.low_bits()); - emitq(reinterpret_cast(ref.address()), - RelocInfo::EXTERNAL_REFERENCE); + int64_t value = reinterpret_cast(ref.address()); + movq(dst, value, RelocInfo::EXTERNAL_REFERENCE); } diff --git a/src/x64/assembler-x64.h b/src/x64/assembler-x64.h index 213db2c..7082af7 100644 --- a/src/x64/assembler-x64.h +++ b/src/x64/assembler-x64.h @@ -509,7 +509,6 @@ class Assembler : public Malloced { void push(Immediate value); void push(Register src); void push(const Operand& src); - void push(Label* label, RelocInfo::Mode relocation_mode); void pop(Register dst); void pop(const Operand& dst); diff --git a/src/x64/codegen-x64.cc b/src/x64/codegen-x64.cc index 35c1a3d..1567255 100644 --- a/src/x64/codegen-x64.cc +++ b/src/x64/codegen-x64.cc @@ -10890,7 +10890,48 @@ void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) { void ApiGetterEntryStub::Generate(MacroAssembler* masm) { - UNREACHABLE(); + Label empty_result; + Label prologue; + Label promote_scheduled_exception; + __ EnterApiExitFrame(ExitFrame::MODE_NORMAL, kStackSpace, 0); + ASSERT_EQ(kArgc, 4); +#ifdef _WIN64 + // All the parameters should be set up by a caller. +#else + // Set 1st parameter register with property name. + __ movq(rsi, rdx); + // Second parameter register rdi should be set with pointer to AccessorInfo + // by a caller. +#endif + // Call the api function! + __ movq(rax, + reinterpret_cast(fun()->address()), + RelocInfo::RUNTIME_ENTRY); + __ call(rax); + // Check if the function scheduled an exception. + ExternalReference scheduled_exception_address = + ExternalReference::scheduled_exception_address(); + __ movq(rsi, scheduled_exception_address); + __ Cmp(Operand(rsi, 0), Factory::the_hole_value()); + __ j(not_equal, &promote_scheduled_exception); +#ifdef _WIN64 + // rax keeps a pointer to v8::Handle, unpack it. + __ movq(rax, Operand(rax, 0)); +#endif + // Check if the result handle holds 0. + __ testq(rax, rax); + __ j(zero, &empty_result); + // It was non-zero. Dereference to get the result value. + __ movq(rax, Operand(rax, 0)); + __ bind(&prologue); + __ LeaveExitFrame(ExitFrame::MODE_NORMAL); + __ ret(0); + __ bind(&promote_scheduled_exception); + __ TailCallRuntime(Runtime::kPromoteScheduledException, 0, 1); + __ bind(&empty_result); + // It was zero; the result is undefined. + __ Move(rax, Factory::undefined_value()); + __ jmp(&prologue); } diff --git a/src/x64/macro-assembler-x64.cc b/src/x64/macro-assembler-x64.cc index a5634a7..88f61d4 100644 --- a/src/x64/macro-assembler-x64.cc +++ b/src/x64/macro-assembler-x64.cc @@ -336,12 +336,32 @@ void MacroAssembler::CallStub(CodeStub* stub) { } +Object* MacroAssembler::TryCallStub(CodeStub* stub) { + ASSERT(allow_stub_calls()); // Calls are not allowed in some stubs. + Object* result = stub->TryGetCode(); + if (!result->IsFailure()) { + call(Handle(Code::cast(result)), RelocInfo::CODE_TARGET); + } + return result; +} + + void MacroAssembler::TailCallStub(CodeStub* stub) { ASSERT(allow_stub_calls()); // calls are not allowed in some stubs Jump(stub->GetCode(), RelocInfo::CODE_TARGET); } +Object* MacroAssembler::TryTailCallStub(CodeStub* stub) { + ASSERT(allow_stub_calls()); // Calls are not allowed in some stubs. + Object* result = stub->TryGetCode(); + if (!result->IsFailure()) { + jmp(Handle(Code::cast(result)), RelocInfo::CODE_TARGET); + } + return result; +} + + void MacroAssembler::StubReturn(int argc) { ASSERT(argc >= 1 && generating_stub()); ret((argc - 1) * kPointerSize); @@ -361,6 +381,12 @@ void MacroAssembler::CallRuntime(Runtime::FunctionId id, int num_arguments) { } +Object* MacroAssembler::TryCallRuntime(Runtime::FunctionId id, + int num_arguments) { + return TryCallRuntime(Runtime::FunctionForId(id), num_arguments); +} + + void MacroAssembler::CallRuntime(Runtime::Function* f, int num_arguments) { // If the expected number of arguments of the runtime function is // constant, we check that the actual number of arguments match the @@ -381,6 +407,26 @@ void MacroAssembler::CallRuntime(Runtime::Function* f, int num_arguments) { } +Object* MacroAssembler::TryCallRuntime(Runtime::Function* f, + int num_arguments) { + if (f->nargs >= 0 && f->nargs != num_arguments) { + IllegalOperation(num_arguments); + // Since we did not call the stub, there was no allocation failure. + // Return some non-failure object. + return Heap::undefined_value(); + } + + // TODO(1236192): Most runtime routines don't need the number of + // arguments passed in because it is constant. At some point we + // should remove this need and make the runtime routine entry code + // smarter. + Set(rax, num_arguments); + movq(rbx, ExternalReference(f)); + CEntryStub ces(f->result_size); + return TryCallStub(&ces); +} + + void MacroAssembler::CallExternalReference(const ExternalReference& ext, int num_arguments) { Set(rax, num_arguments); @@ -417,6 +463,87 @@ void MacroAssembler::TailCallRuntime(Runtime::FunctionId fid, } +static int Offset(ExternalReference ref0, ExternalReference ref1) { + int64_t offset = (ref0.address() - ref1.address()); + // Check that fits into int. + ASSERT(static_cast(offset) == offset); + return static_cast(offset); +} + + +void MacroAssembler::PushHandleScope(Register scratch) { + ExternalReference extensions_address = + ExternalReference::handle_scope_extensions_address(); + const int kExtensionsOffset = 0; + const int kNextOffset = Offset( + ExternalReference::handle_scope_next_address(), + extensions_address); + const int kLimitOffset = Offset( + ExternalReference::handle_scope_limit_address(), + extensions_address); + + // Push the number of extensions, smi-tagged so the gc will ignore it. + movq(kScratchRegister, extensions_address); + movq(scratch, Operand(kScratchRegister, kExtensionsOffset)); + movq(Operand(kScratchRegister, kExtensionsOffset), Immediate(0)); + Integer32ToSmi(scratch, scratch); + push(scratch); + // Push next and limit pointers which will be wordsize aligned and + // hence automatically smi tagged. + push(Operand(kScratchRegister, kNextOffset)); + push(Operand(kScratchRegister, kLimitOffset)); +} + + +Object* MacroAssembler::PopHandleScopeHelper(Register saved, + Register scratch, + bool gc_allowed) { + ExternalReference extensions_address = + ExternalReference::handle_scope_extensions_address(); + const int kExtensionsOffset = 0; + const int kNextOffset = Offset( + ExternalReference::handle_scope_next_address(), + extensions_address); + const int kLimitOffset = Offset( + ExternalReference::handle_scope_limit_address(), + extensions_address); + + Object* result = NULL; + Label write_back; + movq(kScratchRegister, extensions_address); + cmpq(Operand(kScratchRegister, kExtensionsOffset), Immediate(0)); + j(equal, &write_back); + push(saved); + if (gc_allowed) { + CallRuntime(Runtime::kDeleteHandleScopeExtensions, 0); + } else { + result = TryCallRuntime(Runtime::kDeleteHandleScopeExtensions, 0); + if (result->IsFailure()) return result; + } + pop(saved); + movq(kScratchRegister, extensions_address); + + bind(&write_back); + pop(Operand(kScratchRegister, kLimitOffset)); + pop(Operand(kScratchRegister, kNextOffset)); + pop(scratch); + SmiToInteger32(scratch, scratch); + movq(Operand(kScratchRegister, kExtensionsOffset), scratch); + + return result; +} + + +void MacroAssembler::PopHandleScope(Register saved, Register scratch) { + PopHandleScopeHelper(saved, scratch, true); +} + + +Object* MacroAssembler::TryPopHandleScope(Register saved, Register scratch) { + return PopHandleScopeHelper(saved, scratch, false); +} + + void MacroAssembler::JumpToExternalReference(const ExternalReference& ext, int result_size) { // Set the entry point and jump to the C entry runtime stub. @@ -2208,7 +2335,8 @@ void MacroAssembler::LeaveFrame(StackFrame::Type type) { } -void MacroAssembler::EnterExitFrame(ExitFrame::Mode mode, int result_size) { +void MacroAssembler::EnterExitFramePrologue(ExitFrame::Mode mode, + bool save_rax) { // Setup the frame structure on the stack. // All constants are relative to the frame pointer of the exit frame. ASSERT(ExitFrameConstants::kCallerSPDisplacement == +2 * kPointerSize); @@ -2226,18 +2354,19 @@ void MacroAssembler::EnterExitFrame(ExitFrame::Mode mode, int result_size) { // 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(r14, rax); // Backup rax before we use it. + if (save_rax) { + movq(r14, rax); // Backup rax before we use it. + } movq(rax, rbp); store_rax(c_entry_fp_address); movq(rax, rsi); store_rax(context_address); +} - // Setup argv in callee-saved register r12. It is reused in LeaveExitFrame, - // so it must be retained across the C-call. - int offset = StandardFrameConstants::kCallerSPOffset - kPointerSize; - lea(r12, Operand(rbp, r14, times_pointer_size, offset)); - +void MacroAssembler::EnterExitFrameEpilogue(ExitFrame::Mode mode, + int result_size, + int argc) { #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. @@ -2258,7 +2387,7 @@ void MacroAssembler::EnterExitFrame(ExitFrame::Mode mode, int result_size) { // Reserve space for the Arguments object. The Windows 64-bit ABI // requires us to pass this structure as a pointer to its location on // the stack. The structure contains 2 values. - int argument_stack_space = 2 * kPointerSize; + int argument_stack_space = argc * kPointerSize; // We also need backing space for 4 parameters, even though // we only pass one or two parameter, and it is in a register. int argument_mirror_space = 4 * kPointerSize; @@ -2280,6 +2409,33 @@ void MacroAssembler::EnterExitFrame(ExitFrame::Mode mode, int result_size) { } +void MacroAssembler::EnterExitFrame(ExitFrame::Mode mode, int result_size) { + EnterExitFramePrologue(mode, true); + + // Setup argv in callee-saved register r12. It is reused in LeaveExitFrame, + // so it must be retained across the C-call. + int offset = StandardFrameConstants::kCallerSPOffset - kPointerSize; + lea(r12, Operand(rbp, r14, times_pointer_size, offset)); + + EnterExitFrameEpilogue(mode, result_size, 2); +} + + +void MacroAssembler::EnterApiExitFrame(ExitFrame::Mode mode, + int stack_space, + int argc, + int result_size) { + EnterExitFramePrologue(mode, false); + + // Setup argv in callee-saved register r12. It is reused in LeaveExitFrame, + // so it must be retained across the C-call. + int offset = StandardFrameConstants::kCallerSPOffset - kPointerSize; + lea(r12, Operand(rbp, (stack_space * kPointerSize) + offset)); + + EnterExitFrameEpilogue(mode, result_size, argc); +} + + void MacroAssembler::LeaveExitFrame(ExitFrame::Mode mode, int result_size) { // Registers: // r12 : argv diff --git a/src/x64/macro-assembler-x64.h b/src/x64/macro-assembler-x64.h index 64f35e1..7a90dd0 100644 --- a/src/x64/macro-assembler-x64.h +++ b/src/x64/macro-assembler-x64.h @@ -163,6 +163,11 @@ class MacroAssembler: public Assembler { // to the first argument in register rsi. void EnterExitFrame(ExitFrame::Mode mode, int result_size = 1); + void EnterApiExitFrame(ExitFrame::Mode mode, + int stack_space, + int argc, + int result_size = 1); + // Leave the current exit frame. Expects/provides the return value in // register rax:rdx (untouched) and the pointer to the first // argument in register rsi. @@ -719,18 +724,36 @@ class MacroAssembler: public Assembler { // Call a code stub. void CallStub(CodeStub* stub); + // Call a code stub and return the code object called. Try to generate + // the code if necessary. Do not perform a GC but instead return a retry + // after GC failure. + Object* TryCallStub(CodeStub* stub); + // Tail call a code stub (jump). void TailCallStub(CodeStub* stub); + // Tail call a code stub (jump) and return the code object called. Try to + // generate the code if necessary. Do not perform a GC but instead return + // a retry after GC failure. + Object* TryTailCallStub(CodeStub* stub); + // Return from a code stub after popping its arguments. void StubReturn(int argc); // Call a runtime routine. void CallRuntime(Runtime::Function* f, int num_arguments); + // Call a runtime function, returning the CodeStub object called. + // Try to generate the stub code if necessary. Do not perform a GC + // but instead return a retry after GC failure. + Object* TryCallRuntime(Runtime::Function* f, int num_arguments); + // Convenience function: Same as above, but takes the fid instead. void CallRuntime(Runtime::FunctionId id, int num_arguments); + // Convenience function: Same as above, but takes the fid instead. + Object* TryCallRuntime(Runtime::FunctionId id, int num_arguments); + // Convenience function: call an external reference. void CallExternalReference(const ExternalReference& ext, int num_arguments); @@ -747,6 +770,16 @@ class MacroAssembler: public Assembler { int num_arguments, int result_size); + void PushHandleScope(Register scratch); + + // Pops a handle scope using the specified scratch register and + // ensuring that saved register is left unchanged. + void PopHandleScope(Register saved, Register scratch); + + // As PopHandleScope, but does not perform a GC. Instead, returns a + // retry after GC failure object if GC is necessary. + Object* TryPopHandleScope(Register saved, Register scratch); + // Jump to a runtime routine. void JumpToExternalReference(const ExternalReference& ext, int result_size); @@ -835,6 +868,9 @@ class MacroAssembler: public Assembler { void EnterFrame(StackFrame::Type type); void LeaveFrame(StackFrame::Type type); + void EnterExitFramePrologue(ExitFrame::Mode mode, bool save_rax); + void EnterExitFrameEpilogue(ExitFrame::Mode mode, int result_size, int argc); + // Allocation support helpers. // Loads the top of new-space into the result register. // If flags contains RESULT_CONTAINS_TOP then result_end is valid and @@ -848,6 +884,13 @@ class MacroAssembler: public Assembler { // Update allocation top with value in result_end register. // If scratch is valid, it contains the address of the allocation top. void UpdateAllocationTopHelper(Register result_end, Register scratch); + + // Helper for PopHandleScope. Allowed to perform a GC and returns + // NULL if gc_allowed. Does not perform a GC if !gc_allowed, and + // possibly returns a failure object indicating an allocation failure. + Object* PopHandleScopeHelper(Register saved, + Register scratch, + bool gc_allowed); }; diff --git a/src/x64/stub-cache-x64.cc b/src/x64/stub-cache-x64.cc index 2a918f1..e67000a 100644 --- a/src/x64/stub-cache-x64.cc +++ b/src/x64/stub-cache-x64.cc @@ -2211,23 +2211,80 @@ bool StubCompiler::GenerateLoadCallback(JSObject* object, // Check that the maps haven't changed. Register reg = - CheckPrototypes(object, receiver, holder, - scratch1, scratch2, scratch3, name, miss); + CheckPrototypes(object, receiver, holder, scratch1, + scratch2, scratch3, name, miss); - // Push the arguments on the JS stack of the caller. - __ pop(scratch2); // remove return address + Handle callback_handle(callback); + + __ EnterInternalFrame(); + __ PushHandleScope(scratch2); + // Push the stack address where the list of arguments ends. + __ movq(scratch2, rsp); + __ subq(scratch2, Immediate(2 * kPointerSize)); + __ push(scratch2); __ push(receiver); // receiver __ push(reg); // holder - __ Move(reg, Handle(callback)); // callback data - __ push(reg); - __ push(FieldOperand(reg, AccessorInfo::kDataOffset)); + if (Heap::InNewSpace(callback_handle->data())) { + __ Move(scratch2, callback_handle); + __ push(FieldOperand(scratch2, AccessorInfo::kDataOffset)); // data + } else { + __ Push(Handle(callback_handle->data())); + } __ push(name_reg); // name - __ push(scratch2); // restore return address + // Save a pointer to where we pushed the arguments pointer. + // This will be passed as the const AccessorInfo& to the C++ callback. + +#ifdef _WIN64 + // Win64 uses first register--rcx--for returned value. + Register accessor_info_arg = r8; + Register name_arg = rdx; +#else + Register accessor_info_arg = rdx; // temporary, copied to rsi by the stub. + Register name_arg = rdi; +#endif - // Do tail-call to the runtime system. - ExternalReference load_callback_property = - ExternalReference(IC_Utility(IC::kLoadCallbackProperty)); - __ TailCallExternalReference(load_callback_property, 5, 1); + __ movq(accessor_info_arg, rsp); + __ addq(accessor_info_arg, Immediate(4 * kPointerSize)); + __ movq(name_arg, rsp); + + // Do call through the api. + ASSERT_EQ(5, ApiGetterEntryStub::kStackSpace); + Address getter_address = v8::ToCData
(callback->getter()); + ApiFunction fun(getter_address); + ApiGetterEntryStub stub(callback_handle, &fun); +#ifdef _WIN64 + // We need to prepare a slot for result handle on stack and put + // a pointer to it into 1st arg register. + __ push(Immediate(0)); + __ movq(rcx, rsp); +#endif + // Emitting a stub call may try to allocate (if the code is not + // already generated). Do not allow the assembler to perform a + // garbage collection but instead return the allocation failure + // object. + Object* result = masm()->TryCallStub(&stub); + if (result->IsFailure()) { + *failure = Failure::cast(result); + return false; + } +#ifdef _WIN64 + // Discard allocated slot. + __ addq(rsp, Immediate(kPointerSize)); +#endif + + // We need to avoid using rax since that now holds the result. + Register tmp = scratch2.is(rax) ? reg : scratch2; + // Emitting PopHandleScope may try to allocate. Do not allow the + // assembler to perform a garbage collection but instead return a + // failure object. + result = masm()->TryPopHandleScope(rax, tmp); + if (result->IsFailure()) { + *failure = Failure::cast(result); + return false; + } + __ LeaveInternalFrame(); + + __ ret(0); return true; } -- 2.7.4