Support for calls on named and keyed properties in the fast compiler of the form:
authorfschneider@chromium.org <fschneider@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 30 Oct 2009 10:22:31 +0000 (10:22 +0000)
committerfschneider@chromium.org <fschneider@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 30 Oct 2009 10:22:31 +0000 (10:22 +0000)
o.x() and o[expr]()

other changes:
- Fix missing relocation info for StoreIC on global object.
- Generate only one common return sequence instead of always appending
  "return <undefined>" at the end of each function: The first JS
  return-statement will generate the common return sequence. All
  other return-statements will generate a unconditional branch to the common
  return sequence.

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@3183 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

12 files changed:
src/arm/codegen-arm.cc
src/arm/codegen-arm.h
src/arm/fast-codegen-arm.cc
src/compiler.cc
src/fast-codegen.h
src/ia32/codegen-ia32.cc
src/ia32/codegen-ia32.h
src/ia32/fast-codegen-ia32.cc
src/x64/codegen-x64.cc
src/x64/codegen-x64.h
src/x64/fast-codegen-x64.cc
test/mjsunit/compiler/function-call.js [new file with mode: 0644]

index dd88515..79f4017 100644 (file)
@@ -1066,27 +1066,6 @@ void CodeGenerator::Comparison(Condition cc,
 }
 
 
-class CallFunctionStub: public CodeStub {
- public:
-  CallFunctionStub(int argc, InLoopFlag in_loop)
-      : argc_(argc), in_loop_(in_loop) {}
-
-  void Generate(MacroAssembler* masm);
-
- private:
-  int argc_;
-  InLoopFlag in_loop_;
-
-#if defined(DEBUG)
-  void Print() { PrintF("CallFunctionStub (argc %d)\n", argc_); }
-#endif  // defined(DEBUG)
-
-  Major MajorKey() { return CallFunction; }
-  int MinorKey() { return argc_; }
-  InLoopFlag InLoop() { return in_loop_; }
-};
-
-
 // Call the function on the stack with the given arguments.
 void CodeGenerator::CallWithArguments(ZoneList<Expression*>* args,
                                          int position) {
index e079950..46560b7 100644 (file)
@@ -433,6 +433,27 @@ class CodeGenerator: public AstVisitor {
 };
 
 
+class CallFunctionStub: public CodeStub {
+ public:
+  CallFunctionStub(int argc, InLoopFlag in_loop)
+      : argc_(argc), in_loop_(in_loop) {}
+
+  void Generate(MacroAssembler* masm);
+
+ private:
+  int argc_;
+  InLoopFlag in_loop_;
+
+#if defined(DEBUG)
+  void Print() { PrintF("CallFunctionStub (argc %d)\n", argc_); }
+#endif  // defined(DEBUG)
+
+  Major MajorKey() { return CallFunction; }
+  int MinorKey() { return argc_; }
+  InLoopFlag InLoop() { return in_loop_; }
+};
+
+
 class GenericBinaryOpStub : public CodeStub {
  public:
   GenericBinaryOpStub(Token::Value op,
index 747398d..3871d1b 100644 (file)
@@ -99,20 +99,26 @@ void FastCodeGenerator::Generate(FunctionLiteral* fun) {
     // Emit a 'return undefined' in case control fell off the end of the
     // body.
     __ LoadRoot(r0, Heap::kUndefinedValueRootIndex);
-    SetReturnPosition(fun);
-    if (FLAG_trace) {
-      // Push the return value on the stack as the parameter.
-      // Runtime::TraceExit returns its parameter in r0.
-      __ push(r0);
-      __ CallRuntime(Runtime::kTraceExit, 1);
+  }
+  { Comment cmnt(masm_, "Return sequence");
+    if (return_label_.is_bound()) {
+      __ b(&return_label_);
+    } else {
+      __ bind(&return_label_);
+      SetReturnPosition(fun);
+      if (FLAG_trace) {
+        // Push the return value on the stack as the parameter.
+        // Runtime::TraceExit returns its parameter in r0.
+        __ push(r0);
+        __ CallRuntime(Runtime::kTraceExit, 1);
+      }
+      __ RecordJSReturn();
+      __ mov(sp, fp);
+      __ ldm(ia_w, sp, fp.bit() | lr.bit());
+      int num_parameters = function_->scope()->num_parameters();
+      __ add(sp, sp, Operand((num_parameters + 1) * kPointerSize));
+      __ Jump(lr);
     }
-
-    __ RecordJSReturn();
-    __ mov(sp, fp);
-    __ ldm(ia_w, sp, fp.bit() | lr.bit());
-    int num_parameters = function_->scope()->num_parameters();
-    __ add(sp, sp, Operand((num_parameters + 1) * kPointerSize));
-    __ Jump(lr);
   }
 }
 
@@ -183,18 +189,21 @@ void FastCodeGenerator::VisitReturnStatement(ReturnStatement* stmt) {
     Visit(expr);
     __ pop(r0);
   }
-
-  if (FLAG_trace) {
-    __ push(r0);
-    __ CallRuntime(Runtime::kTraceExit, 1);
+  if (return_label_.is_bound()) {
+    __ b(&return_label_);
+  } else {
+    __ bind(&return_label_);
+    if (FLAG_trace) {
+      __ push(r0);
+      __ CallRuntime(Runtime::kTraceExit, 1);
+    }
+    __ RecordJSReturn();
+    __ mov(sp, fp);
+    __ ldm(ia_w, sp, fp.bit() | lr.bit());
+    int num_parameters = function_->scope()->num_parameters();
+    __ add(sp, sp, Operand((num_parameters + 1) * kPointerSize));
+    __ Jump(lr);
   }
-
-  __ RecordJSReturn();
-  __ mov(sp, fp);
-  __ ldm(ia_w, sp, fp.bit() | lr.bit());
-  int num_parameters = function_->scope()->num_parameters();
-  __ add(sp, sp, Operand((num_parameters + 1) * kPointerSize));
-  __ Jump(lr);
 }
 
 
@@ -565,36 +574,97 @@ void FastCodeGenerator::VisitProperty(Property* expr) {
   DropAndMove(expr->context(), r0);
 }
 
-
-void FastCodeGenerator::VisitCall(Call* expr) {
-  Comment cmnt(masm_, "[ Call");
-  Expression* fun = expr->expression();
+void FastCodeGenerator::EmitCallWithIC(Call* expr, RelocInfo::Mode reloc_info) {
+  // Code common for calls using the IC.
   ZoneList<Expression*>* args = expr->arguments();
-  Variable* var = fun->AsVariableProxy()->AsVariable();
-  ASSERT(var != NULL && !var->is_this() && var->is_global());
-  ASSERT(!var->is_possibly_eval());
-
-  __ mov(r1, Operand(var->name()));
-  // Push global object as receiver.
-  __ ldr(r0, CodeGenerator::GlobalObject());
-  __ stm(db_w, sp, r1.bit() | r0.bit());
   int arg_count = args->length();
   for (int i = 0; i < arg_count; i++) {
     Visit(args->at(i));
     ASSERT_EQ(Expression::kValue, args->at(i)->context());
   }
-  // Record source position for debugger
+  // Record source position for debugger.
   SetSourcePosition(expr->position());
   // Call the IC initialization code.
   Handle<Code> ic = CodeGenerator::ComputeCallInitialize(arg_count,
                                                          NOT_IN_LOOP);
-  __ Call(ic, RelocInfo::CODE_TARGET_CONTEXT);
+  __ Call(ic, reloc_info);
+  // Restore context register.
+  __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
+  // Discard the function left on TOS.
+  DropAndMove(expr->context(), r0);
+}
+
+
+void FastCodeGenerator::EmitCallWithStub(Call* expr) {
+  // Code common for calls using the call stub.
+  ZoneList<Expression*>* args = expr->arguments();
+  int arg_count = args->length();
+  for (int i = 0; i < arg_count; i++) {
+    Visit(args->at(i));
+  }
+  // Record source position for debugger.
+  SetSourcePosition(expr->position());
+  CallFunctionStub stub(arg_count, NOT_IN_LOOP);
+  __ CallStub(&stub);
   // Restore context register.
   __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
+  // Discard the function left on TOS.
   DropAndMove(expr->context(), r0);
 }
 
 
+void FastCodeGenerator::VisitCall(Call* expr) {
+  Expression* fun = expr->expression();
+
+  if (fun->AsProperty() != NULL) {
+    // Call on a property.
+    Property* prop = fun->AsProperty();
+    Literal* key = prop->key()->AsLiteral();
+    if (key != NULL && key->handle()->IsSymbol()) {
+      // Call on a named property: foo.x(1,2,3)
+      __ mov(r0, Operand(key->handle()));
+      __ push(r0);
+      Visit(prop->obj());
+      // Use call IC.
+      EmitCallWithIC(expr, RelocInfo::CODE_TARGET);
+    } else {
+      // Call on a keyed property : foo[key](1,2,3)
+      // Use a keyed load IC followed by a call IC.
+      Visit(prop->obj());
+      Visit(prop->key());
+      // Record source position of property.
+      SetSourcePosition(prop->position());
+      Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
+      __ Call(ic, RelocInfo::CODE_TARGET);
+      // Load receiver object into r1.
+      if (prop->is_synthetic()) {
+        __ ldr(r1, CodeGenerator::GlobalObject());
+      } else {
+        __ ldr(r1, MemOperand(sp, kPointerSize));
+      }
+      // Overwrite (object, key) with (function, receiver).
+      __ str(r0, MemOperand(sp, kPointerSize));
+      __ str(r1, MemOperand(sp));
+      EmitCallWithStub(expr);
+    }
+  } else if (fun->AsVariableProxy()->AsVariable() != NULL) {
+    // Call on a global variable
+    Variable* var = fun->AsVariableProxy()->AsVariable();
+    ASSERT(var != NULL && !var->is_this() && var->is_global());
+    ASSERT(!var->is_possibly_eval());
+    __ mov(r1, Operand(var->name()));
+    // Push global object as receiver.
+    __ ldr(r0, CodeGenerator::GlobalObject());
+    __ stm(db_w, sp, r1.bit() | r0.bit());
+    EmitCallWithIC(expr, RelocInfo::CODE_TARGET_CONTEXT);
+  } else {
+    // Calls we cannot handle right now.
+    // Should bailout in the CodeGenSelector.
+    UNREACHABLE();
+  }
+}
+
+
 void FastCodeGenerator::VisitCallNew(CallNew* expr) {
   Comment cmnt(masm_, "[ CallNew");
   // According to ECMA-262, section 11.2.2, page 44, the function
index 991ad78..d8168ec 100644 (file)
@@ -770,8 +770,23 @@ void CodeGenSelector::VisitCall(Call* expr) {
     // ----------------------------------
     // JavaScript example: 'foo(1, 2, 3)'  // foo is global
     // ----------------------------------
+  } else if (fun->AsProperty() != NULL) {
+    // ------------------------------------------------------------------
+    // JavaScript example: 'object.foo(1, 2, 3)' or 'map["key"](1, 2, 3)'
+    // ------------------------------------------------------------------
+    Property* prop = fun->AsProperty();
+    Literal* literal_key = prop->key()->AsLiteral();
+    if (literal_key != NULL && literal_key->handle()->IsSymbol()) {
+      ProcessExpression(prop->obj(), Expression::kValue);
+      CHECK_BAILOUT;
+    } else {
+      ProcessExpression(prop->obj(), Expression::kValue);
+      CHECK_BAILOUT;
+      ProcessExpression(prop->key(), Expression::kValue);
+      CHECK_BAILOUT;
+    }
   } else {
-    BAILOUT("Call to a non-global function");
+    BAILOUT("Unsupported call to a function");
   }
   // Check all arguments to the call.  (Relies on TEMP meaning STACK.)
   for (int i = 0; i < args->length(); i++) {
index f12783c..26f031e 100644 (file)
@@ -63,6 +63,9 @@ class FastCodeGenerator: public AstVisitor {
   Handle<JSFunction> BuildBoilerplate(FunctionLiteral* fun);
   void DeclareGlobals(Handle<FixedArray> pairs);
 
+  void EmitCallWithStub(Call* expr);
+  void EmitCallWithIC(Call* expr, RelocInfo::Mode reloc_info);
+
   void SetFunctionPosition(FunctionLiteral* fun);
   void SetReturnPosition(FunctionLiteral* fun);
   void SetStatementPosition(Statement* stmt);
@@ -80,6 +83,7 @@ class FastCodeGenerator: public AstVisitor {
   FunctionLiteral* function_;
   Handle<Script> script_;
   bool is_eval_;
+  Label return_label_;
 
   DISALLOW_COPY_AND_ASSIGN(FastCodeGenerator);
 };
index 4ac5527..64efa08 100644 (file)
@@ -1970,27 +1970,6 @@ void CodeGenerator::Comparison(Condition cc,
 }
 
 
-class CallFunctionStub: public CodeStub {
- public:
-  CallFunctionStub(int argc, InLoopFlag in_loop)
-      : argc_(argc), in_loop_(in_loop) { }
-
-  void Generate(MacroAssembler* masm);
-
- private:
-  int argc_;
-  InLoopFlag in_loop_;
-
-#ifdef DEBUG
-  void Print() { PrintF("CallFunctionStub (args %d)\n", argc_); }
-#endif
-
-  Major MajorKey() { return CallFunction; }
-  int MinorKey() { return argc_; }
-  InLoopFlag InLoop() { return in_loop_; }
-};
-
-
 // Call the function just below TOS on the stack with the given
 // arguments. The receiver is the TOS.
 void CodeGenerator::CallWithArguments(ZoneList<Expression*>* args,
index 3669e9d..8ede6a3 100644 (file)
@@ -626,6 +626,27 @@ class CodeGenerator: public AstVisitor {
 };
 
 
+class CallFunctionStub: public CodeStub {
+ public:
+  CallFunctionStub(int argc, InLoopFlag in_loop)
+      : argc_(argc), in_loop_(in_loop) { }
+
+  void Generate(MacroAssembler* masm);
+
+ private:
+  int argc_;
+  InLoopFlag in_loop_;
+
+#ifdef DEBUG
+  void Print() { PrintF("CallFunctionStub (args %d)\n", argc_); }
+#endif
+
+  Major MajorKey() { return CallFunction; }
+  int MinorKey() { return argc_; }
+  InLoopFlag InLoop() { return in_loop_; }
+};
+
+
 class ToBooleanStub: public CodeStub {
  public:
   ToBooleanStub() { }
index 4960f52..6a19ae3 100644 (file)
@@ -65,6 +65,10 @@ void FastCodeGenerator::Generate(FunctionLiteral* fun) {
     }
   }
 
+  { Comment cmnt(masm_, "[ Declarations");
+    VisitDeclarations(fun->scope()->declarations());
+  }
+
   { Comment cmnt(masm_, "[ Stack check");
     Label ok;
     ExternalReference stack_guard_limit =
@@ -76,10 +80,6 @@ void FastCodeGenerator::Generate(FunctionLiteral* fun) {
     __ bind(&ok);
   }
 
-  { Comment cmnt(masm_, "[ Declarations");
-    VisitDeclarations(fun->scope()->declarations());
-  }
-
   if (FLAG_trace) {
     __ CallRuntime(Runtime::kTraceEnter, 0);
   }
@@ -92,18 +92,27 @@ void FastCodeGenerator::Generate(FunctionLiteral* fun) {
     // Emit a 'return undefined' in case control fell off the end of the
     // body.
     __ mov(eax, Factory::undefined_value());
+  }
+  { Comment cmnt(masm_, "[ Return sequence");
     SetReturnPosition(fun);
 
-    if (FLAG_trace) {
-      __ push(eax);
-      __ CallRuntime(Runtime::kTraceExit, 1);
+    if (return_label_.is_bound()) {
+      __ jmp(&return_label_);
+    } else {
+      // Common return label
+      __ bind(&return_label_);
+
+      if (FLAG_trace) {
+        __ push(eax);
+        __ CallRuntime(Runtime::kTraceExit, 1);
+      }
+      __ RecordJSReturn();
+      // Do not use the leave instruction here because it is too short to
+      // patch with the code required by the debugger.
+      __ mov(esp, ebp);
+      __ pop(ebp);
+      __ ret((fun->scope()->num_parameters() + 1) * kPointerSize);
     }
-    __ RecordJSReturn();
-    // Do not use the leave instruction here because it is too short to
-    // patch with the code required by the debugger.
-    __ mov(esp, ebp);
-    __ pop(ebp);
-    __ ret((fun->scope()->num_parameters() + 1) * kPointerSize);
   }
 }
 
@@ -171,17 +180,24 @@ void FastCodeGenerator::VisitReturnStatement(ReturnStatement* stmt) {
     __ pop(eax);
   }
 
-  if (FLAG_trace) {
-    __ push(eax);
-    __ CallRuntime(Runtime::kTraceExit, 1);
-  }
-  __ RecordJSReturn();
+  if (return_label_.is_bound()) {
+    __ jmp(&return_label_);
+  } else {
+    __ bind(&return_label_);
 
-  // Do not use the leave instruction here because it is too short to
-  // patch with the code required by the debugger.
-  __ mov(esp, ebp);
-  __ pop(ebp);
-  __ ret((function_->scope()->num_parameters() + 1) * kPointerSize);
+      if (FLAG_trace) {
+        __ push(eax);
+        __ CallRuntime(Runtime::kTraceExit, 1);
+      }
+
+    __ RecordJSReturn();
+
+    // Do not use the leave instruction here because it is too short to
+    // patch with the code required by the debugger.
+    __ mov(esp, ebp);
+    __ pop(ebp);
+    __ ret((function_->scope()->num_parameters() + 1) * kPointerSize);
+  }
 }
 
 
@@ -484,6 +500,8 @@ void FastCodeGenerator::VisitAssignment(Assignment* expr) {
       Visit(rhs);
       __ pop(eax);
     }
+    // Record position for debugger.
+    SetSourcePosition(expr->position());
     __ mov(ecx, var->name());
     __ push(CodeGenerator::GlobalObject());
     Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
@@ -533,6 +551,7 @@ void FastCodeGenerator::VisitProperty(Property* expr) {
 
   // Evaluate receiver.
   Visit(expr->obj());
+
   if (key->AsLiteral() != NULL && key->AsLiteral()->handle()->IsSymbol() &&
       !String::cast(*(key->AsLiteral()->handle()))->AsArrayIndex(&dummy)) {
     // Do a NAMED property load.
@@ -558,27 +577,38 @@ void FastCodeGenerator::VisitProperty(Property* expr) {
 }
 
 
-void FastCodeGenerator::VisitCall(Call* expr) {
-  Expression* fun = expr->expression();
+void FastCodeGenerator::EmitCallWithIC(Call* expr, RelocInfo::Mode reloc_info) {
+  // Code common for calls using the IC.
   ZoneList<Expression*>* args = expr->arguments();
-  Variable* var = fun->AsVariableProxy()->AsVariable();
-  ASSERT(var != NULL && !var->is_this() && var->is_global());
-  ASSERT(!var->is_possibly_eval());
-
-  __ push(Immediate(var->name()));
-  // Push global object (receiver).
-  __ push(CodeGenerator::GlobalObject());
   int arg_count = args->length();
   for (int i = 0; i < arg_count; i++) {
     Visit(args->at(i));
     ASSERT_EQ(Expression::kValue, args->at(i)->context());
   }
-  // Record source position for debugger
+  // Record source position for debugger.
   SetSourcePosition(expr->position());
   // Call the IC initialization code.
   Handle<Code> ic = CodeGenerator::ComputeCallInitialize(arg_count,
                                                          NOT_IN_LOOP);
-  __ call(ic, RelocInfo::CODE_TARGET_CONTEXT);
+  __ call(ic, reloc_info);
+  // Restore context register.
+  __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
+  // Discard the function left on TOS.
+  DropAndMove(expr->context(), eax);
+}
+
+
+void FastCodeGenerator::EmitCallWithStub(Call* expr) {
+  // Code common for calls using the call stub.
+  ZoneList<Expression*>* args = expr->arguments();
+  int arg_count = args->length();
+  for (int i = 0; i < arg_count; i++) {
+    Visit(args->at(i));
+  }
+  // Record source position for debugger.
+  SetSourcePosition(expr->position());
+  CallFunctionStub stub(arg_count, NOT_IN_LOOP);
+  __ CallStub(&stub);
   // Restore context register.
   __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
   // Discard the function left on TOS.
@@ -586,6 +616,61 @@ void FastCodeGenerator::VisitCall(Call* expr) {
 }
 
 
+void FastCodeGenerator::VisitCall(Call* expr) {
+  Expression* fun = expr->expression();
+
+  if (fun->AsProperty() != NULL) {
+    // Call on a property.
+    Property* prop = fun->AsProperty();
+    Literal* key = prop->key()->AsLiteral();
+    if (key != NULL && key->handle()->IsSymbol()) {
+      // Call on a named property: foo.x(1,2,3)
+      __ push(Immediate(key->handle()));
+      Visit(prop->obj());
+      // Use call IC.
+      EmitCallWithIC(expr, RelocInfo::CODE_TARGET);
+    } else {
+      // Call on a keyed property: foo[key](1,2,3)
+      // Use a keyed load IC followed by a call IC.
+      Visit(prop->obj());
+      Visit(prop->key());
+      // Record source position of property.
+      SetSourcePosition(prop->position());
+      Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
+      __ call(ic, RelocInfo::CODE_TARGET);
+      // By emitting a nop we make sure that we do not have a "test eax,..."
+      // instruction after the call it is treated specially by the LoadIC code.
+      __ nop();
+      // Drop key left on the stack by IC.
+      __ add(Operand(esp), Immediate(kPointerSize));
+      // Pop receiver.
+      __ pop(ebx);
+      // Push result (function).
+      __ push(eax);
+      // Push receiver object on stack.
+      if (prop->is_synthetic()) {
+        __ push(CodeGenerator::GlobalObject());
+      } else {
+        __ push(ebx);
+      }
+      EmitCallWithStub(expr);
+    }
+  } else if (fun->AsVariableProxy()->AsVariable() != NULL) {
+    // Call on a global variable
+    Variable* var = fun->AsVariableProxy()->AsVariable();
+    ASSERT(var != NULL && !var->is_this() && var->is_global());
+    ASSERT(!var->is_possibly_eval());
+    __ push(Immediate(var->name()));
+    // Push global object (receiver).
+    __ push(CodeGenerator::GlobalObject());
+    EmitCallWithIC(expr, RelocInfo::CODE_TARGET_CONTEXT);
+  } else {
+    // Calls we cannot handle right now.
+    // Should bailout in the CodeGenSelector.
+    UNREACHABLE();
+  }
+}
+
 void FastCodeGenerator::VisitCallNew(CallNew* expr) {
   Comment cmnt(masm_, "[ CallNew");
   // According to ECMA-262, section 11.2.2, page 44, the function
index 95f30d8..397e964 100644 (file)
@@ -643,27 +643,6 @@ void DeferredReferenceSetKeyedValue::Generate() {
 }
 
 
-class CallFunctionStub: public CodeStub {
- public:
-  CallFunctionStub(int argc, InLoopFlag in_loop)
-      : argc_(argc), in_loop_(in_loop) { }
-
-  void Generate(MacroAssembler* masm);
-
- private:
-  int argc_;
-  InLoopFlag in_loop_;
-
-#ifdef DEBUG
-  void Print() { PrintF("CallFunctionStub (args %d)\n", argc_); }
-#endif
-
-  Major MajorKey() { return CallFunction; }
-  int MinorKey() { return argc_; }
-  InLoopFlag InLoop() { return in_loop_; }
-};
-
-
 void CodeGenerator::CallApplyLazy(Property* apply,
                                   Expression* receiver,
                                   VariableProxy* arguments,
index 0721d52..0a0f1ec 100644 (file)
@@ -633,6 +633,25 @@ class CodeGenerator: public AstVisitor {
 // times by generated code to perform common tasks, often the slow
 // case of a JavaScript operation.  They are all subclasses of CodeStub,
 // which is declared in code-stubs.h.
+class CallFunctionStub: public CodeStub {
+ public:
+  CallFunctionStub(int argc, InLoopFlag in_loop)
+      : argc_(argc), in_loop_(in_loop) { }
+
+  void Generate(MacroAssembler* masm);
+
+ private:
+  int argc_;
+  InLoopFlag in_loop_;
+
+#ifdef DEBUG
+  void Print() { PrintF("CallFunctionStub (args %d)\n", argc_); }
+#endif
+
+  Major MajorKey() { return CallFunction; }
+  int MinorKey() { return argc_; }
+  InLoopFlag InLoop() { return in_loop_; }
+};
 
 
 class ToBooleanStub: public CodeStub {
index b1f863f..4bbb42d 100644 (file)
@@ -91,27 +91,36 @@ void FastCodeGenerator::Generate(FunctionLiteral* fun) {
     // Emit a 'return undefined' in case control fell off the end of the
     // body.
     __ LoadRoot(rax, Heap::kUndefinedValueRootIndex);
+  }
+  { Comment cmnt(masm_, "Return sequence");
     SetReturnPosition(fun);
-    if (FLAG_trace) {
-      __ push(rax);
-      __ CallRuntime(Runtime::kTraceExit, 1);
-    }
-    __ RecordJSReturn();
 
-    // Do not use the leave instruction here because it is too short to
-    // patch with the code required by the debugger.
-    __ movq(rsp, rbp);
-    __ pop(rbp);
-    __ ret((fun->scope()->num_parameters() + 1) * kPointerSize);
+    if (return_label_.is_bound()) {
+      __ jmp(&return_label_);
+    } else {
+      __ bind(&return_label_);
+
+      if (FLAG_trace) {
+        __ push(rax);
+        __ CallRuntime(Runtime::kTraceExit, 1);
+      }
+      __ RecordJSReturn();
+
+      // Do not use the leave instruction here because it is too short to
+      // patch with the code required by the debugger.
+      __ movq(rsp, rbp);
+      __ pop(rbp);
+      __ ret((fun->scope()->num_parameters() + 1) * kPointerSize);
 #ifdef ENABLE_DEBUGGER_SUPPORT
-    // Add padding that will be overwritten by a debugger breakpoint.  We
-    // have just generated "movq rsp, rbp; pop rbp; ret k" with length 7
-    // (3 + 1 + 3).
-    const int kPadding = Debug::kX64JSReturnSequenceLength - 7;
-    for (int i = 0; i < kPadding; ++i) {
-      masm_->int3();
-    }
+      // Add padding that will be overwritten by a debugger breakpoint.  We
+      // have just generated "movq rsp, rbp; pop rbp; ret k" with length 7
+      // (3 + 1 + 3).
+      const int kPadding = Debug::kX64JSReturnSequenceLength - 7;
+      for (int i = 0; i < kPadding; ++i) {
+        masm_->int3();
+      }
 #endif
+    }
   }
 }
 
@@ -179,26 +188,32 @@ void FastCodeGenerator::VisitReturnStatement(ReturnStatement* stmt) {
     __ pop(rax);
   }
 
-  if (FLAG_trace) {
-    __ push(rax);
-    __ CallRuntime(Runtime::kTraceExit, 1);
-  }
+  if (return_label_.is_bound()) {
+    __ jmp(&return_label_);
+  } else {
+    __ bind(&return_label_);
+
+    if (FLAG_trace) {
+      __ push(rax);
+      __ CallRuntime(Runtime::kTraceExit, 1);
+    }
 
-  __ RecordJSReturn();
-  // Do not use the leave instruction here because it is too short to
-  // patch with the code required by the debugger.
-  __ movq(rsp, rbp);
-  __ pop(rbp);
-  __ ret((function_->scope()->num_parameters() + 1) * kPointerSize);
+    __ RecordJSReturn();
+    // Do not use the leave instruction here because it is too short to
+    // patch with the code required by the debugger.
+    __ movq(rsp, rbp);
+    __ pop(rbp);
+    __ ret((function_->scope()->num_parameters() + 1) * kPointerSize);
 #ifdef ENABLE_DEBUGGER_SUPPORT
-  // Add padding that will be overwritten by a debugger breakpoint.  We
-  // have just generated "movq rsp, rbp; pop rbp; ret k" with length 7
-  // (3 + 1 + 3).
-  const int kPadding = Debug::kX64JSReturnSequenceLength - 7;
-  for (int i = 0; i < kPadding; ++i) {
-    masm_->int3();
-  }
+    // Add padding that will be overwritten by a debugger breakpoint.  We
+    // have just generated "movq rsp, rbp; pop rbp; ret k" with length 7
+    // (3 + 1 + 3).
+    const int kPadding = Debug::kX64JSReturnSequenceLength - 7;
+    for (int i = 0; i < kPadding; ++i) {
+      masm_->int3();
+    }
 #endif
+  }
 }
 
 
@@ -495,6 +510,8 @@ void FastCodeGenerator::VisitAssignment(Assignment* expr) {
       Visit(rhs);
       __ pop(rax);
     }
+    // Record position for debugger.
+    SetSourcePosition(expr->position());
     __ Move(rcx, var->name());
     __ push(CodeGenerator::GlobalObject());
     Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
@@ -544,6 +561,7 @@ void FastCodeGenerator::VisitProperty(Property* expr) {
   // Evaluate receiver.
   Visit(expr->obj());
 
+
   if (key->AsLiteral() != NULL && key->AsLiteral()->handle()->IsSymbol() &&
       !String::cast(*(key->AsLiteral()->handle()))->AsArrayIndex(&dummy)) {
     // Do a NAMED property load.
@@ -569,27 +587,20 @@ void FastCodeGenerator::VisitProperty(Property* expr) {
 }
 
 
-void FastCodeGenerator::VisitCall(Call* expr) {
-  Expression* fun = expr->expression();
+void FastCodeGenerator::EmitCallWithIC(Call* expr, RelocInfo::Mode reloc_info) {
+  // Code common for calls using the IC.
   ZoneList<Expression*>* args = expr->arguments();
-  Variable* var = fun->AsVariableProxy()->AsVariable();
-  ASSERT(var != NULL && !var->is_this() && var->is_global());
-  ASSERT(!var->is_possibly_eval());
-
-  __ Push(var->name());
-  // Push global object (receiver).
-  __ push(CodeGenerator::GlobalObject());
   int arg_count = args->length();
   for (int i = 0; i < arg_count; i++) {
     Visit(args->at(i));
     ASSERT_EQ(Expression::kValue, args->at(i)->context());
   }
-  // Record source position for debugger
+  // Record source position for debugger.
   SetSourcePosition(expr->position());
   // Call the IC initialization code.
   Handle<Code> ic = CodeGenerator::ComputeCallInitialize(arg_count,
                                                          NOT_IN_LOOP);
-  __ call(ic, RelocInfo::CODE_TARGET_CONTEXT);
+  __ call(ic, reloc_info);
   // Restore context register.
   __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
   // Discard the function left on TOS.
@@ -597,6 +608,80 @@ void FastCodeGenerator::VisitCall(Call* expr) {
 }
 
 
+void FastCodeGenerator::EmitCallWithStub(Call* expr) {
+  // Code common for calls using the call stub.
+  ZoneList<Expression*>* args = expr->arguments();
+  int arg_count = args->length();
+  for (int i = 0; i < arg_count; i++) {
+    Visit(args->at(i));
+  }
+  // Record source position for debugger.
+  SetSourcePosition(expr->position());
+  CallFunctionStub stub(arg_count, NOT_IN_LOOP);
+  __ CallStub(&stub);
+  // Restore context register.
+  __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
+  // Discard the function left on TOS.
+  DropAndMove(expr->context(), rax);
+}
+
+
+void FastCodeGenerator::VisitCall(Call* expr) {
+  Expression* fun = expr->expression();
+
+  if (fun->AsProperty() != NULL) {
+    // Call on a property.
+    Property* prop = fun->AsProperty();
+    Literal* key = prop->key()->AsLiteral();
+    if (key != NULL && key->handle()->IsSymbol()) {
+      // Call on a named property: foo.x(1,2,3)
+      __ Push(key->handle());
+      Visit(prop->obj());
+      // Use call IC
+      EmitCallWithIC(expr, RelocInfo::CODE_TARGET);
+    } else {
+      // Call on a keyed property: foo[key](1,2,3)
+      // Use a keyed load IC followed by a call IC.
+      Visit(prop->obj());
+      Visit(prop->key());
+      // Record source position of property.
+      SetSourcePosition(prop->position());
+      Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
+      __ call(ic, RelocInfo::CODE_TARGET);
+      // By emitting a nop we make sure that we do not have a "test eax,..."
+      // instruction after the call it is treated specially by the LoadIC code.
+      __ nop();
+      // Drop key left on the stack by IC.
+      __ addq(rsp, Immediate(kPointerSize));
+      // Pop receiver.
+      __ pop(rbx);
+      // Push result (function).
+      __ push(rax);
+      // Push receiver object on stack.
+      if (prop->is_synthetic()) {
+        __ push(CodeGenerator::GlobalObject());
+      } else {
+        __ push(rbx);
+      }
+      EmitCallWithStub(expr);
+    }
+  } else if (fun->AsVariableProxy()->AsVariable() != NULL) {
+    // Call on a global variable
+    Variable* var = fun->AsVariableProxy()->AsVariable();
+    ASSERT(var != NULL && !var->is_this() && var->is_global());
+    ASSERT(!var->is_possibly_eval());
+    __ Push(var->name());
+    // Push global object (receiver).
+    __ push(CodeGenerator::GlobalObject());
+    EmitCallWithIC(expr, RelocInfo::CODE_TARGET_CONTEXT);
+  } else {
+    // Calls we cannot handle right now.
+    // Should bailout in the CodeGenSelector.
+    UNREACHABLE();
+  }
+}
+
+
 void FastCodeGenerator::VisitCallNew(CallNew* expr) {
   Comment cmnt(masm_, "[ CallNew");
   // According to ECMA-262, section 11.2.2, page 44, the function
diff --git a/test/mjsunit/compiler/function-call.js b/test/mjsunit/compiler/function-call.js
new file mode 100644 (file)
index 0000000..2bca509
--- /dev/null
@@ -0,0 +1,48 @@
+// Copyright 2009 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Test of function calls.
+
+function f(x) { return x; }
+
+var a;
+
+// Call on global object.
+a = f(8);
+assertEquals(8, a);
+
+// Call on a named property.
+var b;
+b = {x:f};
+a = b.x(9);
+assertEquals(9, a);
+
+// Call on a keyed property.
+c = "x";
+a = b[c](10);
+assertEquals(10, a);
+