Commit 6bead6bd authored by Jun Lim's avatar Jun Lim Committed by Commit Bot

[compiler]Use Phi in Branch if control flow is known

This CL try to use a phi as a branch condition if the control flow from the
branch is known from previous conditions. This change will open up more branch
folding opportunities for later pass.

Change-Id: I26316ab3a68c2d58d0df53691981288a996d4ba1
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1674484
Commit-Queue: Tobias Tebbi <tebbi@chromium.org>
Reviewed-by: 's avatarTobias Tebbi <tebbi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#63434}
parent 04256154
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "src/compiler/branch-elimination.h" #include "src/compiler/branch-elimination.h"
#include "src/base/small-vector.h"
#include "src/compiler/js-graph.h" #include "src/compiler/js-graph.h"
#include "src/compiler/node-properties.h" #include "src/compiler/node-properties.h"
#include "src/compiler/simplified-operator.h" #include "src/compiler/simplified-operator.h"
...@@ -13,17 +14,17 @@ namespace internal { ...@@ -13,17 +14,17 @@ namespace internal {
namespace compiler { namespace compiler {
BranchElimination::BranchElimination(Editor* editor, JSGraph* js_graph, BranchElimination::BranchElimination(Editor* editor, JSGraph* js_graph,
Zone* zone) Zone* zone, Phase phase)
: AdvancedReducer(editor), : AdvancedReducer(editor),
jsgraph_(js_graph), jsgraph_(js_graph),
node_conditions_(js_graph->graph()->NodeCount(), zone), node_conditions_(js_graph->graph()->NodeCount(), zone),
reduced_(js_graph->graph()->NodeCount(), zone), reduced_(js_graph->graph()->NodeCount(), zone),
zone_(zone), zone_(zone),
dead_(js_graph->Dead()) {} dead_(js_graph->Dead()),
phase_(phase) {}
BranchElimination::~BranchElimination() = default; BranchElimination::~BranchElimination() = default;
Reduction BranchElimination::Reduce(Node* node) { Reduction BranchElimination::Reduce(Node* node) {
switch (node->opcode()) { switch (node->opcode()) {
case IrOpcode::kDead: case IrOpcode::kDead:
...@@ -52,6 +53,74 @@ Reduction BranchElimination::Reduce(Node* node) { ...@@ -52,6 +53,74 @@ Reduction BranchElimination::Reduce(Node* node) {
return NoChange(); return NoChange();
} }
void BranchElimination::SimplifyBranchCondition(Node* branch) {
// Try to use a phi as a branch condition if the control flow from the branch
// is known from previous branches. For example, in the graph below, the
// control flow of the second_branch is predictable because the first_branch
// use the same branch condition. In such case, create a new phi with constant
// inputs and let the second branch use the phi as its branch condition. From
// this transformation, more branch folding opportunities would be exposed to
// later passes through branch cloning in effect-control-linearizer.
//
// condition condition
// | \ |
// | first_branch first_branch
// | / \ / \
// | / \ / \
// |first_true first_false first_true first_false
// | \ / \ /
// | \ / \ /
// | first_merge ==> first_merge
// | | |
// second_branch 1 0 |
// / \ \ / |
// / \ phi |
// second_true second_false \ |
// second_branch
// / \
// / \
// second_true second_false
//
DCHECK_EQ(IrOpcode::kBranch, branch->opcode());
Node* merge = NodeProperties::GetControlInput(branch);
if (merge->opcode() != IrOpcode::kMerge) return;
Node* branch_condition = branch->InputAt(0);
Node* previous_branch;
bool condition_value;
Graph* graph = jsgraph()->graph();
base::SmallVector<Node*, 2> phi_inputs;
Node::Inputs inputs = merge->inputs();
int input_count = inputs.count();
for (int i = 0; i != input_count; ++i) {
Node* input = inputs[i];
ControlPathConditions from_input = node_conditions_.Get(input);
if (!from_input.LookupCondition(branch_condition, &previous_branch,
&condition_value))
return;
if (phase_ == kEARLY) {
phi_inputs.emplace_back(condition_value ? jsgraph()->TrueConstant()
: jsgraph()->FalseConstant());
} else {
phi_inputs.emplace_back(
condition_value
? graph->NewNode(jsgraph()->common()->Int32Constant(1))
: graph->NewNode(jsgraph()->common()->Int32Constant(0)));
}
}
phi_inputs.emplace_back(merge);
Node* new_phi = graph->NewNode(
common()->Phi(phase_ == kEARLY ? MachineRepresentation::kTagged
: MachineRepresentation::kWord32,
input_count),
input_count + 1, &phi_inputs.at(0));
// Replace the branch condition with the new phi.
NodeProperties::ReplaceValueInput(branch, new_phi, 0);
}
Reduction BranchElimination::ReduceBranch(Node* node) { Reduction BranchElimination::ReduceBranch(Node* node) {
Node* condition = node->InputAt(0); Node* condition = node->InputAt(0);
...@@ -87,6 +156,7 @@ Reduction BranchElimination::ReduceBranch(Node* node) { ...@@ -87,6 +156,7 @@ Reduction BranchElimination::ReduceBranch(Node* node) {
} }
return Replace(dead()); return Replace(dead());
} }
SimplifyBranchCondition(node);
return TakeConditionsFromFirstControl(node); return TakeConditionsFromFirstControl(node);
} }
...@@ -151,7 +221,6 @@ Reduction BranchElimination::ReduceIf(Node* node, bool is_true_branch) { ...@@ -151,7 +221,6 @@ Reduction BranchElimination::ReduceIf(Node* node, bool is_true_branch) {
return UpdateConditions(node, from_branch, condition, branch, is_true_branch); return UpdateConditions(node, from_branch, condition, branch, is_true_branch);
} }
Reduction BranchElimination::ReduceLoop(Node* node) { Reduction BranchElimination::ReduceLoop(Node* node) {
// Here we rely on having only reducible loops: // Here we rely on having only reducible loops:
// The loop entry edge always dominates the header, so we can just use // The loop entry edge always dominates the header, so we can just use
...@@ -159,7 +228,6 @@ Reduction BranchElimination::ReduceLoop(Node* node) { ...@@ -159,7 +228,6 @@ Reduction BranchElimination::ReduceLoop(Node* node) {
return TakeConditionsFromFirstControl(node); return TakeConditionsFromFirstControl(node);
} }
Reduction BranchElimination::ReduceMerge(Node* node) { Reduction BranchElimination::ReduceMerge(Node* node) {
// Shortcut for the case when we do not know anything about some // Shortcut for the case when we do not know anything about some
// input. // input.
...@@ -188,18 +256,15 @@ Reduction BranchElimination::ReduceMerge(Node* node) { ...@@ -188,18 +256,15 @@ Reduction BranchElimination::ReduceMerge(Node* node) {
return UpdateConditions(node, conditions); return UpdateConditions(node, conditions);
} }
Reduction BranchElimination::ReduceStart(Node* node) { Reduction BranchElimination::ReduceStart(Node* node) {
return UpdateConditions(node, {}); return UpdateConditions(node, {});
} }
Reduction BranchElimination::ReduceOtherControl(Node* node) { Reduction BranchElimination::ReduceOtherControl(Node* node) {
DCHECK_EQ(1, node->op()->ControlInputCount()); DCHECK_EQ(1, node->op()->ControlInputCount());
return TakeConditionsFromFirstControl(node); return TakeConditionsFromFirstControl(node);
} }
Reduction BranchElimination::TakeConditionsFromFirstControl(Node* node) { Reduction BranchElimination::TakeConditionsFromFirstControl(Node* node) {
// We just propagate the information from the control input (ideally, // We just propagate the information from the control input (ideally,
// we would only revisit control uses if there is change). // we would only revisit control uses if there is change).
......
...@@ -22,7 +22,12 @@ class JSGraph; ...@@ -22,7 +22,12 @@ class JSGraph;
class V8_EXPORT_PRIVATE BranchElimination final class V8_EXPORT_PRIVATE BranchElimination final
: public NON_EXPORTED_BASE(AdvancedReducer) { : public NON_EXPORTED_BASE(AdvancedReducer) {
public: public:
BranchElimination(Editor* editor, JSGraph* js_graph, Zone* zone); enum Phase {
kEARLY,
kLATE,
};
BranchElimination(Editor* editor, JSGraph* js_graph, Zone* zone,
Phase phase = kLATE);
~BranchElimination() final; ~BranchElimination() final;
const char* reducer_name() const override { return "BranchElimination"; } const char* reducer_name() const override { return "BranchElimination"; }
...@@ -62,6 +67,7 @@ class V8_EXPORT_PRIVATE BranchElimination final ...@@ -62,6 +67,7 @@ class V8_EXPORT_PRIVATE BranchElimination final
Reduction ReduceMerge(Node* node); Reduction ReduceMerge(Node* node);
Reduction ReduceStart(Node* node); Reduction ReduceStart(Node* node);
Reduction ReduceOtherControl(Node* node); Reduction ReduceOtherControl(Node* node);
void SimplifyBranchCondition(Node* branch);
Reduction TakeConditionsFromFirstControl(Node* node); Reduction TakeConditionsFromFirstControl(Node* node);
Reduction UpdateConditions(Node* node, ControlPathConditions conditions); Reduction UpdateConditions(Node* node, ControlPathConditions conditions);
...@@ -84,6 +90,7 @@ class V8_EXPORT_PRIVATE BranchElimination final ...@@ -84,6 +90,7 @@ class V8_EXPORT_PRIVATE BranchElimination final
NodeAuxData<bool> reduced_; NodeAuxData<bool> reduced_;
Zone* zone_; Zone* zone_;
Node* dead_; Node* dead_;
Phase phase_;
}; };
} // namespace compiler } // namespace compiler
......
...@@ -1626,7 +1626,8 @@ struct LoadEliminationPhase { ...@@ -1626,7 +1626,8 @@ struct LoadEliminationPhase {
&data->info()->tick_counter(), &data->info()->tick_counter(),
data->jsgraph()->Dead()); data->jsgraph()->Dead());
BranchElimination branch_condition_elimination(&graph_reducer, BranchElimination branch_condition_elimination(&graph_reducer,
data->jsgraph(), temp_zone); data->jsgraph(), temp_zone,
BranchElimination::kEARLY);
DeadCodeElimination dead_code_elimination(&graph_reducer, data->graph(), DeadCodeElimination dead_code_elimination(&graph_reducer, data->graph(),
data->common(), temp_zone); data->common(), temp_zone);
RedundancyElimination redundancy_elimination(&graph_reducer, temp_zone); RedundancyElimination redundancy_elimination(&graph_reducer, temp_zone);
......
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax
// Check that the branch elimination replace the redundant branch condition with
// a phi node, and then the branch is folded in EffectControlLinearizationPhase.
function foo(cond, v1, v2) {
cond = cond | 0;
var a = cond == 1 ? v1 : v2;
if(cond == 1) {
%TurbofanStaticAssert(a == v1);
} else {
%TurbofanStaticAssert(a == v2);
}
}
%PrepareFunctionForOptimization(foo);
foo(1, 10, 20); foo(2, 30, 40);
%OptimizeFunctionOnNextCall(foo);
foo(1, 10, 20); foo(2, 30, 40);
...@@ -39,7 +39,6 @@ class BranchEliminationTest : public GraphTest { ...@@ -39,7 +39,6 @@ class BranchEliminationTest : public GraphTest {
MachineOperatorBuilder machine_; MachineOperatorBuilder machine_;
}; };
TEST_F(BranchEliminationTest, NestedBranchSameTrue) { TEST_F(BranchEliminationTest, NestedBranchSameTrue) {
// { return (x ? (x ? 1 : 2) : 3; } // { return (x ? (x ? 1 : 2) : 3; }
// should be reduced to // should be reduced to
...@@ -80,7 +79,6 @@ TEST_F(BranchEliminationTest, NestedBranchSameTrue) { ...@@ -80,7 +79,6 @@ TEST_F(BranchEliminationTest, NestedBranchSameTrue) {
IsInt32Constant(2), IsMerge(outer_if_true, IsDead()))); IsInt32Constant(2), IsMerge(outer_if_true, IsDead())));
} }
TEST_F(BranchEliminationTest, NestedBranchSameFalse) { TEST_F(BranchEliminationTest, NestedBranchSameFalse) {
// { return (x ? 1 : (x ? 2 : 3); } // { return (x ? 1 : (x ? 2 : 3); }
// should be reduced to // should be reduced to
...@@ -122,10 +120,9 @@ TEST_F(BranchEliminationTest, NestedBranchSameFalse) { ...@@ -122,10 +120,9 @@ TEST_F(BranchEliminationTest, NestedBranchSameFalse) {
IsInt32Constant(3), IsMerge(IsDead(), outer_if_false))); IsInt32Constant(3), IsMerge(IsDead(), outer_if_false)));
} }
TEST_F(BranchEliminationTest, BranchAfterDiamond) { TEST_F(BranchEliminationTest, BranchAfterDiamond) {
// { var y = x ? 1 : 2; return y + x ? 3 : 4; } // { var y = x ? 1 : 2; return y + x ? 3 : 4; }
// should not be reduced. // second branch's condition should be replaced with a phi.
Node* condition = Parameter(0); Node* condition = Parameter(0);
Node* branch1 = Node* branch1 =
...@@ -136,7 +133,7 @@ TEST_F(BranchEliminationTest, BranchAfterDiamond) { ...@@ -136,7 +133,7 @@ TEST_F(BranchEliminationTest, BranchAfterDiamond) {
Node* phi1 = Node* phi1 =
graph()->NewNode(common()->Phi(MachineRepresentation::kWord32, 2), graph()->NewNode(common()->Phi(MachineRepresentation::kWord32, 2),
Int32Constant(1), Int32Constant(2), merge1); Int32Constant(1), Int32Constant(2), merge1);
// Second branch use the same condition.
Node* branch2 = graph()->NewNode(common()->Branch(), condition, merge1); Node* branch2 = graph()->NewNode(common()->Branch(), condition, merge1);
Node* if_true2 = graph()->NewNode(common()->IfTrue(), branch2); Node* if_true2 = graph()->NewNode(common()->IfTrue(), branch2);
Node* if_false2 = graph()->NewNode(common()->IfFalse(), branch2); Node* if_false2 = graph()->NewNode(common()->IfFalse(), branch2);
...@@ -145,7 +142,6 @@ TEST_F(BranchEliminationTest, BranchAfterDiamond) { ...@@ -145,7 +142,6 @@ TEST_F(BranchEliminationTest, BranchAfterDiamond) {
graph()->NewNode(common()->Phi(MachineRepresentation::kWord32, 2), graph()->NewNode(common()->Phi(MachineRepresentation::kWord32, 2),
Int32Constant(3), Int32Constant(4), merge1); Int32Constant(3), Int32Constant(4), merge1);
Node* add = graph()->NewNode(machine()->Int32Add(), phi1, phi2); Node* add = graph()->NewNode(machine()->Int32Add(), phi1, phi2);
Node* zero = graph()->NewNode(common()->Int32Constant(0)); Node* zero = graph()->NewNode(common()->Int32Constant(0));
Node* ret = Node* ret =
...@@ -154,13 +150,13 @@ TEST_F(BranchEliminationTest, BranchAfterDiamond) { ...@@ -154,13 +150,13 @@ TEST_F(BranchEliminationTest, BranchAfterDiamond) {
Reduce(); Reduce();
// Outer branch should not be rewritten, the inner branch condition should // The branch condition for branch2 should be a phi with constants.
// be true. EXPECT_THAT(branch2,
EXPECT_THAT(branch1, IsBranch(condition, graph()->start())); IsBranch(IsPhi(MachineRepresentation::kWord32, IsInt32Constant(1),
EXPECT_THAT(branch2, IsBranch(condition, merge1)); IsInt32Constant(0), merge1),
merge1));
} }
TEST_F(BranchEliminationTest, BranchInsideLoopSame) { TEST_F(BranchEliminationTest, BranchInsideLoopSame) {
// if (x) while (x) { return 2; } else { return 1; } // if (x) while (x) { return 2; } else { return 1; }
// should be rewritten to // should be rewritten to
...@@ -172,7 +168,6 @@ TEST_F(BranchEliminationTest, BranchInsideLoopSame) { ...@@ -172,7 +168,6 @@ TEST_F(BranchEliminationTest, BranchInsideLoopSame) {
graph()->NewNode(common()->Branch(), condition, graph()->start()); graph()->NewNode(common()->Branch(), condition, graph()->start());
Node* outer_if_true = graph()->NewNode(common()->IfTrue(), outer_branch); Node* outer_if_true = graph()->NewNode(common()->IfTrue(), outer_branch);
Node* loop = graph()->NewNode(common()->Loop(1), outer_if_true); Node* loop = graph()->NewNode(common()->Loop(1), outer_if_true);
Node* effect = Node* effect =
graph()->NewNode(common()->EffectPhi(1), graph()->start(), loop); graph()->NewNode(common()->EffectPhi(1), graph()->start(), loop);
......
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