OLD | NEW |
1 // Copyright 2014 the V8 project authors. All rights reserved. | 1 // Copyright 2014 the V8 project authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "src/compiler/graph-reducer.h" | 5 #include "src/compiler/graph-reducer.h" |
6 | 6 |
7 #include <functional> | 7 #include <functional> |
8 | 8 |
9 #include "src/compiler/graph-inl.h" | 9 #include "src/compiler/graph-inl.h" |
10 | 10 |
11 namespace v8 { | 11 namespace v8 { |
12 namespace internal { | 12 namespace internal { |
13 namespace compiler { | 13 namespace compiler { |
14 | 14 |
15 GraphReducer::GraphReducer(Graph* graph) | 15 enum class GraphReducer::State : uint8_t { |
16 : graph_(graph), reducers_(graph->zone()) {} | 16 kUnvisited, |
| 17 kRevisit, |
| 18 kOnStack, |
| 19 kVisited |
| 20 }; |
17 | 21 |
18 | 22 |
19 static bool NodeIdIsLessThan(const Node* node, NodeId id) { | 23 GraphReducer::GraphReducer(Graph* graph) : GraphReducer(graph, graph->zone()) {} |
20 return node->id() < id; | 24 |
| 25 |
| 26 GraphReducer::GraphReducer(Graph* graph, Zone* zone) |
| 27 : graph_(graph), |
| 28 reducers_(zone), |
| 29 revisit_(zone), |
| 30 stack_(zone), |
| 31 state_(zone) {} |
| 32 |
| 33 |
| 34 void GraphReducer::AddReducer(Reducer* reducer) { |
| 35 reducers_.push_back(reducer); |
21 } | 36 } |
22 | 37 |
23 | 38 |
24 void GraphReducer::ReduceNode(Node* node) { | 39 void GraphReducer::ReduceNode(Node* node) { |
25 static const unsigned kMaxAttempts = 16; | 40 DCHECK(stack_.empty()); |
26 bool reduce = true; | 41 DCHECK(revisit_.empty()); |
27 for (unsigned attempts = 0; attempts <= kMaxAttempts; ++attempts) { | 42 std::fill(state_.begin(), state_.end(), State::kUnvisited); |
28 if (!reduce) return; | 43 Push(node); |
29 reduce = false; // Assume we don't need to rerun any reducers. | 44 for (;;) { |
30 int before = graph_->NodeCount(); | 45 DCHECK(!stack_.empty() || |
31 for (ZoneVector<Reducer*>::iterator i = reducers_.begin(); | 46 std::find(state_.begin(), state_.end(), State::kOnStack) == |
32 i != reducers_.end(); ++i) { | 47 state_.end()); |
| 48 if (!stack_.empty()) { |
| 49 // Process the node on the top of the stack, potentially pushing more or |
| 50 // popping the node off the stack. |
| 51 ReduceTop(); |
| 52 } else if (!revisit_.empty()) { |
| 53 // If the stack becomes empty, revisit any nodes in the revisit queue. |
| 54 Node* const node = revisit_.top(); |
| 55 revisit_.pop(); |
| 56 if (state_[node->id()] == State::kRevisit) { |
| 57 // state can change while in queue. |
| 58 Push(node); |
| 59 } |
| 60 } else { |
| 61 break; |
| 62 } |
| 63 } |
| 64 DCHECK(std::find(state_.begin(), state_.end(), State::kOnStack) == |
| 65 state_.end()); |
| 66 DCHECK(revisit_.empty()); |
| 67 DCHECK(stack_.empty()); |
| 68 } |
| 69 |
| 70 |
| 71 void GraphReducer::ReduceGraph() { ReduceNode(graph()->end()); } |
| 72 |
| 73 |
| 74 Reduction GraphReducer::Reduce(Node* const node) { |
| 75 auto skip = reducers_.end(); |
| 76 for (auto i = reducers_.begin(); i != reducers_.end();) { |
| 77 if (i != skip) { |
33 Reduction reduction = (*i)->Reduce(node); | 78 Reduction reduction = (*i)->Reduce(node); |
34 Node* replacement = reduction.replacement(); | 79 if (!reduction.Changed()) { |
35 if (replacement == NULL) { | |
36 // No change from this reducer. | 80 // No change from this reducer. |
37 } else if (replacement == node) { | 81 } else if (reduction.replacement() == node) { |
38 // {replacement == node} represents an in-place reduction. | 82 // {replacement} == {node} represents an in-place reduction. Rerun |
39 // Rerun all the reducers for this node, as now there may be more | 83 // all the other reducers for this node, as now there may be more |
40 // opportunities for reduction. | 84 // opportunities for reduction. |
41 reduce = true; | 85 skip = i; |
42 break; | 86 i = reducers_.begin(); |
| 87 continue; |
43 } else { | 88 } else { |
44 if (node == graph_->start()) graph_->SetStart(replacement); | 89 // {node} was replaced by another node. |
45 if (node == graph_->end()) graph_->SetEnd(replacement); | 90 return reduction; |
46 // If {node} was replaced by an old node, unlink {node} and assume that | 91 } |
47 // {replacement} was already reduced and finish. | 92 } |
48 if (replacement->id() < before) { | 93 ++i; |
49 node->ReplaceUses(replacement); | 94 } |
50 node->Kill(); | 95 if (skip == reducers_.end()) { |
51 return; | 96 // No change from any reducer. |
52 } | 97 return Reducer::NoChange(); |
53 // Otherwise, {node} was replaced by a new node. Replace all old uses of | 98 } |
| 99 // At least one reducer did some in-place reduction. |
| 100 return Reducer::Changed(node); |
| 101 } |
| 102 |
| 103 |
| 104 void GraphReducer::ReduceTop() { |
| 105 Node* const node = Top(); |
| 106 if (node->IsDead()) return Pop(); // Node was killed while on stack. |
| 107 |
| 108 // Recurse on an input if necessary. |
| 109 for (auto const input : node->inputs()) { |
| 110 if (input != node && Recurse(input)) return; |
| 111 } |
| 112 |
| 113 // Remember the node count before reduction. |
| 114 const int node_count = graph()->NodeCount(); |
| 115 |
| 116 // All inputs should be visited or on stack. Apply reductions to node. |
| 117 Reduction reduction = Reduce(node); |
| 118 |
| 119 // After reducing the node, pop it off the stack. |
| 120 Pop(); |
| 121 |
| 122 // If there was a reduction, revisit the uses and reduce the replacement. |
| 123 if (reduction.Changed()) { |
| 124 for (Node* const use : node->uses()) { |
| 125 // Don't revisit this node if it refers to itself. |
| 126 if (use != node) Revisit(use); |
| 127 } |
| 128 Node* const replacement = reduction.replacement(); |
| 129 if (replacement != node) { |
| 130 if (node == graph()->start()) graph()->SetStart(replacement); |
| 131 if (node == graph()->end()) graph()->SetEnd(replacement); |
| 132 // If {node} was replaced by an old node, unlink {node} and assume that |
| 133 // {replacement} was already reduced and finish. |
| 134 if (replacement->id() < node_count) { |
| 135 node->ReplaceUses(replacement); |
| 136 node->Kill(); |
| 137 } else { |
| 138 // Otherwise {node} was replaced by a new node. Replace all old uses of |
54 // {node} with {replacement}. New nodes created by this reduction can | 139 // {node} with {replacement}. New nodes created by this reduction can |
55 // use {node}. | 140 // use {node}. |
56 node->ReplaceUsesIf( | 141 node->ReplaceUsesIf([node_count](Node* const node) { |
57 std::bind2nd(std::ptr_fun(&NodeIdIsLessThan), before), replacement); | 142 return node->id() < node_count; |
| 143 }, |
| 144 replacement); |
58 // Unlink {node} if it's no longer used. | 145 // Unlink {node} if it's no longer used. |
59 if (node->uses().empty()) { | 146 if (node->uses().empty()) { |
60 node->Kill(); | 147 node->Kill(); |
61 } | 148 } |
62 // Rerun all the reductions on the {replacement}. | 149 |
63 node = replacement; | 150 // If there was a replacement, reduce it after popping {node}. |
64 reduce = true; | 151 Recurse(replacement); |
65 break; | |
66 } | 152 } |
67 } | 153 } |
68 } | 154 } |
69 } | 155 } |
70 | 156 |
71 | 157 |
72 // A helper class to reuse the node traversal algorithm. | 158 void GraphReducer::Pop() { |
73 struct GraphReducerVisitor FINAL : public NullNodeVisitor { | 159 Node* const node = Top(); |
74 explicit GraphReducerVisitor(GraphReducer* reducer) : reducer_(reducer) {} | 160 state_[node->id()] = State::kVisited; |
75 void Post(Node* node) { reducer_->ReduceNode(node); } | 161 stack_.pop(); |
76 GraphReducer* reducer_; | |
77 }; | |
78 | |
79 | |
80 void GraphReducer::ReduceGraph() { | |
81 GraphReducerVisitor visitor(this); | |
82 // Perform a post-order reduction of all nodes starting from the end. | |
83 graph()->VisitNodeInputsFromEnd(&visitor); | |
84 } | 162 } |
85 | 163 |
86 | 164 |
87 // TODO(titzer): partial graph reductions. | 165 void GraphReducer::Push(Node* const node) { |
| 166 size_t const id = static_cast<size_t>(node->id()); |
| 167 if (id >= state_.size()) state_.resize(id + 1); |
| 168 DCHECK(id < state_.size()); |
| 169 DCHECK(state_[id] != State::kOnStack); |
| 170 state_[id] = State::kOnStack; |
| 171 stack_.push(node); |
| 172 } |
| 173 |
| 174 |
| 175 Node* GraphReducer::Top() const { |
| 176 DCHECK(!stack_.empty()); |
| 177 Node* const node = stack_.top(); |
| 178 size_t const id = static_cast<size_t>(node->id()); |
| 179 DCHECK(id < state_.size()); |
| 180 DCHECK(state_[id] == State::kOnStack); |
| 181 return node; |
| 182 } |
| 183 |
| 184 |
| 185 bool GraphReducer::Recurse(Node* const node) { |
| 186 size_t const id = static_cast<size_t>(node->id()); |
| 187 if (id < state_.size() && state_[id] > State::kRevisit) return false; |
| 188 Push(node); |
| 189 return true; |
| 190 } |
| 191 |
| 192 |
| 193 void GraphReducer::Revisit(Node* const node) { |
| 194 size_t const id = static_cast<size_t>(node->id()); |
| 195 if (id < state_.size() && state_[id] == State::kVisited) { |
| 196 state_[id] = State::kRevisit; |
| 197 revisit_.push(node); |
| 198 } |
| 199 } |
88 | 200 |
89 } // namespace compiler | 201 } // namespace compiler |
90 } // namespace internal | 202 } // namespace internal |
91 } // namespace v8 | 203 } // namespace v8 |
OLD | NEW |