From da49c0f8a22b8403cfaa8552ae08afaa308d2d44 Mon Sep 17 00:00:00 2001 From: "whesse@chromium.org" Date: Thu, 18 Jun 2009 11:46:38 +0000 Subject: [PATCH] X64 implementation: Add function literals and function calls. Review URL: http://codereview.chromium.org/131029 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@2217 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/ia32/assembler-ia32.cc | 2 +- src/ia32/codegen-ia32.cc | 1 - src/x64/codegen-x64.cc | 256 +++++++++++++++++++++++++++++++-- src/x64/macro-assembler-x64.cc | 54 +++++++ src/x64/macro-assembler-x64.h | 3 + src/x64/virtual-frame-x64.cc | 131 ++++++++++++++++- 6 files changed, 431 insertions(+), 16 deletions(-) diff --git a/src/ia32/assembler-ia32.cc b/src/ia32/assembler-ia32.cc index 9c9806cbf..596861009 100644 --- a/src/ia32/assembler-ia32.cc +++ b/src/ia32/assembler-ia32.cc @@ -1417,7 +1417,7 @@ void Assembler::call(const Operand& adr) { } -void Assembler::call(Handle code, RelocInfo::Mode rmode) { +void Assembler::call(Handle code, RelocInfo::Mode rmode) { WriteRecordedPositions(); EnsureSpace ensure_space(this); last_pc_ = pc_; diff --git a/src/ia32/codegen-ia32.cc b/src/ia32/codegen-ia32.cc index e88443005..eed0b14c0 100644 --- a/src/ia32/codegen-ia32.cc +++ b/src/ia32/codegen-ia32.cc @@ -7163,7 +7163,6 @@ void CallFunctionStub::Generate(MacroAssembler* masm) { } - void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) { // eax holds the exception. diff --git a/src/x64/codegen-x64.cc b/src/x64/codegen-x64.cc index 7156f0f49..0b0db1c08 100644 --- a/src/x64/codegen-x64.cc +++ b/src/x64/codegen-x64.cc @@ -103,7 +103,15 @@ void CodeGenerator::DeclareGlobals(Handle a) { void CodeGenerator::TestCodeGenerator() { // Compile a function from a string, and run it. Handle test_function = Compiler::Compile( - Factory::NewStringFromAscii(CStrVector("39; 42;")), + Factory::NewStringFromAscii(CStrVector( + "39;" + "(function(){return 43})();" + "42;" + // "function foo(x, y){return x;};" + "43;" + // "foo(2,3);" + "44;" + "(function(){return (function(){return 47})()})();")), Factory::NewStringFromAscii(CStrVector("CodeGeneratorTestScript")), 0, 0, @@ -132,7 +140,7 @@ void CodeGenerator::TestCodeGenerator() { &pending_exceptions); // Function compiles and runs, but returns a JSFunction object. CHECK(result->IsSmi()); - CHECK_EQ(42, Smi::cast(*result)->value()); + CHECK_EQ(47, Smi::cast(*result)->value()); } @@ -370,15 +378,44 @@ void CodeGenerator::VisitDebuggerStatement(DebuggerStatement* a) { UNIMPLEMENTED(); } -void CodeGenerator::VisitFunctionLiteral(FunctionLiteral* a) { - UNIMPLEMENTED(); + +void CodeGenerator::InstantiateBoilerplate(Handle boilerplate) { + // Call the runtime to instantiate the function boilerplate object. + // The inevitable call will sync frame elements to memory anyway, so + // we do it eagerly to allow us to push the arguments directly into + // place. + ASSERT(boilerplate->IsBoilerplate()); + frame_->SyncRange(0, frame_->element_count() - 1); + + // Push the boilerplate on the stack. + __ movq(kScratchRegister, boilerplate, RelocInfo::EMBEDDED_OBJECT); + frame_->EmitPush(kScratchRegister); + + // Create a new closure. + frame_->EmitPush(rsi); + Result result = frame_->CallRuntime(Runtime::kNewClosure, 2); + frame_->Push(&result); +} + + +void CodeGenerator::VisitFunctionLiteral(FunctionLiteral* node) { + Comment cmnt(masm_, "[ FunctionLiteral"); + + // Build the function boilerplate and instantiate it. + Handle boilerplate = BuildBoilerplate(node); + // Check for stack-overflow exception. + if (HasStackOverflow()) return; + InstantiateBoilerplate(boilerplate); } + void CodeGenerator::VisitFunctionBoilerplateLiteral( - FunctionBoilerplateLiteral* a) { - UNIMPLEMENTED(); + FunctionBoilerplateLiteral* node) { + Comment cmnt(masm_, "[ FunctionBoilerplateLiteral"); + InstantiateBoilerplate(node->boilerplate()); } + void CodeGenerator::VisitConditional(Conditional* a) { UNIMPLEMENTED(); } @@ -521,10 +558,153 @@ void CodeGenerator::VisitProperty(Property* a) { UNIMPLEMENTED(); } -void CodeGenerator::VisitCall(Call* a) { - UNIMPLEMENTED(); + +void CodeGenerator::VisitCall(Call* node) { + Comment cmnt(masm_, "[ Call"); + + ZoneList* args = node->arguments(); + + CodeForStatementPosition(node); + + // Check if the function is a variable or a property. + Expression* function = node->expression(); + Variable* var = function->AsVariableProxy()->AsVariable(); + Property* property = function->AsProperty(); + + // ------------------------------------------------------------------------ + // Fast-case: Use inline caching. + // --- + // According to ECMA-262, section 11.2.3, page 44, the function to call + // must be resolved after the arguments have been evaluated. The IC code + // automatically handles this by loading the arguments before the function + // is resolved in cache misses (this also holds for megamorphic calls). + // ------------------------------------------------------------------------ + + if (var != NULL && !var->is_this() && var->is_global()) { + // ---------------------------------- + // JavaScript example: 'foo(1, 2, 3)' // foo is global + // ---------------------------------- + + // Push the name of the function and the receiver onto the stack. + frame_->Push(var->name()); + + // Pass the global object as the receiver and let the IC stub + // patch the stack to use the global proxy as 'this' in the + // invoked function. + LoadGlobal(); + + // Load the arguments. + int arg_count = args->length(); + for (int i = 0; i < arg_count; i++) { + Load(args->at(i)); + } + + // Call the IC initialization code. + CodeForSourcePosition(node->position()); + Result result = frame_->CallCallIC(RelocInfo::CODE_TARGET_CONTEXT, + arg_count, + loop_nesting()); + frame_->RestoreContextRegister(); + // Replace the function on the stack with the result. + frame_->SetElementAt(0, &result); + } else if (var != NULL && var->slot() != NULL && + var->slot()->type() == Slot::LOOKUP) { + // TODO(X64): Enable calls of non-global functions. + UNIMPLEMENTED(); + /* + // ---------------------------------- + // JavaScript example: 'with (obj) foo(1, 2, 3)' // foo is in obj + // ---------------------------------- + + // Load the function from the context. Sync the frame so we can + // push the arguments directly into place. + frame_->SyncRange(0, frame_->element_count() - 1); + frame_->EmitPush(esi); + frame_->EmitPush(Immediate(var->name())); + frame_->CallRuntime(Runtime::kLoadContextSlot, 2); + // The runtime call returns a pair of values in eax and edx. The + // looked-up function is in eax and the receiver is in edx. These + // register references are not ref counted here. We spill them + // eagerly since they are arguments to an inevitable call (and are + // not sharable by the arguments). + ASSERT(!allocator()->is_used(eax)); + frame_->EmitPush(eax); + + // Load the receiver. + ASSERT(!allocator()->is_used(edx)); + frame_->EmitPush(edx); + + // Call the function. + CallWithArguments(args, node->position()); + */ + } else if (property != NULL) { + UNIMPLEMENTED(); + /* + // Check if the key is a literal string. + Literal* literal = property->key()->AsLiteral(); + + if (literal != NULL && literal->handle()->IsSymbol()) { + // ------------------------------------------------------------------ + // JavaScript example: 'object.foo(1, 2, 3)' or 'map["key"](1, 2, 3)' + // ------------------------------------------------------------------ + + // Push the name of the function and the receiver onto the stack. + frame_->Push(literal->handle()); + Load(property->obj()); + + // Load the arguments. + int arg_count = args->length(); + for (int i = 0; i < arg_count; i++) { + Load(args->at(i)); + } + + // Call the IC initialization code. + CodeForSourcePosition(node->position()); + Result result = + frame_->CallCallIC(RelocInfo::CODE_TARGET, arg_count, loop_nesting()); + frame_->RestoreContextRegister(); + // Replace the function on the stack with the result. + frame_->SetElementAt(0, &result); + + } else { + // ------------------------------------------- + // JavaScript example: 'array[index](1, 2, 3)' + // ------------------------------------------- + + // Load the function to call from the property through a reference. + Reference ref(this, property); + ref.GetValue(NOT_INSIDE_TYPEOF); + + // Pass receiver to called function. + if (property->is_synthetic()) { + // Use global object as receiver. + LoadGlobalReceiver(); + } else { + // The reference's size is non-negative. + frame_->PushElementAt(ref.size()); + } + + // Call the function. + CallWithArguments(args, node->position()); + } + */ + } else { + // ---------------------------------- + // JavaScript example: 'foo(1, 2, 3)' // foo is not global + // ---------------------------------- + + // Load the function. + Load(function); + + // Pass the global proxy as the receiver. + LoadGlobalReceiver(); + + // Call the function. + CallWithArguments(args, node->position()); + } } + void CodeGenerator::VisitCallEval(CallEval* a) { UNIMPLEMENTED(); } @@ -537,6 +717,7 @@ void CodeGenerator::VisitCallRuntime(CallRuntime* a) { UNIMPLEMENTED(); } + void CodeGenerator::VisitUnaryOperation(UnaryOperation* a) { UNIMPLEMENTED(); } @@ -1216,6 +1397,16 @@ void CodeGenerator::LoadGlobal() { } } + +void CodeGenerator::LoadGlobalReceiver() { + Result temp = allocator_->Allocate(); + Register reg = temp.reg(); + __ movq(reg, GlobalObject()); + __ movq(reg, FieldOperand(reg, GlobalObject::kGlobalReceiverOffset)); + frame_->Push(&temp); +} + + #undef __ // End of CodeGenerator implementation. @@ -1588,6 +1779,54 @@ class CallFunctionStub: public CodeStub { void CallFunctionStub::Generate(MacroAssembler* masm) { + Label slow; + + // Get the function to call from the stack. + // +2 ~ receiver, return address + __ movq(rdi, Operand(rsp, (argc_ + 2) * kPointerSize)); + + // Check that the function really is a JavaScript function. + __ testq(rdi, Immediate(kSmiTagMask)); + __ j(zero, &slow); + // Goto slow case if we do not have a function. + __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx); + __ j(not_equal, &slow); + + // Fast-case: Just invoke the function. + ParameterCount actual(argc_); + __ InvokeFunction(rdi, actual, JUMP_FUNCTION); + + // Slow-case: Non-function called. + __ bind(&slow); + __ Set(rax, argc_); + __ Set(rbx, 0); + __ GetBuiltinEntry(rdx, Builtins::CALL_NON_FUNCTION); + Handle adaptor(Builtins::builtin(Builtins::ArgumentsAdaptorTrampoline)); + __ Jump(adaptor, RelocInfo::CODE_TARGET); +} + + +// Call the function just below TOS on the stack with the given +// arguments. The receiver is the TOS. +void CodeGenerator::CallWithArguments(ZoneList* args, + int position) { + // Push the arguments ("left-to-right") on the stack. + int arg_count = args->length(); + for (int i = 0; i < arg_count; i++) { + Load(args->at(i)); + } + + // Record the position for debugging purposes. + CodeForSourcePosition(position); + + // Use the shared code stub to call the function. + InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP; + CallFunctionStub call_function(arg_count, in_loop); + Result answer = frame_->CallStub(&call_function, arg_count + 1); + // Restore context and replace function on the stack with the + // result of the stub invocation. + frame_->RestoreContextRegister(); + frame_->SetElementAt(0, &answer); } @@ -1595,7 +1834,6 @@ void InstanceofStub::Generate(MacroAssembler* masm) { } - void ArgumentsAccessStub::GenerateNewObject(MacroAssembler* masm) { // The displacement is used for skipping the return address and the // frame pointer on the stack. It is the offset of the last diff --git a/src/x64/macro-assembler-x64.cc b/src/x64/macro-assembler-x64.cc index a87fd4fc1..e74db44a5 100644 --- a/src/x64/macro-assembler-x64.cc +++ b/src/x64/macro-assembler-x64.cc @@ -206,6 +206,44 @@ void MacroAssembler::JumpToBuiltin(const ExternalReference& ext) { } +void MacroAssembler::GetBuiltinEntry(Register target, Builtins::JavaScript id) { + bool resolved; + Handle code = ResolveBuiltin(id, &resolved); + + const char* name = Builtins::GetName(id); + int argc = Builtins::GetArgumentsCount(id); + + movq(target, code, RelocInfo::EXTERNAL_REFERENCE); // Is external reference? + if (!resolved) { + uint32_t flags = + Bootstrapper::FixupFlagsArgumentsCount::encode(argc) | + Bootstrapper::FixupFlagsIsPCRelative::encode(false) | + Bootstrapper::FixupFlagsUseCodeObject::encode(true); + Unresolved entry = { pc_offset() - sizeof(intptr_t), flags, name }; + unresolved_.Add(entry); + } + addq(target, Immediate(Code::kHeaderSize - kHeapObjectTag)); +} + + +Handle MacroAssembler::ResolveBuiltin(Builtins::JavaScript id, + bool* resolved) { + // Move the builtin function into the temporary function slot by + // reading it from the builtins object. NOTE: We should be able to + // reduce this to two instructions by putting the function table in + // the global object instead of the "builtins" object and by using a + // real register for the function. + movq(rdx, Operand(rsi, Context::SlotOffset(Context::GLOBAL_INDEX))); + movq(rdx, FieldOperand(rdx, GlobalObject::kBuiltinsOffset)); + int builtins_offset = + JSBuiltinsObject::kJSBuiltinsOffset + (id * kPointerSize); + movq(rdi, FieldOperand(rdx, builtins_offset)); + + + return Builtins::GetCode(id, resolved); +} + + void MacroAssembler::Set(Register dst, int64_t x) { if (is_int32(x)) { movq(dst, Immediate(x)); @@ -241,6 +279,14 @@ void MacroAssembler::Jump(Address destination, RelocInfo::Mode rmode) { } +void MacroAssembler::Jump(Handle code_object, RelocInfo::Mode rmode) { + WriteRecordedPositions(); + ASSERT(RelocInfo::IsCodeTarget(rmode)); + movq(kScratchRegister, code_object, rmode); + jmp(kScratchRegister); +} + + void MacroAssembler::Call(ExternalReference ext) { movq(kScratchRegister, ext); call(kScratchRegister); @@ -253,6 +299,14 @@ void MacroAssembler::Call(Address destination, RelocInfo::Mode rmode) { } +void MacroAssembler::Call(Handle code_object, RelocInfo::Mode rmode) { + WriteRecordedPositions(); + ASSERT(RelocInfo::IsCodeTarget(rmode)); + movq(kScratchRegister, code_object, rmode); + call(kScratchRegister); +} + + void MacroAssembler::PushTryHandler(CodeLocation try_location, HandlerType type) { // Adjust this code if not the case. diff --git a/src/x64/macro-assembler-x64.h b/src/x64/macro-assembler-x64.h index 51e998c31..873821996 100644 --- a/src/x64/macro-assembler-x64.h +++ b/src/x64/macro-assembler-x64.h @@ -161,8 +161,11 @@ class MacroAssembler: public Assembler { // Control Flow void Jump(Address destination, RelocInfo::Mode rmode); void Jump(ExternalReference ext); + void Jump(Handle code_object, RelocInfo::Mode rmode); + void Call(Address destination, RelocInfo::Mode rmode); void Call(ExternalReference ext); + void Call(Handle code_object, RelocInfo::Mode rmode); // Compare object type for heap object. // Incoming register is heap_object and outgoing register is map. diff --git a/src/x64/virtual-frame-x64.cc b/src/x64/virtual-frame-x64.cc index 52a762b6f..609540b72 100644 --- a/src/x64/virtual-frame-x64.cc +++ b/src/x64/virtual-frame-x64.cc @@ -137,6 +137,26 @@ void VirtualFrame::AllocateStackSlots() { } +void VirtualFrame::SaveContextRegister() { + ASSERT(elements_[context_index()].is_memory()); + __ movq(Operand(rbp, fp_relative(context_index())), rsi); +} + + +void VirtualFrame::RestoreContextRegister() { + ASSERT(elements_[context_index()].is_memory()); + __ movq(rsi, Operand(rbp, fp_relative(context_index()))); +} + + +void VirtualFrame::PushReceiverSlotAddress() { + Result temp = cgen()->allocator()->Allocate(); + ASSERT(temp.is_valid()); + __ lea(temp.reg(), ParameterAt(-1)); + Push(&temp); +} + + void VirtualFrame::EmitPop(Register reg) { ASSERT(stack_pointer_ == element_count() - 1); stack_pointer_--; @@ -433,13 +453,65 @@ Result VirtualFrame::Pop() { } -Result VirtualFrame::RawCallStub(CodeStub* a) { - UNIMPLEMENTED(); - return Result(NULL); +Result VirtualFrame::RawCallStub(CodeStub* stub) { + ASSERT(cgen()->HasValidEntryRegisters()); + __ CallStub(stub); + Result result = cgen()->allocator()->Allocate(rax); + ASSERT(result.is_valid()); + return result; } -void VirtualFrame::SyncElementBelowStackPointer(int a) { - UNIMPLEMENTED(); + +void VirtualFrame::SyncElementBelowStackPointer(int index) { + // Emit code to write elements below the stack pointer to their + // (already allocated) stack address. + ASSERT(index <= stack_pointer_); + FrameElement element = elements_[index]; + ASSERT(!element.is_synced()); + switch (element.type()) { + case FrameElement::INVALID: + break; + + case FrameElement::MEMORY: + // This function should not be called with synced elements. + // (memory elements are always synced). + UNREACHABLE(); + break; + + case FrameElement::REGISTER: + __ movq(Operand(rbp, fp_relative(index)), element.reg()); + break; + + case FrameElement::CONSTANT: + if (element.handle()->IsSmi()) { + if (CodeGeneratorScope::Current()->IsUnsafeSmi(element.handle())) { + CodeGeneratorScope::Current()->LoadUnsafeSmi(kScratchRegister, + element.handle()); + } else { + __ movq(kScratchRegister, element.handle(), RelocInfo::NONE); + } + } else { + __ movq(kScratchRegister, + element.handle(), + RelocInfo::EMBEDDED_OBJECT); + } + __ movq(Operand(rbp, fp_relative(index)), kScratchRegister); + break; + + case FrameElement::COPY: { + int backing_index = element.index(); + FrameElement backing_element = elements_[backing_index]; + if (backing_element.is_memory()) { + __ movq(kScratchRegister, Operand(rbp, fp_relative(backing_index))); + __ movq(Operand(rbp, fp_relative(index)), kScratchRegister); + } else { + ASSERT(backing_element.is_register()); + __ movq(Operand(rbp, fp_relative(index)), backing_element.reg()); + } + break; + } + } + elements_[index].set_sync(); } @@ -519,6 +591,55 @@ void VirtualFrame::SyncRange(int begin, int end) { } } +//------------------------------------------------------------------------------ +// Virtual frame stub and IC calling functions. + +Result VirtualFrame::RawCallCodeObject(Handle code, + RelocInfo::Mode rmode) { + ASSERT(cgen()->HasValidEntryRegisters()); + __ Call(code, rmode); + Result result = cgen()->allocator()->Allocate(rax); + ASSERT(result.is_valid()); + return result; +} + + +Result VirtualFrame::CallRuntime(Runtime::Function* f, int arg_count) { + PrepareForCall(arg_count, arg_count); + ASSERT(cgen()->HasValidEntryRegisters()); + __ CallRuntime(f, arg_count); + Result result = cgen()->allocator()->Allocate(rax); + ASSERT(result.is_valid()); + return result; +} + + +Result VirtualFrame::CallRuntime(Runtime::FunctionId id, int arg_count) { + PrepareForCall(arg_count, arg_count); + ASSERT(cgen()->HasValidEntryRegisters()); + __ CallRuntime(id, arg_count); + Result result = cgen()->allocator()->Allocate(rax); + ASSERT(result.is_valid()); + return result; +} + + +Result VirtualFrame::CallCallIC(RelocInfo::Mode mode, + int arg_count, + int loop_nesting) { + // Arguments, receiver, and function name are on top of the frame. + // The IC expects them on the stack. It does not drop the function + // name slot (but it does drop the rest). + InLoopFlag in_loop = loop_nesting > 0 ? IN_LOOP : NOT_IN_LOOP; + Handle ic = cgen()->ComputeCallInitialize(arg_count, in_loop); + // Spill args, receiver, and function. The call will drop args and + // receiver. + PrepareForCall(arg_count + 2, arg_count + 1); + return RawCallCodeObject(ic, mode); +} + + + #undef __ -- 2.34.1