[turbofan] Make arguments object materialization inlinable.
authormstarzinger <mstarzinger@chromium.org>
Wed, 16 Sep 2015 13:04:25 +0000 (06:04 -0700)
committerCommit bot <commit-bot@chromium.org>
Wed, 16 Sep 2015 13:04:34 +0000 (13:04 +0000)
This makes sure that the arguments object materialization in the method
prologue is composable with respect to inlining. The generic runtime
functions materializing those objects now respect the deoptimization
information when reconstructing the original arguments.

R=mvstanton@chromium.org

Review URL: https://codereview.chromium.org/1340313003

Cr-Commit-Position: refs/heads/master@{#30766}

src/compiler/ast-graph-builder.cc
src/compiler/js-generic-lowering.cc
src/compiler/js-inlining.cc
src/compiler/js-operator.cc
src/compiler/js-operator.h
src/compiler/linkage.cc
src/compiler/operator-properties.cc
src/runtime/runtime-scopes.cc
src/runtime/runtime.h
test/cctest/compiler/test-run-inlining.cc

index 6718535..48600b7 100644 (file)
@@ -3151,17 +3151,17 @@ Node* AstGraphBuilder::BuildArgumentsObject(Variable* arguments) {
   if (arguments == NULL) return NULL;
 
   // Allocate and initialize a new arguments object.
-  Node* callee = GetFunctionClosure();
   CreateArgumentsParameters::Type type =
       is_strict(language_mode()) || !info()->has_simple_parameters()
           ? CreateArgumentsParameters::kUnmappedArguments
           : CreateArgumentsParameters::kMappedArguments;
   const Operator* op = javascript()->CreateArguments(type, 0);
-  Node* object = NewNode(op, callee);
+  Node* object = NewNode(op, GetFunctionClosure());
+  PrepareFrameState(object, BailoutId::None());
 
-  // Assign the object to the arguments variable.
+  // Assign the object to the {arguments} variable. This should never lazy
+  // deopt, so it is fine to send invalid bailout id.
   DCHECK(arguments->IsContextSlot() || arguments->IsStackAllocated());
-  // This should never lazy deopt, so it is fine to send invalid bailout id.
   FrameStateBeforeAndAfter states(this, BailoutId::None());
   BuildVariableAssignment(arguments, object, Token::ASSIGN, VectorSlotPair(),
                           BailoutId::None(), states);
@@ -3175,7 +3175,8 @@ Node* AstGraphBuilder::BuildThisFunctionVariable(Variable* this_function_var) {
   // Retrieve the closure we were called with.
   Node* this_function = GetFunctionClosure();
 
-  // Assign the object to the {.this_function} variable.
+  // Assign the object to the {.this_function} variable. This should never lazy
+  // deopt, so it is fine to send invalid bailout id.
   FrameStateBeforeAndAfter states(this, BailoutId::None());
   BuildVariableAssignment(this_function_var, this_function, Token::INIT_CONST,
                           VectorSlotPair(), BailoutId::None(), states);
@@ -3191,7 +3192,8 @@ Node* AstGraphBuilder::BuildNewTargetVariable(Variable* new_target_var) {
       javascript()->CallRuntime(Runtime::kGetOriginalConstructor, 0);
   Node* object = NewNode(op);
 
-  // Assign the object to the {new.target} variable.
+  // Assign the object to the {new.target} variable. This should never lazy
+  // deopt, so it is fine to send invalid bailout id.
   FrameStateBeforeAndAfter states(this, BailoutId::None());
   BuildVariableAssignment(new_target_var, object, Token::INIT_CONST,
                           VectorSlotPair(), BailoutId::None(), states);
index c7c6b1f..ece5a72 100644 (file)
@@ -115,7 +115,6 @@ REPLACE_COMPARE_IC_CALL_WITH_LANGUAGE_MODE(JSGreaterThanOrEqual, Token::GTE)
     ReplaceWithRuntimeCall(node, fun);            \
   }
 REPLACE_RUNTIME_CALL(JSCreate, Runtime::kAbort)
-REPLACE_RUNTIME_CALL(JSCreateArguments, Runtime::kNewArguments)
 REPLACE_RUNTIME_CALL(JSCreateFunctionContext, Runtime::kNewFunctionContext)
 REPLACE_RUNTIME_CALL(JSCreateWithContext, Runtime::kPushWithContext)
 REPLACE_RUNTIME_CALL(JSCreateBlockContext, Runtime::kPushBlockContext)
@@ -490,6 +489,22 @@ void JSGenericLowering::LowerJSLoadDynamicContext(Node* node) {
 }
 
 
+void JSGenericLowering::LowerJSCreateArguments(Node* node) {
+  const CreateArgumentsParameters& p = CreateArgumentsParametersOf(node->op());
+  switch (p.type()) {
+    case CreateArgumentsParameters::kMappedArguments:
+      ReplaceWithRuntimeCall(node, Runtime::kNewSloppyArguments_Generic);
+      break;
+    case CreateArgumentsParameters::kUnmappedArguments:
+      ReplaceWithRuntimeCall(node, Runtime::kNewStrictArguments_Generic);
+      break;
+    case CreateArgumentsParameters::kRestArray:
+      UNIMPLEMENTED();
+      break;
+  }
+}
+
+
 void JSGenericLowering::LowerJSCreateClosure(Node* node) {
   CreateClosureParameters p = CreateClosureParametersOf(node->op());
   node->InsertInput(zone(), 0, jsgraph()->HeapConstant(p.shared_info()));
index 8e98547..5f97835 100644 (file)
@@ -138,18 +138,17 @@ Reduction JSInliner::InlineCall(Node* call, Node* context, Node* frame_state,
     switch (use->opcode()) {
       case IrOpcode::kParameter: {
         int index = 1 + ParameterIndexOf(use->op());
+        DCHECK_LE(index, inlinee_context_index);
         if (index < inliner_inputs && index < inlinee_context_index) {
           // There is an input from the call, and the index is a value
           // projection but not the context, so rewire the input.
           Replace(use, call->InputAt(index));
         } else if (index == inlinee_context_index) {
+          // The projection is requesting the inlinee function context.
           Replace(use, context);
-        } else if (index < inlinee_context_index) {
+        } else {
           // Call has fewer arguments than required, fill with undefined.
           Replace(use, jsgraph_->UndefinedConstant());
-        } else {
-          // We got too many arguments, discard for now.
-          // TODO(sigurds): Fix to treat arguments array correctly.
         }
         break;
       }
@@ -319,14 +318,6 @@ Reduction JSInliner::Reduce(Node* node) {
     return NoChange();
   }
 
-  if (info.scope()->arguments() != NULL && is_sloppy(info.language_mode())) {
-    // For now do not inline functions that use their arguments array.
-    TRACE("Not inlining %s into %s because inlinee uses arguments array\n",
-          function->shared()->DebugName()->ToCString().get(),
-          info_->shared_info()->DebugName()->ToCString().get());
-    return NoChange();
-  }
-
   TRACE("Inlining %s into %s\n",
         function->shared()->DebugName()->ToCString().get(),
         info_->shared_info()->DebugName()->ToCString().get());
index 975ca0c..e7de5ee 100644 (file)
@@ -413,6 +413,13 @@ std::ostream& operator<<(std::ostream& os, CreateArgumentsParameters const& p) {
 }
 
 
+const CreateArgumentsParameters& CreateArgumentsParametersOf(
+    const Operator* op) {
+  DCHECK_EQ(IrOpcode::kJSCreateArguments, op->opcode());
+  return OpParameter<CreateArgumentsParameters>(op);
+}
+
+
 bool operator==(CreateClosureParameters const& lhs,
                 CreateClosureParameters const& rhs) {
   return lhs.pretenure() == rhs.pretenure() &&
index 2c281e7..4846add 100644 (file)
@@ -432,6 +432,9 @@ size_t hash_value(CreateArgumentsParameters const&);
 
 std::ostream& operator<<(std::ostream&, CreateArgumentsParameters const&);
 
+const CreateArgumentsParameters& CreateArgumentsParametersOf(
+    const Operator* op);
+
 
 // Defines shared information for the closure that should be created. This is
 // used as a parameter by JSCreateClosure operators.
index 097d3ab..1def1d9 100644 (file)
@@ -225,7 +225,6 @@ int Linkage::FrameStateInputCount(Runtime::FunctionId function) {
     case Runtime::kForInDone:
     case Runtime::kForInStep:
     case Runtime::kGetOriginalConstructor:
-    case Runtime::kNewArguments:
     case Runtime::kNewClosure:
     case Runtime::kNewClosure_Tenured:
     case Runtime::kNewFunctionContext:
index 6de6d24..60e6ad7 100644 (file)
@@ -45,6 +45,7 @@ int OperatorProperties::GetFrameStateInputCount(const Operator* op) {
     case IrOpcode::kJSInstanceOf:
 
     // Object operations
+    case IrOpcode::kJSCreateArguments:
     case IrOpcode::kJSCreateLiteralArray:
     case IrOpcode::kJSCreateLiteralObject:
 
index cf0429f..c3928a7 100644 (file)
@@ -404,10 +404,11 @@ RUNTIME_FUNCTION(Runtime_InitializeLegacyConstLookupSlot) {
 }
 
 
-static Handle<JSObject> NewSloppyArguments(Isolate* isolate,
-                                           Handle<JSFunction> callee,
-                                           Object** parameters,
-                                           int argument_count) {
+namespace {
+
+template <typename T>
+Handle<JSObject> NewSloppyArguments(Isolate* isolate, Handle<JSFunction> callee,
+                                    T parameters, int argument_count) {
   CHECK(!IsSubclassConstructor(callee->shared()->kind()));
   DCHECK(callee->has_simple_parameters());
   Handle<JSObject> result =
@@ -437,7 +438,7 @@ static Handle<JSObject> NewSloppyArguments(Isolate* isolate,
       while (index >= mapped_count) {
         // These go directly in the arguments array and have no
         // corresponding slot in the parameter map.
-        arguments->set(index, *(parameters - index - 1));
+        arguments->set(index, parameters[index]);
         --index;
       }
 
@@ -457,7 +458,7 @@ static Handle<JSObject> NewSloppyArguments(Isolate* isolate,
         if (duplicate) {
           // This goes directly in the arguments array with a hole in the
           // parameter map.
-          arguments->set(index, *(parameters - index - 1));
+          arguments->set(index, parameters[index]);
           parameter_map->set_the_hole(index + 2);
         } else {
           // The context index goes in the parameter map with a hole in the
@@ -486,7 +487,7 @@ static Handle<JSObject> NewSloppyArguments(Isolate* isolate,
           isolate->factory()->NewFixedArray(argument_count, NOT_TENURED);
       result->set_elements(*elements);
       for (int i = 0; i < argument_count; ++i) {
-        elements->set(i, *(parameters - i - 1));
+        elements->set(i, parameters[i]);
       }
     }
   }
@@ -494,10 +495,9 @@ static Handle<JSObject> NewSloppyArguments(Isolate* isolate,
 }
 
 
-static Handle<JSObject> NewStrictArguments(Isolate* isolate,
-                                           Handle<JSFunction> callee,
-                                           Object** parameters,
-                                           int argument_count) {
+template <typename T>
+Handle<JSObject> NewStrictArguments(Isolate* isolate, Handle<JSFunction> callee,
+                                    T parameters, int argument_count) {
   Handle<JSObject> result =
       isolate->factory()->NewArgumentsObject(callee, argument_count);
 
@@ -507,7 +507,7 @@ static Handle<JSObject> NewStrictArguments(Isolate* isolate,
     DisallowHeapAllocation no_gc;
     WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
     for (int i = 0; i < argument_count; i++) {
-      array->set(i, *--parameters, mode);
+      array->set(i, parameters[i], mode);
     }
     result->set_elements(*array);
   }
@@ -515,24 +515,53 @@ static Handle<JSObject> NewStrictArguments(Isolate* isolate,
 }
 
 
-RUNTIME_FUNCTION(Runtime_NewArguments) {
+class HandleArguments BASE_EMBEDDED {
+ public:
+  explicit HandleArguments(Handle<Object>* array) : array_(array) {}
+  Object* operator[](int index) { return *array_[index]; }
+
+ private:
+  Handle<Object>* array_;
+};
+
+
+class ParameterArguments BASE_EMBEDDED {
+ public:
+  explicit ParameterArguments(Object** parameters) : parameters_(parameters) {}
+  Object*& operator[](int index) { return *(parameters_ - index - 1); }
+
+ private:
+  Object** parameters_;
+};
+
+}  // namespace
+
+
+RUNTIME_FUNCTION(Runtime_NewSloppyArguments_Generic) {
   HandleScope scope(isolate);
   DCHECK(args.length() == 1);
   CONVERT_ARG_HANDLE_CHECKED(JSFunction, callee, 0);
-  JavaScriptFrameIterator it(isolate);
-
-  // Find the frame that holds the actual arguments passed to the function.
-  it.AdvanceToArgumentsFrame();
-  JavaScriptFrame* frame = it.frame();
+  // This generic runtime function can also be used when the caller has been
+  // inlined, we use the slow but accurate {Runtime::GetCallerArguments}.
+  int argument_count = 0;
+  base::SmartArrayPointer<Handle<Object>> arguments =
+      Runtime::GetCallerArguments(isolate, 0, &argument_count);
+  HandleArguments argument_getter(arguments.get());
+  return *NewSloppyArguments(isolate, callee, argument_getter, argument_count);
+}
 
-  // Determine parameter location on the stack and dispatch on language mode.
-  int argument_count = frame->GetArgumentsLength();
-  Object** parameters = reinterpret_cast<Object**>(frame->GetParameterSlot(-1));
 
-  return (is_strict(callee->shared()->language_mode()) ||
-             !callee->has_simple_parameters())
-             ? *NewStrictArguments(isolate, callee, parameters, argument_count)
-             : *NewSloppyArguments(isolate, callee, parameters, argument_count);
+RUNTIME_FUNCTION(Runtime_NewStrictArguments_Generic) {
+  HandleScope scope(isolate);
+  DCHECK(args.length() == 1);
+  CONVERT_ARG_HANDLE_CHECKED(JSFunction, callee, 0);
+  // This generic runtime function can also be used when the caller has been
+  // inlined, we use the slow but accurate {Runtime::GetCallerArguments}.
+  int argument_count = 0;
+  base::SmartArrayPointer<Handle<Object>> arguments =
+      Runtime::GetCallerArguments(isolate, 0, &argument_count);
+  HandleArguments argument_getter(arguments.get());
+  return *NewStrictArguments(isolate, callee, argument_getter, argument_count);
 }
 
 
@@ -548,7 +577,8 @@ RUNTIME_FUNCTION(Runtime_NewSloppyArguments) {
   JavaScriptFrameIterator it(isolate);
   DCHECK(!it.frame()->HasInlinedFrames());
 #endif  // DEBUG
-  return *NewSloppyArguments(isolate, callee, parameters, argument_count);
+  ParameterArguments argument_getter(parameters);
+  return *NewSloppyArguments(isolate, callee, argument_getter, argument_count);
 }
 
 
@@ -564,7 +594,8 @@ RUNTIME_FUNCTION(Runtime_NewStrictArguments) {
   JavaScriptFrameIterator it(isolate);
   DCHECK(!it.frame()->HasInlinedFrames());
 #endif  // DEBUG
-  return *NewStrictArguments(isolate, callee, parameters, argument_count);
+  ParameterArguments argument_getter(parameters);
+  return *NewStrictArguments(isolate, callee, argument_getter, argument_count);
 }
 
 
index f20e1ab..78e3d92 100644 (file)
@@ -547,30 +547,31 @@ namespace internal {
   F(IsRegExp, 1, 1)
 
 
-#define FOR_EACH_INTRINSIC_SCOPES(F)                         \
-  F(ThrowConstAssignError, 0, 1)                             \
-  F(DeclareGlobals, 2, 1)                                    \
-  F(InitializeVarGlobal, 3, 1)                               \
-  F(InitializeConstGlobal, 2, 1)                             \
-  F(DeclareLookupSlot, 2, 1)                                 \
-  F(DeclareReadOnlyLookupSlot, 2, 1)                         \
-  F(InitializeLegacyConstLookupSlot, 3, 1)                   \
-  F(NewArguments, 1, 1) /* TODO(turbofan): Only temporary */ \
-  F(NewSloppyArguments, 3, 1)                                \
-  F(NewStrictArguments, 3, 1)                                \
-  F(NewClosure, 1, 1)                                        \
-  F(NewClosure_Tenured, 1, 1)                                \
-  F(NewScriptContext, 2, 1)                                  \
-  F(NewFunctionContext, 1, 1)                                \
-  F(PushWithContext, 2, 1)                                   \
-  F(PushCatchContext, 3, 1)                                  \
-  F(PushBlockContext, 2, 1)                                  \
-  F(IsJSModule, 1, 1)                                        \
-  F(PushModuleContext, 2, 1)                                 \
-  F(DeclareModules, 1, 1)                                    \
-  F(DeleteLookupSlot, 2, 1)                                  \
-  F(StoreLookupSlot, 4, 1)                                   \
-  F(ArgumentsLength, 0, 1)                                   \
+#define FOR_EACH_INTRINSIC_SCOPES(F)       \
+  F(ThrowConstAssignError, 0, 1)           \
+  F(DeclareGlobals, 2, 1)                  \
+  F(InitializeVarGlobal, 3, 1)             \
+  F(InitializeConstGlobal, 2, 1)           \
+  F(DeclareLookupSlot, 2, 1)               \
+  F(DeclareReadOnlyLookupSlot, 2, 1)       \
+  F(InitializeLegacyConstLookupSlot, 3, 1) \
+  F(NewSloppyArguments_Generic, 1, 1)      \
+  F(NewStrictArguments_Generic, 1, 1)      \
+  F(NewSloppyArguments, 3, 1)              \
+  F(NewStrictArguments, 3, 1)              \
+  F(NewClosure, 1, 1)                      \
+  F(NewClosure_Tenured, 1, 1)              \
+  F(NewScriptContext, 2, 1)                \
+  F(NewFunctionContext, 1, 1)              \
+  F(PushWithContext, 2, 1)                 \
+  F(PushCatchContext, 3, 1)                \
+  F(PushBlockContext, 2, 1)                \
+  F(IsJSModule, 1, 1)                      \
+  F(PushModuleContext, 2, 1)               \
+  F(DeclareModules, 1, 1)                  \
+  F(DeleteLookupSlot, 2, 1)                \
+  F(StoreLookupSlot, 4, 1)                 \
+  F(ArgumentsLength, 0, 1)                 \
   F(Arguments, 1, 1)
 
 
index 5119bb7..8aa9d03 100644 (file)
@@ -161,7 +161,8 @@ TEST(InlineOmitArguments) {
       "(function () {"
       "  var x = 42;"
       "  function bar(s, t, u, v) { AssertInlineCount(2); return x + s; };"
-      "  return (function (s,t) { return bar(s); });"
+      "  function foo(s, t) { return bar(s); };"
+      "  return foo;"
       "})();",
       kInlineFlags);
 
@@ -170,6 +171,22 @@ TEST(InlineOmitArguments) {
 }
 
 
+TEST(InlineOmitArgumentsObject) {
+  FunctionTester T(
+      "(function () {"
+      "  function bar(s, t, u, v) { AssertInlineCount(2); return arguments; };"
+      "  function foo(s, t) { var args = bar(s);"
+      "                       return args.length == 1 &&"
+      "                              args[0] == 11; };"
+      "  return foo;"
+      "})();",
+      kInlineFlags);
+
+  InstallAssertInlineCountHelper(CcTest::isolate());
+  T.CheckCall(T.true_value(), T.Val(11), T.undefined());
+}
+
+
 TEST(InlineOmitArgumentsDeopt) {
   FunctionTester T(
       "(function () {"
@@ -192,7 +209,7 @@ TEST(InlineSurplusArguments) {
       "(function () {"
       "  var x = 42;"
       "  function foo(s) { AssertInlineCount(2); return x + s; };"
-      "  function bar(s,t) { return foo(s,t,13); };"
+      "  function bar(s, t) { return foo(s, t, 13); };"
       "  return bar;"
       "})();",
       kInlineFlags);
@@ -202,6 +219,24 @@ TEST(InlineSurplusArguments) {
 }
 
 
+TEST(InlineSurplusArgumentsObject) {
+  FunctionTester T(
+      "(function () {"
+      "  function foo(s) { AssertInlineCount(2); return arguments; };"
+      "  function bar(s, t) { var args = foo(s, t, 13);"
+      "                       return args.length == 3 &&"
+      "                              args[0] == 11 &&"
+      "                              args[1] == 12 &&"
+      "                              args[2] == 13; };"
+      "  return bar;"
+      "})();",
+      kInlineFlags);
+
+  InstallAssertInlineCountHelper(CcTest::isolate());
+  T.CheckCall(T.true_value(), T.Val(11), T.Val(12));
+}
+
+
 TEST(InlineSurplusArgumentsDeopt) {
   FunctionTester T(
       "(function () {"