[turbofan] Make arguments object materialization inlinable.

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}
This commit is contained in:
mstarzinger 2015-09-16 06:04:25 -07:00 committed by Commit bot
parent 2d8d02f555
commit 2c54dbda35
10 changed files with 158 additions and 73 deletions

View 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);

View 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()));

View 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());

View 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() &&

View 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.

View 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:

View 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:

View 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);
// 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);
}
// Find the frame that holds the actual arguments passed to the function.
it.AdvanceToArgumentsFrame();
JavaScriptFrame* frame = it.frame();
// 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);
}

View 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)

View 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 () {"