[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:
parent
2d8d02f555
commit
2c54dbda35
@ -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);
|
||||
|
@ -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()));
|
||||
|
@ -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());
|
||||
|
@ -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() &&
|
||||
|
@ -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.
|
||||
|
@ -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:
|
||||
|
@ -45,6 +45,7 @@ int OperatorProperties::GetFrameStateInputCount(const Operator* op) {
|
||||
case IrOpcode::kJSInstanceOf:
|
||||
|
||||
// Object operations
|
||||
case IrOpcode::kJSCreateArguments:
|
||||
case IrOpcode::kJSCreateLiteralArray:
|
||||
case IrOpcode::kJSCreateLiteralObject:
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
||||
|
@ -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 () {"
|
||||
|
Loading…
Reference in New Issue
Block a user