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