X64 implementation: Add function literals and function calls.
authorwhesse@chromium.org <whesse@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 18 Jun 2009 11:46:38 +0000 (11:46 +0000)
committerwhesse@chromium.org <whesse@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 18 Jun 2009 11:46:38 +0000 (11:46 +0000)
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
src/ia32/codegen-ia32.cc
src/x64/codegen-x64.cc
src/x64/macro-assembler-x64.cc
src/x64/macro-assembler-x64.h
src/x64/virtual-frame-x64.cc

index 9c9806c..5968610 100644 (file)
@@ -1417,7 +1417,7 @@ void Assembler::call(const Operand& adr) {
 }
 
 
-void Assembler::call(Handle<Code> code,  RelocInfo::Mode rmode) {
+void Assembler::call(Handle<Code> code, RelocInfo::Mode rmode) {
   WriteRecordedPositions();
   EnsureSpace ensure_space(this);
   last_pc_ = pc_;
index e884430..eed0b14 100644 (file)
@@ -7163,7 +7163,6 @@ void CallFunctionStub::Generate(MacroAssembler* masm) {
 }
 
 
-
 void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) {
   // eax holds the exception.
 
index 7156f0f..0b0db1c 100644 (file)
@@ -103,7 +103,15 @@ void CodeGenerator::DeclareGlobals(Handle<FixedArray> a) {
 void CodeGenerator::TestCodeGenerator() {
   // Compile a function from a string, and run it.
   Handle<JSFunction> 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<JSFunction> 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<JSFunction> 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<Expression*>* 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<Code> 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<Expression*>* 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
index a87fd4f..e74db44 100644 (file)
@@ -206,6 +206,44 @@ void MacroAssembler::JumpToBuiltin(const ExternalReference& ext) {
 }
 
 
+void MacroAssembler::GetBuiltinEntry(Register target, Builtins::JavaScript id) {
+  bool resolved;
+  Handle<Code> 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<Code> 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> 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> 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.
index 51e998c..8738219 100644 (file)
@@ -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> code_object, RelocInfo::Mode rmode);
+
   void Call(Address destination, RelocInfo::Mode rmode);
   void Call(ExternalReference ext);
+  void Call(Handle<Code> code_object, RelocInfo::Mode rmode);
 
   // Compare object type for heap object.
   // Incoming register is heap_object and outgoing register is map.
index 52a762b..609540b 100644 (file)
@@ -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> 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<Code> 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 __