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/common-operator.h" | 5 #include "src/compiler/common-operator.h" |
6 #include "src/compiler/control-reducer.h" | 6 #include "src/compiler/control-reducer.h" |
7 #include "src/compiler/graph.h" | 7 #include "src/compiler/graph.h" |
8 #include "src/compiler/js-graph.h" | 8 #include "src/compiler/js-graph.h" |
9 #include "src/compiler/node-matchers.h" | 9 #include "src/compiler/node-matchers.h" |
10 #include "src/compiler/node-properties-inl.h" | 10 #include "src/compiler/node-properties-inl.h" |
11 #include "src/zone-containers.h" | 11 #include "src/zone-containers.h" |
12 | 12 |
13 namespace v8 { | 13 namespace v8 { |
14 namespace internal { | 14 namespace internal { |
15 namespace compiler { | 15 namespace compiler { |
16 | 16 |
17 enum VisitState { kUnvisited = 0, kOnStack = 1, kRevisit = 2, kVisited = 3 }; | 17 enum VisitState { kUnvisited = 0, kOnStack = 1, kRevisit = 2, kVisited = 3 }; |
18 enum Reachability { kFromStart = 8 }; | |
19 enum Decision { kFalse, kUnknown, kTrue }; | 18 enum Decision { kFalse, kUnknown, kTrue }; |
20 | 19 |
| 20 class ReachabilityMarker : public NodeMarker<uint8_t> { |
| 21 public: |
| 22 explicit ReachabilityMarker(Graph* graph) : NodeMarker<uint8_t>(graph, 8) {} |
| 23 bool SetReachableFromEnd(Node* node) { |
| 24 uint8_t before = Get(node); |
| 25 Set(node, before | kFromEnd); |
| 26 return before & kFromEnd; |
| 27 } |
| 28 bool IsReachableFromEnd(Node* node) { return Get(node) & kFromEnd; } |
| 29 bool SetReachableFromStart(Node* node) { |
| 30 uint8_t before = Get(node); |
| 31 Set(node, before | kFromStart); |
| 32 return before & kFromStart; |
| 33 } |
| 34 bool IsReachableFromStart(Node* node) { return Get(node) & kFromStart; } |
| 35 void Push(Node* node) { Set(node, Get(node) | kFwStack); } |
| 36 void Pop(Node* node) { Set(node, Get(node) & ~kFwStack); } |
| 37 bool IsOnStack(Node* node) { return Get(node) & kFwStack; } |
| 38 |
| 39 private: |
| 40 enum Bit { kFromEnd = 1, kFromStart = 2, kFwStack = 4 }; |
| 41 }; |
| 42 |
| 43 |
21 #define TRACE(x) \ | 44 #define TRACE(x) \ |
22 if (FLAG_trace_turbo_reduction) PrintF x | 45 if (FLAG_trace_turbo_reduction) PrintF x |
23 | 46 |
24 class ControlReducerImpl { | 47 class ControlReducerImpl { |
25 public: | 48 public: |
26 ControlReducerImpl(Zone* zone, JSGraph* jsgraph, | 49 ControlReducerImpl(Zone* zone, JSGraph* jsgraph, |
27 CommonOperatorBuilder* common) | 50 CommonOperatorBuilder* common) |
28 : zone_(zone), | 51 : zone_(zone), |
29 jsgraph_(jsgraph), | 52 jsgraph_(jsgraph), |
30 common_(common), | 53 common_(common), |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
65 return false; | 88 return false; |
66 } | 89 } |
67 | 90 |
68 // Repair the graph after the possible creation of non-terminating or dead | 91 // Repair the graph after the possible creation of non-terminating or dead |
69 // loops. Removing dead loops can produce more opportunities for reduction. | 92 // loops. Removing dead loops can produce more opportunities for reduction. |
70 bool RepairAndRemoveLoops() { | 93 bool RepairAndRemoveLoops() { |
71 // TODO(turbofan): we can skip this if the graph has no loops, but | 94 // TODO(turbofan): we can skip this if the graph has no loops, but |
72 // we have to be careful about proper loop detection during reduction. | 95 // we have to be careful about proper loop detection during reduction. |
73 | 96 |
74 // Gather all nodes backwards-reachable from end (through inputs). | 97 // Gather all nodes backwards-reachable from end (through inputs). |
75 state_.assign(graph()->NodeCount(), kUnvisited); | 98 ReachabilityMarker marked(graph()); |
76 NodeVector nodes(zone_); | 99 NodeVector nodes(zone_); |
77 AddNodesReachableFromEnd(nodes); | 100 AddNodesReachableFromEnd(marked, nodes); |
78 | 101 |
79 // Walk forward through control nodes, looking for back edges to nodes | 102 // Walk forward through control nodes, looking for back edges to nodes |
80 // that are not connected to end. Those are non-terminating loops (NTLs). | 103 // that are not connected to end. Those are non-terminating loops (NTLs). |
81 Node* start = graph()->start(); | 104 Node* start = graph()->start(); |
82 ZoneVector<byte> fw_reachability(graph()->NodeCount(), 0, zone_); | 105 marked.Push(start); |
83 fw_reachability[start->id()] = kFromStart | kOnStack; | 106 marked.SetReachableFromStart(start); |
84 | 107 |
85 // We use a stack of (Node, UseIter) pairs to avoid O(n^2) traversal. | 108 // We use a stack of (Node, UseIter) pairs to avoid O(n^2) traversal. |
86 typedef std::pair<Node*, UseIter> FwIter; | 109 typedef std::pair<Node*, UseIter> FwIter; |
87 ZoneDeque<FwIter> fw_stack(zone_); | 110 ZoneDeque<FwIter> fw_stack(zone_); |
88 fw_stack.push_back(FwIter(start, start->uses().begin())); | 111 fw_stack.push_back(FwIter(start, start->uses().begin())); |
89 | 112 |
90 while (!fw_stack.empty()) { | 113 while (!fw_stack.empty()) { |
91 Node* node = fw_stack.back().first; | 114 Node* node = fw_stack.back().first; |
92 TRACE(("ControlFw: #%d:%s\n", node->id(), node->op()->mnemonic())); | 115 TRACE(("ControlFw: #%d:%s\n", node->id(), node->op()->mnemonic())); |
93 bool pop = true; | 116 bool pop = true; |
94 while (fw_stack.back().second != node->uses().end()) { | 117 while (fw_stack.back().second != node->uses().end()) { |
95 Node* succ = *(fw_stack.back().second); | 118 Node* succ = *(fw_stack.back().second); |
96 byte reach = fw_reachability[succ->id()]; | 119 if (marked.IsOnStack(succ) && !marked.IsReachableFromEnd(succ)) { |
97 if ((reach & kOnStack) != 0 && state_[succ->id()] != kVisited) { | |
98 // {succ} is on stack and not reachable from end. | 120 // {succ} is on stack and not reachable from end. |
99 ConnectNTL(nodes, succ); | 121 Node* added = ConnectNTL(succ); |
100 fw_reachability.resize(graph()->NodeCount(), 0); | 122 nodes.push_back(added); |
| 123 marked.SetReachableFromEnd(added); |
| 124 AddBackwardsReachableNodes(marked, nodes, nodes.size() - 1); |
| 125 |
101 // The use list of {succ} might have changed. | 126 // The use list of {succ} might have changed. |
102 fw_stack[fw_stack.size() - 1] = FwIter(succ, succ->uses().begin()); | 127 fw_stack[fw_stack.size() - 1] = FwIter(succ, succ->uses().begin()); |
103 pop = false; // restart traversing successors of this node. | 128 pop = false; // restart traversing successors of this node. |
104 break; | 129 break; |
105 } | 130 } |
106 if ((reach & kFromStart) == 0 && | 131 if (IrOpcode::IsControlOpcode(succ->opcode()) && |
107 IrOpcode::IsControlOpcode(succ->opcode())) { | 132 !marked.IsReachableFromStart(succ)) { |
108 // {succ} is a control node and not yet reached from start. | 133 // {succ} is a control node and not yet reached from start. |
109 fw_reachability[succ->id()] |= kFromStart | kOnStack; | 134 marked.Push(succ); |
| 135 marked.SetReachableFromStart(succ); |
110 fw_stack.push_back(FwIter(succ, succ->uses().begin())); | 136 fw_stack.push_back(FwIter(succ, succ->uses().begin())); |
111 pop = false; // "recurse" into successor control node. | 137 pop = false; // "recurse" into successor control node. |
112 break; | 138 break; |
113 } | 139 } |
114 ++fw_stack.back().second; | 140 ++fw_stack.back().second; |
115 } | 141 } |
116 if (pop) { | 142 if (pop) { |
117 fw_reachability[node->id()] &= ~kOnStack; | 143 marked.Pop(node); |
118 fw_stack.pop_back(); | 144 fw_stack.pop_back(); |
119 } | 145 } |
120 } | 146 } |
121 | 147 |
122 // Trim references from dead nodes to live nodes first. | 148 // Trim references from dead nodes to live nodes first. |
123 jsgraph_->GetCachedNodes(&nodes); | 149 jsgraph_->GetCachedNodes(&nodes); |
124 TrimNodes(nodes); | 150 TrimNodes(marked, nodes); |
125 | 151 |
126 // Any control nodes not reachable from start are dead, even loops. | 152 // Any control nodes not reachable from start are dead, even loops. |
127 for (size_t i = 0; i < nodes.size(); i++) { | 153 for (size_t i = 0; i < nodes.size(); i++) { |
128 Node* node = nodes[i]; | 154 Node* node = nodes[i]; |
129 byte reach = fw_reachability[node->id()]; | 155 if (IrOpcode::IsControlOpcode(node->opcode()) && |
130 if ((reach & kFromStart) == 0 && | 156 !marked.IsReachableFromStart(node)) { |
131 IrOpcode::IsControlOpcode(node->opcode())) { | |
132 ReplaceNode(node, dead()); // uses will be added to revisit queue. | 157 ReplaceNode(node, dead()); // uses will be added to revisit queue. |
133 } | 158 } |
134 } | 159 } |
135 return TryRevisit(); // try to push a node onto the stack. | 160 return TryRevisit(); // try to push a node onto the stack. |
136 } | 161 } |
137 | 162 |
138 // Connect {loop}, the header of a non-terminating loop, to the end node. | 163 // Connect {loop}, the header of a non-terminating loop, to the end node. |
139 void ConnectNTL(NodeVector& nodes, Node* loop) { | 164 Node* ConnectNTL(Node* loop) { |
140 TRACE(("ConnectNTL: #%d:%s\n", loop->id(), loop->op()->mnemonic())); | 165 TRACE(("ConnectNTL: #%d:%s\n", loop->id(), loop->op()->mnemonic())); |
141 | 166 |
142 if (loop->opcode() != IrOpcode::kTerminate) { | 167 if (loop->opcode() != IrOpcode::kTerminate) { |
143 // Insert a {Terminate} node if the loop has effects. | 168 // Insert a {Terminate} node if the loop has effects. |
144 ZoneDeque<Node*> effects(zone_); | 169 ZoneDeque<Node*> effects(zone_); |
145 for (Node* const use : loop->uses()) { | 170 for (Node* const use : loop->uses()) { |
146 if (use->opcode() == IrOpcode::kEffectPhi) effects.push_back(use); | 171 if (use->opcode() == IrOpcode::kEffectPhi) effects.push_back(use); |
147 } | 172 } |
148 int count = static_cast<int>(effects.size()); | 173 int count = static_cast<int>(effects.size()); |
149 if (count > 0) { | 174 if (count > 0) { |
(...skipping 16 matching lines...) Expand all Loading... |
166 } else if (merge->opcode() != IrOpcode::kMerge) { | 191 } else if (merge->opcode() != IrOpcode::kMerge) { |
167 // Introduce a final merge node for {end->InputAt(0)} and {loop}. | 192 // Introduce a final merge node for {end->InputAt(0)} and {loop}. |
168 merge = graph()->NewNode(common_->Merge(2), merge, loop); | 193 merge = graph()->NewNode(common_->Merge(2), merge, loop); |
169 end->ReplaceInput(0, merge); | 194 end->ReplaceInput(0, merge); |
170 to_add = merge; | 195 to_add = merge; |
171 } else { | 196 } else { |
172 // Append a new input to the final merge at the end. | 197 // Append a new input to the final merge at the end. |
173 merge->AppendInput(graph()->zone(), loop); | 198 merge->AppendInput(graph()->zone(), loop); |
174 merge->set_op(common_->Merge(merge->InputCount())); | 199 merge->set_op(common_->Merge(merge->InputCount())); |
175 } | 200 } |
176 nodes.push_back(to_add); | 201 return to_add; |
177 state_.resize(graph()->NodeCount(), kUnvisited); | |
178 state_[to_add->id()] = kVisited; | |
179 AddBackwardsReachableNodes(nodes, nodes.size() - 1); | |
180 } | 202 } |
181 | 203 |
182 void AddNodesReachableFromEnd(NodeVector& nodes) { | 204 void AddNodesReachableFromEnd(ReachabilityMarker& marked, NodeVector& nodes) { |
183 Node* end = graph()->end(); | 205 Node* end = graph()->end(); |
184 state_[end->id()] = kVisited; | 206 marked.SetReachableFromEnd(end); |
185 if (!end->IsDead()) { | 207 if (!end->IsDead()) { |
186 nodes.push_back(end); | 208 nodes.push_back(end); |
187 AddBackwardsReachableNodes(nodes, nodes.size() - 1); | 209 AddBackwardsReachableNodes(marked, nodes, nodes.size() - 1); |
188 } | 210 } |
189 } | 211 } |
190 | 212 |
191 void AddBackwardsReachableNodes(NodeVector& nodes, size_t cursor) { | 213 void AddBackwardsReachableNodes(ReachabilityMarker& marked, NodeVector& nodes, |
| 214 size_t cursor) { |
192 while (cursor < nodes.size()) { | 215 while (cursor < nodes.size()) { |
193 Node* node = nodes[cursor++]; | 216 Node* node = nodes[cursor++]; |
194 for (Node* const input : node->inputs()) { | 217 for (Node* const input : node->inputs()) { |
195 if (state_[input->id()] != kVisited) { | 218 if (!marked.SetReachableFromEnd(input)) { |
196 state_[input->id()] = kVisited; | |
197 nodes.push_back(input); | 219 nodes.push_back(input); |
198 } | 220 } |
199 } | 221 } |
200 } | 222 } |
201 } | 223 } |
202 | 224 |
203 void Trim() { | 225 void Trim() { |
204 // Gather all nodes backwards-reachable from end through inputs. | 226 // Gather all nodes backwards-reachable from end through inputs. |
205 state_.assign(graph()->NodeCount(), kUnvisited); | 227 ReachabilityMarker marked(graph()); |
206 NodeVector nodes(zone_); | 228 NodeVector nodes(zone_); |
207 AddNodesReachableFromEnd(nodes); | 229 AddNodesReachableFromEnd(marked, nodes); |
208 | 230 |
209 // Process cached nodes in the JSGraph too. | 231 // Process cached nodes in the JSGraph too. |
210 jsgraph_->GetCachedNodes(&nodes); | 232 jsgraph_->GetCachedNodes(&nodes); |
211 TrimNodes(nodes); | 233 TrimNodes(marked, nodes); |
212 } | 234 } |
213 | 235 |
214 void TrimNodes(NodeVector& nodes) { | 236 void TrimNodes(ReachabilityMarker& marked, NodeVector& nodes) { |
215 // Remove dead->live edges. | 237 // Remove dead->live edges. |
216 for (size_t j = 0; j < nodes.size(); j++) { | 238 for (size_t j = 0; j < nodes.size(); j++) { |
217 Node* node = nodes[j]; | 239 Node* node = nodes[j]; |
218 for (UseIter i = node->uses().begin(); i != node->uses().end();) { | 240 for (UseIter i = node->uses().begin(); i != node->uses().end();) { |
219 size_t id = static_cast<size_t>((*i)->id()); | 241 if (!marked.IsReachableFromEnd(*i)) { |
220 if (state_[id] != kVisited) { | |
221 TRACE(("DeadLink: #%d:%s(%d) -> #%d:%s\n", (*i)->id(), | 242 TRACE(("DeadLink: #%d:%s(%d) -> #%d:%s\n", (*i)->id(), |
222 (*i)->op()->mnemonic(), i.index(), node->id(), | 243 (*i)->op()->mnemonic(), i.index(), node->id(), |
223 node->op()->mnemonic())); | 244 node->op()->mnemonic())); |
224 i.UpdateToAndIncrement(NULL); | 245 i.UpdateToAndIncrement(NULL); |
225 } else { | 246 } else { |
226 ++i; | 247 ++i; |
227 } | 248 } |
228 } | 249 } |
229 } | 250 } |
230 #if DEBUG | 251 #if DEBUG |
231 // Verify that no inputs to live nodes are NULL. | 252 // Verify that no inputs to live nodes are NULL. |
232 for (size_t j = 0; j < nodes.size(); j++) { | 253 for (size_t j = 0; j < nodes.size(); j++) { |
233 Node* node = nodes[j]; | 254 Node* node = nodes[j]; |
234 for (Node* const input : node->inputs()) { | 255 for (Node* const input : node->inputs()) { |
235 CHECK_NE(NULL, input); | 256 CHECK_NE(NULL, input); |
236 } | 257 } |
237 for (Node* const use : node->uses()) { | 258 for (Node* const use : node->uses()) { |
238 size_t id = static_cast<size_t>(use->id()); | 259 CHECK(marked.IsReachableFromEnd(use)); |
239 CHECK_EQ(kVisited, state_[id]); | |
240 } | 260 } |
241 } | 261 } |
242 #endif | 262 #endif |
243 } | 263 } |
244 | 264 |
245 // Reduce the node on the top of the stack. | 265 // Reduce the node on the top of the stack. |
246 // If an input {i} is not yet visited or needs to be revisited, push {i} onto | 266 // If an input {i} is not yet visited or needs to be revisited, push {i} onto |
247 // the stack and return. Otherwise, all inputs are visited, so apply | 267 // the stack and return. Otherwise, all inputs are visited, so apply |
248 // reductions for {node} and pop it off the stack. | 268 // reductions for {node} and pop it off the stack. |
249 void ReduceTop() { | 269 void ReduceTop() { |
(...skipping 262 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
512 } | 532 } |
513 | 533 |
514 Graph* graph() { return jsgraph_->graph(); } | 534 Graph* graph() { return jsgraph_->graph(); } |
515 }; | 535 }; |
516 | 536 |
517 | 537 |
518 void ControlReducer::ReduceGraph(Zone* zone, JSGraph* jsgraph, | 538 void ControlReducer::ReduceGraph(Zone* zone, JSGraph* jsgraph, |
519 CommonOperatorBuilder* common) { | 539 CommonOperatorBuilder* common) { |
520 ControlReducerImpl impl(zone, jsgraph, common); | 540 ControlReducerImpl impl(zone, jsgraph, common); |
521 impl.Reduce(); | 541 impl.Reduce(); |
522 impl.Trim(); | |
523 } | 542 } |
524 | 543 |
525 | 544 |
526 void ControlReducer::TrimGraph(Zone* zone, JSGraph* jsgraph) { | 545 void ControlReducer::TrimGraph(Zone* zone, JSGraph* jsgraph) { |
527 ControlReducerImpl impl(zone, jsgraph, NULL); | 546 ControlReducerImpl impl(zone, jsgraph, NULL); |
528 impl.Trim(); | 547 impl.Trim(); |
529 } | 548 } |
530 | 549 |
531 | 550 |
532 Node* ControlReducer::ReducePhiForTesting(JSGraph* jsgraph, | 551 Node* ControlReducer::ReducePhiForTesting(JSGraph* jsgraph, |
(...skipping 17 matching lines...) Expand all Loading... |
550 Node* ControlReducer::ReduceBranchForTesting(JSGraph* jsgraph, | 569 Node* ControlReducer::ReduceBranchForTesting(JSGraph* jsgraph, |
551 CommonOperatorBuilder* common, | 570 CommonOperatorBuilder* common, |
552 Node* node) { | 571 Node* node) { |
553 Zone zone(jsgraph->graph()->zone()->isolate()); | 572 Zone zone(jsgraph->graph()->zone()->isolate()); |
554 ControlReducerImpl impl(&zone, jsgraph, common); | 573 ControlReducerImpl impl(&zone, jsgraph, common); |
555 return impl.ReduceBranch(node); | 574 return impl.ReduceBranch(node); |
556 } | 575 } |
557 } | 576 } |
558 } | 577 } |
559 } // namespace v8::internal::compiler | 578 } // namespace v8::internal::compiler |
OLD | NEW |