Commit a61a6f99 authored by bmeurer's avatar bmeurer Committed by Commit bot

[turbofan] Allow to consume feedback on CallConstruct.

Add an eager deoptimization location for JSCallConstruct and adapt the
JSCallReducer to consume target feedback for construction sites (only
applies to explicit new F(...args) not the super constructor calls).
Also recognize the new Array(...args) constructs with only target
feedback.

R=jarin@chromium.org
BUG=v8:4470
LOG=n

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

Cr-Commit-Position: refs/heads/master@{#32177}
parent 0da1a0c0
...@@ -2514,8 +2514,9 @@ void AstGraphBuilder::VisitCallSuper(Call* expr) { ...@@ -2514,8 +2514,9 @@ void AstGraphBuilder::VisitCallSuper(Call* expr) {
// Create node to perform the super call. // Create node to perform the super call.
const Operator* call = const Operator* call =
javascript()->CallConstruct(args->length() + 2, VectorSlotPair()); javascript()->CallConstruct(args->length() + 2, VectorSlotPair());
FrameStateBeforeAndAfter states(this, super->new_target_var()->id());
Node* value = ProcessArguments(call, args->length() + 2); Node* value = ProcessArguments(call, args->length() + 2);
PrepareFrameState(value, expr->id(), ast_context()->GetStateCombine()); states.AddToNode(value, expr->ReturnId(), OutputFrameStateCombine::Push());
ast_context()->ProduceValue(value); ast_context()->ProduceValue(value);
} }
...@@ -2527,6 +2528,11 @@ void AstGraphBuilder::VisitCallNew(CallNew* expr) { ...@@ -2527,6 +2528,11 @@ void AstGraphBuilder::VisitCallNew(CallNew* expr) {
ZoneList<Expression*>* args = expr->arguments(); ZoneList<Expression*>* args = expr->arguments();
VisitForValues(args); VisitForValues(args);
// The baseline compiler doesn't push the new.target, so we need to record
// the frame state before the push.
FrameStateBeforeAndAfter states(
this, args->is_empty() ? expr->expression()->id() : args->last()->id());
// The new target is the same as the callee. // The new target is the same as the callee.
environment()->Push(environment()->Peek(args->length())); environment()->Push(environment()->Peek(args->length()));
...@@ -2535,7 +2541,7 @@ void AstGraphBuilder::VisitCallNew(CallNew* expr) { ...@@ -2535,7 +2541,7 @@ void AstGraphBuilder::VisitCallNew(CallNew* expr) {
const Operator* call = const Operator* call =
javascript()->CallConstruct(args->length() + 2, feedback); javascript()->CallConstruct(args->length() + 2, feedback);
Node* value = ProcessArguments(call, args->length() + 2); Node* value = ProcessArguments(call, args->length() + 2);
PrepareFrameState(value, expr->id(), ast_context()->GetStateCombine()); states.AddToNode(value, expr->ReturnId(), OutputFrameStateCombine::Push());
ast_context()->ProduceValue(value); ast_context()->ProduceValue(value);
} }
......
...@@ -329,6 +329,10 @@ Reduction JSCallReducer::ReduceJSCallConstruct(Node* node) { ...@@ -329,6 +329,10 @@ Reduction JSCallReducer::ReduceJSCallConstruct(Node* node) {
int const arity = static_cast<int>(p.arity() - 2); int const arity = static_cast<int>(p.arity() - 2);
Node* target = NodeProperties::GetValueInput(node, 0); Node* target = NodeProperties::GetValueInput(node, 0);
Node* new_target = NodeProperties::GetValueInput(node, arity + 1); Node* new_target = NodeProperties::GetValueInput(node, arity + 1);
Node* context = NodeProperties::GetContextInput(node);
Node* frame_state = NodeProperties::GetFrameStateInput(node, 1);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
// Try to specialize JSCallConstruct {node}s with constant {target}s. // Try to specialize JSCallConstruct {node}s with constant {target}s.
HeapObjectMatcher m(target); HeapObjectMatcher m(target);
...@@ -338,6 +342,11 @@ Reduction JSCallReducer::ReduceJSCallConstruct(Node* node) { ...@@ -338,6 +342,11 @@ Reduction JSCallReducer::ReduceJSCallConstruct(Node* node) {
// Raise a TypeError if the {target} is not a constructor. // Raise a TypeError if the {target} is not a constructor.
if (!function->IsConstructor()) { if (!function->IsConstructor()) {
// Drop the lazy bailout location and use the eager bailout point for
// the runtime function (actually as lazy bailout point). It doesn't
// really matter which bailout location we use since we never really
// go back after throwing the exception.
NodeProperties::RemoveFrameStateInput(node, 0);
NodeProperties::ReplaceValueInputs(node, target); NodeProperties::ReplaceValueInputs(node, target);
NodeProperties::ChangeOp( NodeProperties::ChangeOp(
node, node,
...@@ -358,6 +367,7 @@ Reduction JSCallReducer::ReduceJSCallConstruct(Node* node) { ...@@ -358,6 +367,7 @@ Reduction JSCallReducer::ReduceJSCallConstruct(Node* node) {
} }
// Turn the {node} into a {JSCreateArray} call. // Turn the {node} into a {JSCreateArray} call.
NodeProperties::RemoveFrameStateInput(node, 1);
for (int i = arity; i > 0; --i) { for (int i = arity; i > 0; --i) {
NodeProperties::ReplaceValueInput( NodeProperties::ReplaceValueInput(
node, NodeProperties::GetValueInput(node, i), i + 1); node, NodeProperties::GetValueInput(node, i), i + 1);
...@@ -373,6 +383,94 @@ Reduction JSCallReducer::ReduceJSCallConstruct(Node* node) { ...@@ -373,6 +383,94 @@ Reduction JSCallReducer::ReduceJSCallConstruct(Node* node) {
return NoChange(); return NoChange();
} }
// Not much we can do if deoptimization support is disabled.
if (!(flags() & kDeoptimizationEnabled)) return NoChange();
// TODO(mvstanton): Use ConstructICNexus here, once available.
Handle<Object> feedback;
if (!p.feedback().IsValid()) return NoChange();
feedback = handle(p.feedback().vector()->Get(p.feedback().slot()), isolate());
if (feedback->IsAllocationSite()) {
// The feedback is an AllocationSite, which means we have called the
// Array function and collected transition (and pretenuring) feedback
// for the resulting arrays. This has to be kept in sync with the
// implementation of the CallConstructStub.
Handle<AllocationSite> site = Handle<AllocationSite>::cast(feedback);
// Retrieve the Array function from the {node}.
Node* array_function;
Handle<Context> native_context;
if (GetNativeContext(node).ToHandle(&native_context)) {
array_function = jsgraph()->HeapConstant(
handle(native_context->array_function(), isolate()));
} else {
Node* global_object = effect = graph()->NewNode(
javascript()->LoadContext(0, Context::GLOBAL_OBJECT_INDEX, true),
context, context, effect);
Node* native_context = effect = graph()->NewNode(
javascript()->LoadNativeContext(), global_object, context, effect);
array_function = effect = graph()->NewNode(
javascript()->LoadContext(0, Context::ARRAY_FUNCTION_INDEX, true),
native_context, native_context, effect);
}
// Check that the {target} is still the {array_function}.
Node* check = effect =
graph()->NewNode(javascript()->StrictEqual(), target, array_function,
context, effect, control);
Node* branch =
graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
Node* deoptimize =
graph()->NewNode(common()->Deoptimize(), frame_state, effect, if_false);
// TODO(bmeurer): This should be on the AdvancedReducer somehow.
NodeProperties::MergeControlToEnd(graph(), common(), deoptimize);
control = graph()->NewNode(common()->IfTrue(), branch);
// Turn the {node} into a {JSCreateArray} call.
NodeProperties::ReplaceEffectInput(node, effect);
NodeProperties::ReplaceControlInput(node, control);
NodeProperties::RemoveFrameStateInput(node, 1);
for (int i = arity; i > 0; --i) {
NodeProperties::ReplaceValueInput(
node, NodeProperties::GetValueInput(node, i), i + 1);
}
NodeProperties::ReplaceValueInput(node, new_target, 1);
NodeProperties::ChangeOp(node, javascript()->CreateArray(arity, site));
return Changed(node);
} else if (feedback->IsWeakCell()) {
Handle<WeakCell> cell = Handle<WeakCell>::cast(feedback);
if (cell->value()->IsJSFunction()) {
Node* target_function =
jsgraph()->Constant(handle(cell->value(), isolate()));
// Check that the {target} is still the {target_function}.
Node* check = effect =
graph()->NewNode(javascript()->StrictEqual(), target, target_function,
context, effect, control);
Node* branch =
graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
Node* deoptimize = graph()->NewNode(common()->Deoptimize(), frame_state,
effect, if_false);
// TODO(bmeurer): This should be on the AdvancedReducer somehow.
NodeProperties::MergeControlToEnd(graph(), common(), deoptimize);
control = graph()->NewNode(common()->IfTrue(), branch);
// Specialize the JSCallConstruct node to the {target_function}.
NodeProperties::ReplaceValueInput(node, target_function, 0);
NodeProperties::ReplaceEffectInput(node, effect);
NodeProperties::ReplaceControlInput(node, control);
if (target == new_target) {
NodeProperties::ReplaceValueInput(node, target_function, arity + 1);
}
// Try to further reduce the JSCallConstruct {node}.
Reduction const reduction = ReduceJSCallConstruct(node);
return reduction.Changed() ? reduction : Changed(node);
}
}
return NoChange(); return NoChange();
} }
......
...@@ -55,7 +55,6 @@ class JSCallAccessor { ...@@ -55,7 +55,6 @@ class JSCallAccessor {
} }
Node* frame_state_before() { Node* frame_state_before() {
DCHECK_EQ(IrOpcode::kJSCallFunction, call_->opcode());
return NodeProperties::GetFrameStateInput(call_, 1); return NodeProperties::GetFrameStateInput(call_, 1);
} }
......
...@@ -35,13 +35,11 @@ int OperatorProperties::GetFrameStateInputCount(const Operator* op) { ...@@ -35,13 +35,11 @@ int OperatorProperties::GetFrameStateInputCount(const Operator* op) {
return 0; return 0;
// We record the frame state immediately before and immediately after every // We record the frame state immediately before and immediately after every
// function call. // construct/function call.
case IrOpcode::kJSCallConstruct:
case IrOpcode::kJSCallFunction: case IrOpcode::kJSCallFunction:
return 2; return 2;
// Construct calls
case IrOpcode::kJSCallConstruct:
// Compare operations // Compare operations
case IrOpcode::kJSEqual: case IrOpcode::kJSEqual:
case IrOpcode::kJSNotEqual: case IrOpcode::kJSNotEqual:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment