Commit 2b59959c authored by titzer's avatar titzer Committed by Commit bot

[turbofan] Match selects in control reducer (configurable).

R=bmeurer@chromium.org
BUG=

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

Cr-Commit-Position: refs/heads/master@{#27658}
parent 2395eda3
...@@ -56,7 +56,8 @@ class ControlReducerImpl { ...@@ -56,7 +56,8 @@ class ControlReducerImpl {
common_(common), common_(common),
state_(jsgraph->graph()->NodeCount(), kUnvisited, zone_), state_(jsgraph->graph()->NodeCount(), kUnvisited, zone_),
stack_(zone_), stack_(zone_),
revisit_(zone_) {} revisit_(zone_),
max_phis_for_select_(0) {}
Zone* zone_; Zone* zone_;
JSGraph* jsgraph_; JSGraph* jsgraph_;
...@@ -64,6 +65,7 @@ class ControlReducerImpl { ...@@ -64,6 +65,7 @@ class ControlReducerImpl {
ZoneVector<VisitState> state_; ZoneVector<VisitState> state_;
ZoneDeque<Node*> stack_; ZoneDeque<Node*> stack_;
ZoneDeque<Node*> revisit_; ZoneDeque<Node*> revisit_;
int max_phis_for_select_;
void Reduce() { void Reduce() {
Push(graph()->end()); Push(graph()->end());
...@@ -536,7 +538,7 @@ class ControlReducerImpl { ...@@ -536,7 +538,7 @@ class ControlReducerImpl {
if (live == 0) return dead(); // no remaining inputs. if (live == 0) return dead(); // no remaining inputs.
// Gather phis and effect phis to be edited. // Gather phis and effect phis to be edited.
ZoneVector<Node*> phis(zone_); NodeVector phis(zone_);
for (Node* const use : node->uses()) { for (Node* const use : node->uses()) {
if (NodeProperties::IsPhi(use)) phis.push_back(use); if (NodeProperties::IsPhi(use)) phis.push_back(use);
} }
...@@ -564,25 +566,49 @@ class ControlReducerImpl { ...@@ -564,25 +566,49 @@ class ControlReducerImpl {
DCHECK_EQ(live, node->InputCount()); DCHECK_EQ(live, node->InputCount());
// Check if it's an unused diamond. // Try to remove dead diamonds or introduce selects.
if (live == 2 && phis.empty()) { if (live == 2 && CheckPhisForSelect(phis)) {
DiamondMatcher matcher(node); DiamondMatcher matcher(node);
if (matcher.Matched() && matcher.IfProjectionsAreOwned()) { if (matcher.Matched() && matcher.IfProjectionsAreOwned()) {
// It's a dead diamond, i.e. neither the IfTrue nor the IfFalse nodes // Dead diamond, i.e. neither the IfTrue nor the IfFalse nodes
// have uses except for the Merge and the Merge has no Phi or // have uses except for the Merge. Remove the branch if there
// EffectPhi uses, so replace the Merge with the control input of the // are no phis or replace phis with selects.
// diamond. Node* control = NodeProperties::GetControlInput(matcher.Branch());
TRACE(" DeadDiamond: #%d:Branch #%d:IfTrue #%d:IfFalse\n", if (phis.size() == 0) {
matcher.Branch()->id(), matcher.IfTrue()->id(), // No phis. Remove the branch altogether.
matcher.IfFalse()->id()); TRACE(" DeadDiamond: #%d:Branch #%d:IfTrue #%d:IfFalse\n",
// TODO(turbofan): replace single-phi diamonds with selects. matcher.Branch()->id(), matcher.IfTrue()->id(),
return NodeProperties::GetControlInput(matcher.Branch()); matcher.IfFalse()->id());
return control;
} else {
// A small number of phis. Replace with selects.
Node* cond = matcher.Branch()->InputAt(0);
for (Node* phi : phis) {
Node* select = graph()->NewNode(
common_->Select(OpParameter<MachineType>(phi),
BranchHintOf(matcher.Branch()->op())),
cond, matcher.TrueInputOf(phi), matcher.FalseInputOf(phi));
TRACE(" MatchSelect: #%d:Branch #%d:IfTrue #%d:IfFalse -> #%d\n",
matcher.Branch()->id(), matcher.IfTrue()->id(),
matcher.IfFalse()->id(), select->id());
ReplaceNode(phi, select);
}
return control;
}
} }
} }
return node; return node;
} }
bool CheckPhisForSelect(const NodeVector& phis) {
if (phis.size() > static_cast<size_t>(max_phis_for_select_)) return false;
for (Node* phi : phis) {
if (phi->opcode() != IrOpcode::kPhi) return false; // no EffectPhis.
}
return true;
}
// Reduce if projections if the branch has a constant input. // Reduce if projections if the branch has a constant input.
Node* ReduceIfProjection(Node* node, Decision decision) { Node* ReduceIfProjection(Node* node, Decision decision) {
Node* branch = node->InputAt(0); Node* branch = node->InputAt(0);
...@@ -638,8 +664,10 @@ class ControlReducerImpl { ...@@ -638,8 +664,10 @@ class ControlReducerImpl {
void ControlReducer::ReduceGraph(Zone* zone, JSGraph* jsgraph, void ControlReducer::ReduceGraph(Zone* zone, JSGraph* jsgraph,
CommonOperatorBuilder* common) { CommonOperatorBuilder* common,
int max_phis_for_select) {
ControlReducerImpl impl(zone, jsgraph, common); ControlReducerImpl impl(zone, jsgraph, common);
impl.max_phis_for_select_ = max_phis_for_select;
impl.Reduce(); impl.Reduce();
} }
...@@ -651,9 +679,11 @@ void ControlReducer::TrimGraph(Zone* zone, JSGraph* jsgraph) { ...@@ -651,9 +679,11 @@ void ControlReducer::TrimGraph(Zone* zone, JSGraph* jsgraph) {
Node* ControlReducer::ReduceMerge(JSGraph* jsgraph, Node* ControlReducer::ReduceMerge(JSGraph* jsgraph,
CommonOperatorBuilder* common, Node* node) { CommonOperatorBuilder* common, Node* node,
int max_phis_for_select) {
Zone zone; Zone zone;
ControlReducerImpl impl(&zone, jsgraph, common); ControlReducerImpl impl(&zone, jsgraph, common);
impl.max_phis_for_select_ = max_phis_for_select;
return impl.ReduceMerge(node); return impl.ReduceMerge(node);
} }
......
...@@ -23,14 +23,15 @@ class ControlReducer { ...@@ -23,14 +23,15 @@ class ControlReducer {
public: public:
// Perform branch folding and dead code elimination on the graph. // Perform branch folding and dead code elimination on the graph.
static void ReduceGraph(Zone* zone, JSGraph* graph, static void ReduceGraph(Zone* zone, JSGraph* graph,
CommonOperatorBuilder* builder); CommonOperatorBuilder* builder,
int max_phis_for_select = 0);
// Trim nodes in the graph that are not reachable from end. // Trim nodes in the graph that are not reachable from end.
static void TrimGraph(Zone* zone, JSGraph* graph); static void TrimGraph(Zone* zone, JSGraph* graph);
// Reduces a single merge node and attached phis. // Reduces a single merge node and attached phis.
static Node* ReduceMerge(JSGraph* graph, CommonOperatorBuilder* builder, static Node* ReduceMerge(JSGraph* graph, CommonOperatorBuilder* builder,
Node* node); Node* node, int max_phis_for_select = 0);
// Testing interface. // Testing interface.
static Node* ReducePhiForTesting(JSGraph* graph, static Node* ReducePhiForTesting(JSGraph* graph,
......
...@@ -570,6 +570,20 @@ struct DiamondMatcher : public NodeMatcher { ...@@ -570,6 +570,20 @@ struct DiamondMatcher : public NodeMatcher {
Node* IfFalse() const { return if_false_; } Node* IfFalse() const { return if_false_; }
Node* Merge() const { return node(); } Node* Merge() const { return node(); }
Node* TrueInputOf(Node* phi) const {
DCHECK(IrOpcode::IsPhiOpcode(phi->opcode()));
DCHECK_EQ(3, phi->InputCount());
DCHECK_EQ(Merge(), phi->InputAt(2));
return phi->InputAt(if_true_ == Merge()->InputAt(0) ? 0 : 1);
}
Node* FalseInputOf(Node* phi) const {
DCHECK(IrOpcode::IsPhiOpcode(phi->opcode()));
DCHECK_EQ(3, phi->InputCount());
DCHECK_EQ(Merge(), phi->InputAt(2));
return phi->InputAt(if_true_ == Merge()->InputAt(0) ? 1 : 0);
}
private: private:
Node* branch_; Node* branch_;
Node* if_true_; Node* if_true_;
......
...@@ -593,22 +593,23 @@ struct ChangeLoweringPhase { ...@@ -593,22 +593,23 @@ struct ChangeLoweringPhase {
}; };
struct ControlReductionPhase { struct EarlyControlReductionPhase {
static const char* phase_name() { return "early control reduction"; }
void Run(PipelineData* data, Zone* temp_zone) { void Run(PipelineData* data, Zone* temp_zone) {
SourcePositionTable::Scope pos(data->source_positions(), SourcePositionTable::Scope pos(data->source_positions(),
SourcePosition::Unknown()); SourcePosition::Unknown());
ControlReducer::ReduceGraph(temp_zone, data->jsgraph(), data->common()); ControlReducer::ReduceGraph(temp_zone, data->jsgraph(), data->common(), 1);
} }
}; };
struct EarlyControlReductionPhase : ControlReductionPhase { struct LateControlReductionPhase {
static const char* phase_name() { return "early control reduction"; }
};
struct LateControlReductionPhase : ControlReductionPhase {
static const char* phase_name() { return "late control reduction"; } static const char* phase_name() { return "late control reduction"; }
void Run(PipelineData* data, Zone* temp_zone) {
SourcePositionTable::Scope pos(data->source_positions(),
SourcePosition::Unknown());
ControlReducer::ReduceGraph(temp_zone, data->jsgraph(), data->common(), 0);
}
}; };
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
// found in the LICENSE file. // found in the LICENSE file.
#include "src/compiler/control-reducer.h" #include "src/compiler/control-reducer.h"
#include "src/compiler/diamond.h"
#include "src/compiler/graph-visualizer.h" #include "src/compiler/graph-visualizer.h"
#include "src/compiler/js-graph.h" #include "src/compiler/js-graph.h"
#include "src/compiler/js-operator.h" #include "src/compiler/js-operator.h"
...@@ -34,13 +35,14 @@ class ControlReducerTest : public TypedGraphTest { ...@@ -34,13 +35,14 @@ class ControlReducerTest : public TypedGraphTest {
JSOperatorBuilder javascript_; JSOperatorBuilder javascript_;
JSGraph jsgraph_; JSGraph jsgraph_;
void ReduceGraph() { void ReduceGraph(int max_phis_for_select = 0) {
if (FLAG_trace_turbo_graph) { if (FLAG_trace_turbo_graph) {
OFStream os(stdout); OFStream os(stdout);
os << "-- Graph before control reduction" << std::endl; os << "-- Graph before control reduction" << std::endl;
os << AsRPO(*graph()); os << AsRPO(*graph());
} }
ControlReducer::ReduceGraph(zone(), jsgraph(), common()); ControlReducer::ReduceGraph(zone(), jsgraph(), common(),
max_phis_for_select);
if (FLAG_trace_turbo_graph) { if (FLAG_trace_turbo_graph) {
OFStream os(stdout); OFStream os(stdout);
os << "-- Graph after control reduction" << std::endl; os << "-- Graph after control reduction" << std::endl;
...@@ -279,6 +281,75 @@ TEST_F(ControlReducerTest, RangeAsInputToBranch_true2) { ...@@ -279,6 +281,75 @@ TEST_F(ControlReducerTest, RangeAsInputToBranch_true2) {
} }
TEST_F(ControlReducerTest, SelectPhi) {
Node* p0 = Parameter(0);
const MachineType kType = kMachInt32;
Diamond d(graph(), common(), p0);
Node* phi =
d.Phi(kType, jsgraph()->Int32Constant(1), jsgraph()->Int32Constant(2));
Node* ret =
graph()->NewNode(common()->Return(), phi, graph()->start(), d.merge);
graph()->end()->ReplaceInput(0, ret);
ReduceGraph(1);
// Phi should be replaced with a select.
EXPECT_THAT(graph()->end(),
IsEnd(IsReturn(
IsSelect(kType, p0, IsInt32Constant(1), IsInt32Constant(2)),
graph()->start(), graph()->start())));
}
TEST_F(ControlReducerTest, SelectPhis_fail) {
Node* p0 = Parameter(0);
const MachineType kType = kMachInt32;
Diamond d(graph(), common(), p0);
Node* phi =
d.Phi(kType, jsgraph()->Int32Constant(1), jsgraph()->Int32Constant(2));
Node* phi2 =
d.Phi(kType, jsgraph()->Int32Constant(11), jsgraph()->Int32Constant(22));
USE(phi2);
Node* ret =
graph()->NewNode(common()->Return(), phi, graph()->start(), d.merge);
graph()->end()->ReplaceInput(0, ret);
ReduceGraph(1);
// Diamond should not be replaced with a select (too many phis).
EXPECT_THAT(ret, IsReturn(phi, graph()->start(), d.merge));
EXPECT_THAT(graph()->end(), IsEnd(ret));
}
TEST_F(ControlReducerTest, SelectTwoPhis) {
Node* p0 = Parameter(0);
const MachineType kType = kMachInt32;
Diamond d(graph(), common(), p0);
Node* phi1 =
d.Phi(kType, jsgraph()->Int32Constant(1), jsgraph()->Int32Constant(2));
Node* phi2 =
d.Phi(kType, jsgraph()->Int32Constant(2), jsgraph()->Int32Constant(3));
MachineOperatorBuilder machine(zone());
Node* add = graph()->NewNode(machine.Int32Add(), phi1, phi2);
Node* ret =
graph()->NewNode(common()->Return(), add, graph()->start(), d.merge);
graph()->end()->ReplaceInput(0, ret);
ReduceGraph(2);
// Phis should be replaced with two selects.
EXPECT_THAT(
ret,
IsReturn(IsInt32Add(
IsSelect(kType, p0, IsInt32Constant(1), IsInt32Constant(2)),
IsSelect(kType, p0, IsInt32Constant(2), IsInt32Constant(3))),
graph()->start(), graph()->start()));
EXPECT_THAT(graph()->end(), IsEnd(ret));
}
} // namespace compiler } // namespace compiler
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
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