Commit 7b33409b authored by bmeurer's avatar bmeurer Committed by Commit bot

[turbofan] Add support for advanced reducers.

An AdvancedReducer is basically a regular Reducer with an editor
that can perform graph editing operations beyond changing or
replacing the node that is currently being reduced. The GraphReducer
is the default implementation of the AdvancedReducer::Editor interface.

The ControlReducerImpl is now just an AdvancedReducer, which
temporarily requires a Finish method in the reducer to implement
the dead node trimming until we move that to the GraphReducer
(which in turn requires that all loops are connected to End).

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

Cr-Commit-Position: refs/heads/master@{#28251}
parent a5de69f4
This diff is collapsed.
......@@ -23,22 +23,18 @@ class ControlReducer {
public:
// Perform branch folding and dead code elimination on the graph.
static void ReduceGraph(Zone* zone, JSGraph* graph,
CommonOperatorBuilder* builder,
int max_phis_for_select = 0);
// Trim nodes in the graph that are not reachable from end.
static void TrimGraph(Zone* zone, JSGraph* graph);
// Reduces a single merge node and attached phis.
static Node* ReduceMerge(JSGraph* graph, CommonOperatorBuilder* builder,
Node* node, int max_phis_for_select = 0);
static Node* ReduceMerge(JSGraph* graph, Node* node,
int max_phis_for_select = 0);
// Testing interface.
static Node* ReducePhiForTesting(JSGraph* graph,
CommonOperatorBuilder* builder, Node* node);
static Node* ReduceIfNodeForTesting(JSGraph* graph,
CommonOperatorBuilder* builder,
Node* node);
static Node* ReducePhiForTesting(JSGraph* graph, Node* node);
static Node* ReduceIfNodeForTesting(JSGraph* graph, Node* node);
};
}
}
......
......@@ -3,6 +3,7 @@
// found in the LICENSE file.
#include <functional>
#include <limits>
#include "src/compiler/graph.h"
#include "src/compiler/graph-reducer.h"
......@@ -12,6 +13,9 @@ namespace v8 {
namespace internal {
namespace compiler {
bool Reducer::Finish() { return true; }
enum class GraphReducer::State : uint8_t {
kUnvisited,
kRevisit,
......@@ -28,6 +32,9 @@ GraphReducer::GraphReducer(Graph* graph, Zone* zone)
stack_(zone) {}
GraphReducer::~GraphReducer() {}
void GraphReducer::AddReducer(Reducer* reducer) {
reducers_.push_back(reducer);
}
......@@ -59,7 +66,23 @@ void GraphReducer::ReduceNode(Node* node) {
}
void GraphReducer::ReduceGraph() { ReduceNode(graph()->end()); }
void GraphReducer::ReduceGraph() {
for (;;) {
ReduceNode(graph()->end());
// TODO(turbofan): Remove this once the dead node trimming is in the
// GraphReducer.
bool done = true;
for (Reducer* const reducer : reducers_) {
if (!reducer->Finish()) {
done = false;
break;
}
}
if (done) break;
// Reset all marks on the graph in preparation to re-reduce the graph.
state_.Reset(graph());
}
}
Reduction GraphReducer::Reduce(Node* const node) {
......@@ -112,8 +135,8 @@ void GraphReducer::ReduceTop() {
if (input != node && Recurse(input)) return;
}
// Remember the node count before reduction.
const int node_count = graph()->NodeCount();
// Remember the max node id before reduction.
NodeId const max_id = graph()->NodeCount() - 1;
// All inputs should be visited or on stack. Apply reductions to node.
Reduction reduction = Reduce(node);
......@@ -135,38 +158,53 @@ void GraphReducer::ReduceTop() {
// After reducing the node, pop it off the stack.
Pop();
// Revisit all uses of the node.
for (Node* const use : node->uses()) {
// Don't revisit this node if it refers to itself.
if (use != node) Revisit(use);
}
// Check if we have a new replacement.
if (replacement != node) {
if (node == graph()->start()) graph()->SetStart(replacement);
if (node == graph()->end()) graph()->SetEnd(replacement);
// If {node} was replaced by an old node, unlink {node} and assume that
Replace(node, replacement, max_id);
} else {
// Revisit all uses of the node.
for (Node* const user : node->uses()) {
// Don't revisit this node if it refers to itself.
if (user != node) Revisit(user);
}
}
}
void GraphReducer::Replace(Node* node, Node* replacement) {
Replace(node, replacement, std::numeric_limits<NodeId>::max());
}
void GraphReducer::Replace(Node* node, Node* replacement, NodeId max_id) {
if (node == graph()->start()) graph()->SetStart(replacement);
if (node == graph()->end()) graph()->SetEnd(replacement);
if (replacement->id() <= max_id) {
// {replacement} is an old node, so unlink {node} and assume that
// {replacement} was already reduced and finish.
if (replacement->id() < node_count) {
node->ReplaceUses(replacement);
node->Kill();
} else {
// Otherwise {node} was replaced by a new node. Replace all old uses of
// {node} with {replacement}. New nodes created by this reduction can
// use {node}.
for (Edge edge : node->use_edges()) {
if (edge.from()->id() < node_count) {
edge.UpdateTo(replacement);
}
}
// Unlink {node} if it's no longer used.
if (node->uses().empty()) {
node->Kill();
for (Edge edge : node->use_edges()) {
Node* const user = edge.from();
edge.UpdateTo(replacement);
// Don't revisit this node if it refers to itself.
if (user != node) Revisit(user);
}
node->Kill();
} else {
// Replace all old uses of {node} with {replacement}, but allow new nodes
// created by this reduction to use {node}.
for (Edge edge : node->use_edges()) {
Node* const user = edge.from();
if (user->id() <= max_id) {
edge.UpdateTo(replacement);
// Don't revisit this node if it refers to itself.
if (user != node) Revisit(user);
}
// If there was a replacement, reduce it after popping {node}.
Recurse(replacement);
}
// Unlink {node} if it's no longer used.
if (node->uses().empty()) node->Kill();
// If there was a replacement, reduce it after popping {node}.
Recurse(replacement);
}
}
......
......@@ -17,13 +17,18 @@ class Graph;
class Node;
// NodeIds are identifying numbers for nodes that can be used to index auxiliary
// out-of-line data associated with each node.
typedef int32_t NodeId;
// Represents the result of trying to reduce a node in the graph.
class Reduction final {
public:
explicit Reduction(Node* replacement = NULL) : replacement_(replacement) {}
explicit Reduction(Node* replacement = nullptr) : replacement_(replacement) {}
Node* replacement() const { return replacement_; }
bool Changed() const { return replacement() != NULL; }
bool Changed() const { return replacement() != nullptr; }
private:
Node* replacement_;
......@@ -37,26 +42,66 @@ class Reduction final {
// phase.
class Reducer {
public:
Reducer() {}
virtual ~Reducer() {}
// Try to reduce a node if possible.
virtual Reduction Reduce(Node* node) = 0;
// Ask this reducer to finish operation, returns {true} if the reducer is
// done, while {false} indicates that the graph might need to be reduced
// again.
// TODO(turbofan): Remove this once the dead node trimming is in the
// GraphReducer.
virtual bool Finish();
// Helper functions for subclasses to produce reductions for a node.
static Reduction NoChange() { return Reduction(); }
static Reduction Replace(Node* node) { return Reduction(node); }
static Reduction Changed(Node* node) { return Reduction(node); }
};
// An advanced reducer can also edit the graphs by changing and replacing nodes
// other than the one currently being reduced.
class AdvancedReducer : public Reducer {
public:
// Observe the actions of this reducer.
class Editor {
public:
virtual ~Editor() {}
// Replace {node} with {replacement}.
virtual void Replace(Node* node, Node* replacement) = 0;
// Revisit the {node} again later.
virtual void Revisit(Node* node) = 0;
};
explicit AdvancedReducer(Editor* editor) : editor_(editor) {}
protected:
// Helper functions for subclasses to produce reductions for a node.
static Reduction Replace(Node* node) { return Reducer::Replace(node); }
// Helper functions for subclasses to edit the graph.
void Replace(Node* node, Node* replacement) {
DCHECK_NOT_NULL(editor_);
editor_->Replace(node, replacement);
}
void Revisit(Node* node) {
DCHECK_NOT_NULL(editor_);
editor_->Revisit(node);
}
private:
DISALLOW_COPY_AND_ASSIGN(Reducer);
Editor* const editor_;
};
// Performs an iterative reduction of a node graph.
class GraphReducer final {
class GraphReducer final : public AdvancedReducer::Editor {
public:
GraphReducer(Graph* graph, Zone* zone);
~GraphReducer() final;
Graph* graph() const { return graph_; }
......@@ -79,15 +124,22 @@ class GraphReducer final {
// Reduce the node on top of the stack.
void ReduceTop();
// Replace {node} with {replacement}.
void Replace(Node* node, Node* replacement) final;
// Replace all uses of {node} with {replacement} if the id of {replacement} is
// less than or equal to {max_id}. Otherwise, replace all uses of {node} whose
// id is less than or equal to {max_id} with the {replacement}.
void Replace(Node* node, Node* replacement, NodeId max_id);
// Node stack operations.
void Pop();
void Push(Node* node);
// Revisit queue operations.
bool Recurse(Node* node);
void Revisit(Node* node);
void Revisit(Node* node) final;
Graph* graph_;
Graph* const graph_;
NodeMarker<State> state_;
ZoneVector<Reducer*> reducers_;
ZoneStack<Node*> revisit_;
......
......@@ -13,8 +13,8 @@ namespace compiler {
NodeMarkerBase::NodeMarkerBase(Graph* graph, uint32_t num_states)
: mark_min_(graph->mark_max_), mark_max_(graph->mark_max_ += num_states) {
DCHECK(num_states > 0); // user error!
DCHECK(mark_max_ > mark_min_); // check for wraparound.
DCHECK_NE(0u, num_states); // user error!
DCHECK_LT(mark_min_, mark_max_); // check for wraparound.
}
......@@ -35,6 +35,14 @@ void NodeMarkerBase::Set(Node* node, Mark mark) {
node->set_mark(mark + mark_min_);
}
void NodeMarkerBase::Reset(Graph* graph) {
uint32_t const num_states = mark_max_ - mark_min_;
mark_min_ = graph->mark_max_;
mark_max_ = graph->mark_max_ += num_states;
DCHECK_LT(mark_min_, mark_max_); // check for wraparound.
}
} // namespace compiler
} // namespace internal
} // namespace v8
......@@ -29,6 +29,7 @@ class NodeMarkerBase {
Mark Get(Node* node);
void Set(Node* node, Mark mark);
void Reset(Graph* graph);
private:
Mark mark_min_;
......
......@@ -339,12 +339,12 @@ void OsrHelper::Deconstruct(JSGraph* jsgraph, CommonOperatorBuilder* common,
// but we need to avoid that because the osr_loop is reachable through
// the second input, so reduce it and its phis manually.
osr_loop->ReplaceInput(0, dead);
Node* node = ControlReducer::ReduceMerge(jsgraph, common, osr_loop);
Node* node = ControlReducer::ReduceMerge(jsgraph, osr_loop);
if (node != osr_loop) osr_loop->ReplaceUses(node);
// Run the normal control reduction, which naturally trims away the dead
// parts of the graph.
ControlReducer::ReduceGraph(tmp_zone, jsgraph, common);
ControlReducer::ReduceGraph(tmp_zone, jsgraph);
}
......
......@@ -647,8 +647,7 @@ struct EarlyControlReductionPhase {
void Run(PipelineData* data, Zone* temp_zone) {
SourcePositionTable::Scope pos(data->source_positions(),
SourcePosition::Unknown());
// TODO(turbofan): enable select matching in early control reduction.
ControlReducer::ReduceGraph(temp_zone, data->jsgraph(), data->common(), 0);
ControlReducer::ReduceGraph(temp_zone, data->jsgraph(), 0);
}
};
......@@ -658,7 +657,7 @@ struct LateControlReductionPhase {
void Run(PipelineData* data, Zone* temp_zone) {
SourcePositionTable::Scope pos(data->source_positions(),
SourcePosition::Unknown());
ControlReducer::ReduceGraph(temp_zone, data->jsgraph(), data->common(), 0);
ControlReducer::ReduceGraph(temp_zone, data->jsgraph(), 0);
}
};
......
......@@ -41,8 +41,7 @@ class ControlReducerTest : public TypedGraphTest {
os << "-- Graph before control reduction" << std::endl;
os << AsRPO(*graph());
}
ControlReducer::ReduceGraph(zone(), jsgraph(), common(),
max_phis_for_select);
ControlReducer::ReduceGraph(zone(), jsgraph(), max_phis_for_select);
if (FLAG_trace_turbo_graph) {
OFStream os(stdout);
os << "-- Graph after control reduction" << std::endl;
......
......@@ -3,11 +3,10 @@
// found in the LICENSE file.
#include "src/compiler/graph.h"
#include "src/compiler/graph-reducer.h"
#include "src/compiler/node.h"
#include "src/compiler/operator.h"
#include "test/unittests/compiler/graph-reducer-unittest.h"
#include "test/unittests/test-utils.h"
#include "testing/gmock/include/gmock/gmock.h"
using testing::_;
using testing::DefaultValue;
......@@ -55,9 +54,9 @@ struct MockReducer : public Reducer {
// Replaces all "A" operators with "B" operators without creating new nodes.
class InPlaceABReducer : public Reducer {
class InPlaceABReducer final : public Reducer {
public:
virtual Reduction Reduce(Node* node) {
Reduction Reduce(Node* node) final {
switch (node->op()->opcode()) {
case kOpcodeA0:
EXPECT_EQ(0, node->InputCount());
......@@ -78,10 +77,11 @@ class InPlaceABReducer : public Reducer {
// Replaces all "A" operators with "B" operators by allocating new nodes.
class NewABReducer : public Reducer {
class NewABReducer final : public Reducer {
public:
explicit NewABReducer(Graph* graph) : graph_(graph) {}
virtual Reduction Reduce(Node* node) {
Reduction Reduce(Node* node) final {
switch (node->op()->opcode()) {
case kOpcodeA0:
EXPECT_EQ(0, node->InputCount());
......@@ -96,7 +96,9 @@ class NewABReducer : public Reducer {
}
return NoChange();
}
Graph* graph_;
private:
Graph* const graph_;
};
......@@ -104,7 +106,8 @@ class NewABReducer : public Reducer {
class A0Wrapper final : public Reducer {
public:
explicit A0Wrapper(Graph* graph) : graph_(graph) {}
virtual Reduction Reduce(Node* node) override {
Reduction Reduce(Node* node) final {
switch (node->op()->opcode()) {
case kOpcodeA0:
EXPECT_EQ(0, node->InputCount());
......@@ -112,7 +115,9 @@ class A0Wrapper final : public Reducer {
}
return NoChange();
}
Graph* graph_;
private:
Graph* const graph_;
};
......@@ -120,7 +125,8 @@ class A0Wrapper final : public Reducer {
class B0Wrapper final : public Reducer {
public:
explicit B0Wrapper(Graph* graph) : graph_(graph) {}
virtual Reduction Reduce(Node* node) override {
Reduction Reduce(Node* node) final {
switch (node->op()->opcode()) {
case kOpcodeB0:
EXPECT_EQ(0, node->InputCount());
......@@ -128,13 +134,16 @@ class B0Wrapper final : public Reducer {
}
return NoChange();
}
Graph* graph_;
private:
Graph* const graph_;
};
// Replaces all "kOpA1" nodes with the first input.
class A1Forwarder : public Reducer {
virtual Reduction Reduce(Node* node) {
class A1Forwarder final : public Reducer {
public:
Reduction Reduce(Node* node) final {
switch (node->op()->opcode()) {
case kOpcodeA1:
EXPECT_EQ(1, node->InputCount());
......@@ -146,8 +155,9 @@ class A1Forwarder : public Reducer {
// Replaces all "kOpB1" nodes with the first input.
class B1Forwarder : public Reducer {
virtual Reduction Reduce(Node* node) {
class B1Forwarder final : public Reducer {
public:
Reduction Reduce(Node* node) final {
switch (node->op()->opcode()) {
case kOpcodeB1:
EXPECT_EQ(1, node->InputCount());
......@@ -159,9 +169,9 @@ class B1Forwarder : public Reducer {
// Replaces all "B" operators with "C" operators without creating new nodes.
class InPlaceBCReducer : public Reducer {
class InPlaceBCReducer final : public Reducer {
public:
virtual Reduction Reduce(Node* node) {
Reduction Reduce(Node* node) final {
switch (node->op()->opcode()) {
case kOpcodeB0:
EXPECT_EQ(0, node->InputCount());
......@@ -182,8 +192,9 @@ class InPlaceBCReducer : public Reducer {
// Swaps the inputs to "kOp2A" and "kOp2B" nodes based on ids.
class AB2Sorter : public Reducer {
virtual Reduction Reduce(Node* node) {
class AB2Sorter final : public Reducer {
public:
Reduction Reduce(Node* node) final {
switch (node->op()->opcode()) {
case kOpcodeA2:
case kOpcodeB2:
......@@ -200,10 +211,59 @@ class AB2Sorter : public Reducer {
}
};
} // namespace
class AdvancedReducerTest : public TestWithZone {
public:
AdvancedReducerTest() : graph_(zone()) {}
protected:
Graph* graph() { return &graph_; }
private:
Graph graph_;
};
TEST_F(AdvancedReducerTest, Replace) {
struct DummyReducer final : public AdvancedReducer {
explicit DummyReducer(Editor* editor) : AdvancedReducer(editor) {}
Reduction Reduce(Node* node) final {
Replace(node, node);
return NoChange();
}
};
StrictMock<MockAdvancedReducerEditor> e;
DummyReducer r(&e);
Node* node0 = graph()->NewNode(&kOpA0);
Node* node1 = graph()->NewNode(&kOpA1, node0);
EXPECT_CALL(e, Replace(node0, node0));
EXPECT_CALL(e, Replace(node1, node1));
EXPECT_FALSE(r.Reduce(node0).Changed());
EXPECT_FALSE(r.Reduce(node1).Changed());
}
TEST_F(AdvancedReducerTest, Revisit) {
struct DummyReducer final : public AdvancedReducer {
explicit DummyReducer(Editor* editor) : AdvancedReducer(editor) {}
Reduction Reduce(Node* node) final {
Revisit(node);
return NoChange();
}
};
StrictMock<MockAdvancedReducerEditor> e;
DummyReducer r(&e);
Node* node0 = graph()->NewNode(&kOpA0);
Node* node1 = graph()->NewNode(&kOpA1, node0);
EXPECT_CALL(e, Revisit(node0));
EXPECT_CALL(e, Revisit(node1));
EXPECT_FALSE(r.Reduce(node0).Changed());
EXPECT_FALSE(r.Reduce(node1).Changed());
}
class GraphReducerTest : public TestWithZone {
public:
GraphReducerTest() : graph_(zone()) {}
......@@ -573,6 +633,8 @@ TEST_F(GraphReducerTest, Sorter1) {
}
namespace {
// Generate a node graph with the given permutations.
void GenDAG(Graph* graph, int* p3, int* p2, int* p1) {
Node* level4 = graph->NewNode(&kOpA0);
......@@ -591,6 +653,8 @@ void GenDAG(Graph* graph, int* p3, int* p2, int* p1) {
graph->SetEnd(end);
}
} // namespace
TEST_F(GraphReducerTest, SortForwardReduce) {
// Tests combined reductions on a series of DAGs.
......@@ -667,7 +731,6 @@ TEST_F(GraphReducerTest, Order) {
}
}
} // namespace compiler
} // namespace internal
} // namespace v8
// Copyright 2015 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.
#ifndef V8_UNITTESTS_COMPILER_GRAPH_REDUCER_UNITTEST_H_
#define V8_UNITTESTS_COMPILER_GRAPH_REDUCER_UNITTEST_H_
#include "src/compiler/graph-reducer.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace v8 {
namespace internal {
namespace compiler {
struct MockAdvancedReducerEditor : public AdvancedReducer::Editor {
MOCK_METHOD1(Revisit, void(Node*));
MOCK_METHOD2(Replace, void(Node*, Node*));
};
} // namespace compiler
} // namespace internal
} // namespace v8
#endif // V8_UNITTESTS_COMPILER_GRAPH_REDUCER_UNITTEST_H_
......@@ -47,6 +47,7 @@
'compiler/control-reducer-unittest.cc',
'compiler/diamond-unittest.cc',
'compiler/graph-reducer-unittest.cc',
'compiler/graph-reducer-unittest.h',
'compiler/graph-unittest.cc',
'compiler/graph-unittest.h',
'compiler/instruction-selector-unittest.cc',
......
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