From 0cdd84e230ea9a3e4edef922fd81179e282d017b Mon Sep 17 00:00:00 2001 From: Tobias Tebbi Date: Fri, 10 Nov 2017 14:36:12 +0100 Subject: [PATCH] [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 Commit-Queue: Tobias Tebbi Cr-Commit-Position: refs/heads/master@{#49387} --- src/compiler/graph-assembler.h | 3 +++ src/compiler/js-builtin-reducer.cc | 6 ++++++ src/compiler/js-call-reducer.cc | 22 ++++++++++++++-------- src/compiler/js-typed-lowering.cc | 2 ++ src/compiler/verifier.cc | 17 ++++++++++++++++- 5 files changed, 41 insertions(+), 9 deletions(-) diff --git a/src/compiler/graph-assembler.h b/src/compiler/graph-assembler.h index 2fc8d226e7..f6d882ab27 100644 --- a/src/compiler/graph-assembler.h +++ b/src/compiler/graph-assembler.h @@ -285,6 +285,9 @@ void GraphAssembler::MergeState(GraphAssemblerLabel* 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], diff --git a/src/compiler/js-builtin-reducer.cc b/src/compiler/js-builtin-reducer.cc index 553a8c126e..e2490ba018 100644 --- a/src/compiler/js-builtin-reducer.cc +++ b/src/compiler/js-builtin-reducer.cc @@ -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); diff --git a/src/compiler/js-call-reducer.cc b/src/compiler/js-call-reducer.cc index ebf040a6c7..56a614ffa5 100644 --- a/src/compiler/js-call-reducer.cc +++ b/src/compiler/js-call-reducer.cc @@ -935,6 +935,8 @@ Reduction JSCallReducer::ReduceArrayForEach(Handle 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 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 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 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 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 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); diff --git a/src/compiler/js-typed-lowering.cc b/src/compiler/js-typed-lowering.cc index 54d09e1a26..2bc315abc7 100644 --- a/src/compiler/js-typed-lowering.cc +++ b/src/compiler/js-typed-lowering.cc @@ -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()); diff --git a/src/compiler/verifier.cc b/src/compiler/verifier.cc index 3bd86122ea..cd40a2500b 100644 --- a/src/compiler/verifier.cc +++ b/src/compiler/verifier.cc @@ -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.