[turbofan] add Terminate nodes to all loops

This simplifies the existing invariant and enables loop-peeling on all loops.
The main motivation is that it enables dead code elimination to always eagerly
fold away branches even when this would create infinite loops.

Bug: 
Change-Id: If4347f748f8d8735965771f66260a8f931b24132
Reviewed-on: https://chromium-review.googlesource.com/763531
Reviewed-by: Michael Starzinger <mstarzinger@chromium.org>
Commit-Queue: Tobias Tebbi <tebbi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#49387}
This commit is contained in:
Tobias Tebbi 2017-11-10 14:36:12 +01:00 committed by Commit Bot
parent aafdfba899
commit 0cdd84e230
5 changed files with 41 additions and 9 deletions

View File

@ -285,6 +285,9 @@ void GraphAssembler::MergeState(GraphAssemblerLabel<sizeof...(Vars)>* label,
current_control_);
label->effect_ = graph()->NewNode(common()->EffectPhi(2), current_effect_,
current_effect_, label->control_);
Node* terminate = graph()->NewNode(common()->Terminate(), label->effect_,
label->control_);
NodeProperties::MergeControlToEnd(graph(), common(), terminate);
for (size_t i = 0; i < sizeof...(vars); i++) {
label->bindings_[i] = graph()->NewNode(
common()->Phi(label->representations_[i], 2), var_array[i + 1],

View File

@ -1183,6 +1183,8 @@ Reduction JSBuiltinReducer::ReduceArrayShift(Node* node) {
Node* loop = graph()->NewNode(common()->Loop(2), if_true1, if_true1);
Node* eloop =
graph()->NewNode(common()->EffectPhi(2), etrue1, etrue1, loop);
Node* terminate = graph()->NewNode(common()->Terminate(), eloop, loop);
NodeProperties::MergeControlToEnd(graph(), common(), terminate);
Node* index = graph()->NewNode(
common()->Phi(MachineRepresentation::kTagged, 2),
jsgraph()->OneConstant(),
@ -1414,6 +1416,8 @@ Reduction JSBuiltinReducer::ReduceCollectionIteratorNext(
graph()->NewNode(common()->Loop(2), control, control);
Node* eloop = effect =
graph()->NewNode(common()->EffectPhi(2), effect, effect, loop);
Node* terminate = graph()->NewNode(common()->Terminate(), eloop, loop);
NodeProperties::MergeControlToEnd(graph(), common(), terminate);
// Check if reached the final table of the {receiver}.
Node* table = effect = graph()->NewNode(
@ -1502,6 +1506,8 @@ Reduction JSBuiltinReducer::ReduceCollectionIteratorNext(
Node* loop = graph()->NewNode(common()->Loop(2), control, control);
Node* eloop =
graph()->NewNode(common()->EffectPhi(2), effect, effect, loop);
Node* terminate = graph()->NewNode(common()->Terminate(), eloop, loop);
NodeProperties::MergeControlToEnd(graph(), common(), terminate);
Node* iloop = graph()->NewNode(
common()->Phi(MachineRepresentation::kTagged, 2), index, index, loop);
NodeProperties::SetType(iloop, type_cache_.kFixedArrayLengthType);

View File

@ -935,6 +935,8 @@ Reduction JSCallReducer::ReduceArrayForEach(Handle<JSFunction> function,
Node* loop = control = graph()->NewNode(common()->Loop(2), control, control);
Node* eloop = effect =
graph()->NewNode(common()->EffectPhi(2), effect, effect, loop);
Node* terminate = graph()->NewNode(common()->Terminate(), eloop, loop);
NodeProperties::MergeControlToEnd(graph(), common(), terminate);
Node* vloop = k = graph()->NewNode(
common()->Phi(MachineRepresentation::kTagged, 2), k, k, loop);
checkpoint_params[3] = k;
@ -1032,9 +1034,9 @@ Reduction JSCallReducer::ReduceArrayForEach(Handle<JSFunction> function,
// Since {check_throw} is an unconditional throw, it's impossible to
// return a successful completion. Therefore, we simply connect the successful
// completion to the graph end.
Node* terminate =
Node* throw_node =
graph()->NewNode(common()->Throw(), check_throw, check_fail);
NodeProperties::MergeControlToEnd(graph(), common(), terminate);
NodeProperties::MergeControlToEnd(graph(), common(), throw_node);
ReplaceWithValue(node, jsgraph()->UndefinedConstant(), effect, control);
return Replace(jsgraph()->UndefinedConstant());
@ -1135,6 +1137,8 @@ Reduction JSCallReducer::ReduceArrayMap(Handle<JSFunction> function,
Node* loop = control = graph()->NewNode(common()->Loop(2), control, control);
Node* eloop = effect =
graph()->NewNode(common()->EffectPhi(2), effect, effect, loop);
Node* terminate = graph()->NewNode(common()->Terminate(), eloop, loop);
NodeProperties::MergeControlToEnd(graph(), common(), terminate);
Node* vloop = k = graph()->NewNode(
common()->Phi(MachineRepresentation::kTagged, 2), k, k, loop);
checkpoint_params[4] = k;
@ -1208,9 +1212,9 @@ Reduction JSCallReducer::ReduceArrayMap(Handle<JSFunction> function,
// Since {check_throw} is an unconditional throw, it's impossible to
// return a successful completion. Therefore, we simply connect the successful
// completion to the graph end.
Node* terminate =
Node* throw_node =
graph()->NewNode(common()->Throw(), check_throw, check_fail);
NodeProperties::MergeControlToEnd(graph(), common(), terminate);
NodeProperties::MergeControlToEnd(graph(), common(), throw_node);
ReplaceWithValue(node, a, effect, control);
return Replace(a);
@ -1323,6 +1327,8 @@ Reduction JSCallReducer::ReduceArrayFilter(Handle<JSFunction> function,
Node* loop = control = graph()->NewNode(common()->Loop(2), control, control);
Node* eloop = effect =
graph()->NewNode(common()->EffectPhi(2), effect, effect, loop);
Node* terminate = graph()->NewNode(common()->Terminate(), eloop, loop);
NodeProperties::MergeControlToEnd(graph(), common(), terminate);
Node* vloop = k = graph()->NewNode(
common()->Phi(MachineRepresentation::kTagged, 2), k, k, loop);
Node* v_to_loop = to = graph()->NewNode(
@ -1429,9 +1435,9 @@ Reduction JSCallReducer::ReduceArrayFilter(Handle<JSFunction> function,
// Since {check_throw} is an unconditional throw, it's impossible to
// return a successful completion. Therefore, we simply connect the successful
// completion to the graph end.
Node* terminate =
Node* throw_node =
graph()->NewNode(common()->Throw(), check_throw, check_fail);
NodeProperties::MergeControlToEnd(graph(), common(), terminate);
NodeProperties::MergeControlToEnd(graph(), common(), throw_node);
ReplaceWithValue(node, a, effect, control);
return Replace(a);
@ -1881,9 +1887,9 @@ Reduction JSCallReducer::ReduceCallOrConstructWithArrayLikeOrSpread(
// The above %ThrowTypeError runtime call is an unconditional throw, making
// it impossible to return a successful completion in this case. We simply
// connect the successful completion to the graph end.
Node* terminate =
Node* throw_node =
graph()->NewNode(common()->Throw(), check_throw, check_fail);
NodeProperties::MergeControlToEnd(graph(), common(), terminate);
NodeProperties::MergeControlToEnd(graph(), common(), throw_node);
Reduction const reduction = ReduceJSConstruct(node);
return reduction.Changed() ? reduction : Changed(node);

View File

@ -1113,6 +1113,8 @@ Reduction JSTypedLowering::ReduceJSHasInPrototypeChain(Node* node) {
Node* loop = control = graph()->NewNode(common()->Loop(2), control, control);
Node* eloop = effect =
graph()->NewNode(common()->EffectPhi(2), effect, effect, loop);
Node* terminate = graph()->NewNode(common()->Terminate(), eloop, loop);
NodeProperties::MergeControlToEnd(graph(), common(), terminate);
Node* vloop = value = graph()->NewNode(
common()->Phi(MachineRepresentation::kTagged, 2), value, value, loop);
NodeProperties::SetType(vloop, Type::NonInternal());

View File

@ -316,7 +316,22 @@ void Verifier::Visitor::Check(Node* node) {
// Type is empty.
CheckNotTyped(node);
break;
case IrOpcode::kLoop:
case IrOpcode::kLoop: {
CHECK_EQ(control_count, input_count);
// Type is empty.
CheckNotTyped(node);
// All loops need to be connected to a {Terminate} node to ensure they
// stay connected to the graph end.
bool has_terminate = false;
for (const Node* use : node->uses()) {
if (use->opcode() == IrOpcode::kTerminate) {
has_terminate = true;
break;
}
}
CHECK(has_terminate);
break;
}
case IrOpcode::kMerge:
CHECK_EQ(control_count, input_count);
// Type is empty.