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/graph-reducer.h" |
8 #include "src/compiler/js-graph.h" | 9 #include "src/compiler/js-graph.h" |
9 #include "src/compiler/node-marker.h" | 10 #include "src/compiler/node-marker.h" |
10 #include "src/compiler/node-matchers.h" | 11 #include "src/compiler/node-matchers.h" |
11 #include "src/compiler/node-properties.h" | 12 #include "src/compiler/node-properties.h" |
12 #include "src/zone-containers.h" | 13 #include "src/zone-containers.h" |
13 | 14 |
14 namespace v8 { | 15 namespace v8 { |
15 namespace internal { | 16 namespace internal { |
16 namespace compiler { | 17 namespace compiler { |
17 | 18 |
(...skipping 22 matching lines...) Expand all Loading... |
40 bool IsReachableFromStart(Node* node) { return Get(node) & kFromStart; } | 41 bool IsReachableFromStart(Node* node) { return Get(node) & kFromStart; } |
41 void Push(Node* node) { Set(node, Get(node) | kFwStack); } | 42 void Push(Node* node) { Set(node, Get(node) | kFwStack); } |
42 void Pop(Node* node) { Set(node, Get(node) & ~kFwStack); } | 43 void Pop(Node* node) { Set(node, Get(node) & ~kFwStack); } |
43 bool IsOnStack(Node* node) { return Get(node) & kFwStack; } | 44 bool IsOnStack(Node* node) { return Get(node) & kFwStack; } |
44 | 45 |
45 private: | 46 private: |
46 enum Bit { kFromEnd = 1, kFromStart = 2, kFwStack = 4 }; | 47 enum Bit { kFromEnd = 1, kFromStart = 2, kFwStack = 4 }; |
47 }; | 48 }; |
48 | 49 |
49 | 50 |
50 class ControlReducerImpl { | 51 class ControlReducerImpl final : public Reducer { |
51 public: | 52 public: |
52 ControlReducerImpl(Zone* zone, JSGraph* jsgraph, | |
53 CommonOperatorBuilder* common) | |
54 : zone_(zone), | |
55 jsgraph_(jsgraph), | |
56 common_(common), | |
57 state_(jsgraph->graph()->NodeCount(), kUnvisited, zone_), | |
58 stack_(zone_), | |
59 revisit_(zone_), | |
60 max_phis_for_select_(0) {} | |
61 | |
62 Zone* zone_; | 53 Zone* zone_; |
63 JSGraph* jsgraph_; | 54 JSGraph* jsgraph_; |
64 CommonOperatorBuilder* common_; | |
65 ZoneVector<VisitState> state_; | |
66 ZoneDeque<Node*> stack_; | |
67 ZoneDeque<Node*> revisit_; | |
68 int max_phis_for_select_; | 55 int max_phis_for_select_; |
69 | 56 |
70 void Reduce() { | 57 ControlReducerImpl(Zone* zone, JSGraph* jsgraph) |
71 Push(graph()->end()); | 58 : zone_(zone), jsgraph_(jsgraph), max_phis_for_select_(0) {} |
72 do { | |
73 // Process the node on the top of the stack, potentially pushing more | |
74 // or popping the node off the stack. | |
75 ReduceTop(); | |
76 // If the stack becomes empty, revisit any nodes in the revisit queue. | |
77 // If no nodes in the revisit queue, try removing dead loops. | |
78 // If no dead loops, then finish. | |
79 } while (!stack_.empty() || TryRevisit() || RepairAndRemoveLoops()); | |
80 } | |
81 | 59 |
82 bool TryRevisit() { | 60 Graph* graph() { return jsgraph_->graph(); } |
83 while (!revisit_.empty()) { | 61 CommonOperatorBuilder* common() { return jsgraph_->common(); } |
84 Node* n = revisit_.back(); | 62 Node* dead() { return jsgraph_->DeadControl(); } |
85 revisit_.pop_back(); | |
86 if (state_[n->id()] == kRevisit) { // state can change while in queue. | |
87 Push(n); | |
88 return true; | |
89 } | |
90 } | |
91 return false; | |
92 } | |
93 | 63 |
94 // Repair the graph after the possible creation of non-terminating or dead | 64 // Finish reducing the graph by trimming nodes and/or connecting NTLs. |
95 // loops. Removing dead loops can produce more opportunities for reduction. | 65 bool Finish() final { |
96 bool RepairAndRemoveLoops() { | 66 bool done = true; |
97 // TODO(turbofan): we can skip this if the graph has no loops, but | |
98 // we have to be careful about proper loop detection during reduction. | |
99 | |
100 // Gather all nodes backwards-reachable from end (through inputs). | 67 // Gather all nodes backwards-reachable from end (through inputs). |
101 ReachabilityMarker marked(graph()); | 68 ReachabilityMarker marked(graph()); |
102 NodeVector nodes(zone_); | 69 NodeVector nodes(zone_); |
103 AddNodesReachableFromRoots(marked, nodes); | 70 AddNodesReachableFromRoots(marked, nodes); |
104 | 71 |
105 // Walk forward through control nodes, looking for back edges to nodes | 72 // Walk forward through control nodes, looking for back edges to nodes |
106 // that are not connected to end. Those are non-terminating loops (NTLs). | 73 // that are not connected to end. Those are non-terminating loops (NTLs). |
107 Node* start = graph()->start(); | 74 Node* start = graph()->start(); |
108 marked.Push(start); | 75 marked.Push(start); |
109 marked.SetReachableFromStart(start); | 76 marked.SetReachableFromStart(start); |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
157 } | 124 } |
158 } | 125 } |
159 | 126 |
160 // Trim references from dead nodes to live nodes first. | 127 // Trim references from dead nodes to live nodes first. |
161 TrimNodes(marked, nodes); | 128 TrimNodes(marked, nodes); |
162 | 129 |
163 // Any control nodes not reachable from start are dead, even loops. | 130 // Any control nodes not reachable from start are dead, even loops. |
164 for (size_t i = 0; i < nodes.size(); i++) { | 131 for (size_t i = 0; i < nodes.size(); i++) { |
165 Node* node = nodes[i]; | 132 Node* node = nodes[i]; |
166 if (node->op()->ControlOutputCount() > 0 && | 133 if (node->op()->ControlOutputCount() > 0 && |
167 !marked.IsReachableFromStart(node)) { | 134 !marked.IsReachableFromStart(node) && |
168 ReplaceNode(node, dead()); // uses will be added to revisit queue. | 135 node->opcode() != IrOpcode::kDead) { |
| 136 TRACE("Dead: #%d:%s\n", node->id(), node->op()->mnemonic()); |
| 137 node->ReplaceUses(dead()); |
| 138 done = false; |
169 } | 139 } |
170 } | 140 } |
171 return TryRevisit(); // try to push a node onto the stack. | 141 |
| 142 return done; |
172 } | 143 } |
173 | 144 |
174 // Connect {loop}, the header of a non-terminating loop, to the end node. | 145 // Connect {loop}, the header of a non-terminating loop, to the end node. |
175 Node* ConnectNTL(Node* loop) { | 146 Node* ConnectNTL(Node* loop) { |
176 TRACE("ConnectNTL: #%d:%s\n", loop->id(), loop->op()->mnemonic()); | 147 TRACE("ConnectNTL: #%d:%s\n", loop->id(), loop->op()->mnemonic()); |
177 DCHECK_EQ(IrOpcode::kLoop, loop->opcode()); | 148 DCHECK_EQ(IrOpcode::kLoop, loop->opcode()); |
178 | 149 |
179 Node* always = graph()->NewNode(common_->Always()); | 150 Node* always = graph()->NewNode(common()->Always()); |
180 // Mark the node as visited so that we can revisit later. | 151 Node* branch = graph()->NewNode(common()->Branch(), always, loop); |
181 MarkAsVisited(always); | 152 Node* if_true = graph()->NewNode(common()->IfTrue(), branch); |
| 153 Node* if_false = graph()->NewNode(common()->IfFalse(), branch); |
182 | 154 |
183 Node* branch = graph()->NewNode(common_->Branch(), always, loop); | 155 // Insert the branch into the loop and collect all loop effects. |
184 // Mark the node as visited so that we can revisit later. | |
185 MarkAsVisited(branch); | |
186 | |
187 Node* if_true = graph()->NewNode(common_->IfTrue(), branch); | |
188 // Mark the node as visited so that we can revisit later. | |
189 MarkAsVisited(if_true); | |
190 | |
191 Node* if_false = graph()->NewNode(common_->IfFalse(), branch); | |
192 // Mark the node as visited so that we can revisit later. | |
193 MarkAsVisited(if_false); | |
194 | |
195 // Hook up the branch into the loop and collect all loop effects. | |
196 NodeVector effects(zone_); | 156 NodeVector effects(zone_); |
197 for (auto edge : loop->use_edges()) { | 157 for (auto edge : loop->use_edges()) { |
198 DCHECK_EQ(loop, edge.to()); | 158 DCHECK_EQ(loop, edge.to()); |
199 DCHECK(NodeProperties::IsControlEdge(edge)); | 159 DCHECK(NodeProperties::IsControlEdge(edge)); |
200 if (edge.from() == branch) continue; | 160 if (edge.from() == branch) continue; |
201 switch (edge.from()->opcode()) { | 161 switch (edge.from()->opcode()) { |
202 case IrOpcode::kPhi: | 162 case IrOpcode::kPhi: |
203 break; | 163 break; |
204 case IrOpcode::kEffectPhi: | 164 case IrOpcode::kEffectPhi: |
205 effects.push_back(edge.from()); | 165 effects.push_back(edge.from()); |
206 break; | 166 break; |
207 default: | 167 default: |
208 // Update all control edges (except {branch}) pointing to the {loop}. | 168 // Update all control edges (except {branch}) pointing to the {loop}. |
209 edge.UpdateTo(if_true); | 169 edge.UpdateTo(if_true); |
210 break; | 170 break; |
211 } | 171 } |
212 } | 172 } |
213 | 173 |
214 // Compute effects for the Return. | 174 // Compute effects for the Return. |
215 Node* effect = graph()->start(); | 175 Node* effect = graph()->start(); |
216 int const effects_count = static_cast<int>(effects.size()); | 176 int const effects_count = static_cast<int>(effects.size()); |
217 if (effects_count == 1) { | 177 if (effects_count == 1) { |
218 effect = effects[0]; | 178 effect = effects[0]; |
219 } else if (effects_count > 1) { | 179 } else if (effects_count > 1) { |
220 effect = graph()->NewNode(common_->EffectSet(effects_count), | 180 effect = graph()->NewNode(common()->EffectSet(effects_count), |
221 effects_count, &effects.front()); | 181 effects_count, &effects.front()); |
222 // Mark the node as visited so that we can revisit later. | |
223 MarkAsVisited(effect); | |
224 } | 182 } |
225 | 183 |
226 // Add a return to connect the NTL to the end. | 184 // Add a return to connect the NTL to the end. |
227 Node* ret = graph()->NewNode( | 185 Node* ret = graph()->NewNode( |
228 common_->Return(), jsgraph_->UndefinedConstant(), effect, if_false); | 186 common()->Return(), jsgraph_->UndefinedConstant(), effect, if_false); |
229 // Mark the node as visited so that we can revisit later. | |
230 MarkAsVisited(ret); | |
231 | 187 |
232 Node* end = graph()->end(); | 188 Node* end = graph()->end(); |
233 CHECK_EQ(IrOpcode::kEnd, end->opcode()); | 189 if (end->opcode() == IrOpcode::kDead) { |
| 190 // End is actually the dead node. Make a new end. |
| 191 end = graph()->NewNode(common()->End(), ret); |
| 192 graph()->SetEnd(end); |
| 193 return end; |
| 194 } |
| 195 // End is not dead. |
234 Node* merge = end->InputAt(0); | 196 Node* merge = end->InputAt(0); |
235 if (merge == NULL || merge->opcode() == IrOpcode::kDead) { | 197 if (merge == NULL || merge->opcode() == IrOpcode::kDead) { |
236 // The end node died; just connect end to {ret}. | 198 // The end node died; just connect end to {ret}. |
237 end->ReplaceInput(0, ret); | 199 end->ReplaceInput(0, ret); |
238 } else if (merge->opcode() != IrOpcode::kMerge) { | 200 } else if (merge->opcode() != IrOpcode::kMerge) { |
239 // Introduce a final merge node for {end->InputAt(0)} and {ret}. | 201 // Introduce a final merge node for {end->InputAt(0)} and {ret}. |
240 merge = graph()->NewNode(common_->Merge(2), merge, ret); | 202 merge = graph()->NewNode(common()->Merge(2), merge, ret); |
241 end->ReplaceInput(0, merge); | 203 end->ReplaceInput(0, merge); |
242 ret = merge; | 204 ret = merge; |
243 // Mark the node as visited so that we can revisit later. | |
244 MarkAsVisited(merge); | |
245 } else { | 205 } else { |
246 // Append a new input to the final merge at the end. | 206 // Append a new input to the final merge at the end. |
247 merge->AppendInput(graph()->zone(), ret); | 207 merge->AppendInput(graph()->zone(), ret); |
248 merge->set_op(common_->Merge(merge->InputCount())); | 208 merge->set_op(common()->Merge(merge->InputCount())); |
249 } | 209 } |
250 return ret; | 210 return ret; |
251 } | 211 } |
252 | 212 |
253 void AddNodesReachableFromRoots(ReachabilityMarker& marked, | 213 void AddNodesReachableFromRoots(ReachabilityMarker& marked, |
254 NodeVector& nodes) { | 214 NodeVector& nodes) { |
255 jsgraph_->GetCachedNodes(&nodes); // Consider cached nodes roots. | 215 jsgraph_->GetCachedNodes(&nodes); // Consider cached nodes roots. |
256 Node* end = graph()->end(); | 216 Node* end = graph()->end(); |
257 marked.SetReachableFromEnd(end); | 217 marked.SetReachableFromEnd(end); |
258 if (!end->IsDead()) nodes.push_back(end); // Consider end to be a root. | 218 if (!end->IsDead()) nodes.push_back(end); // Consider end to be a root. |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
313 FATAL(str.str().c_str()); | 273 FATAL(str.str().c_str()); |
314 } | 274 } |
315 } | 275 } |
316 for (Node* use : node->uses()) { | 276 for (Node* use : node->uses()) { |
317 CHECK(marked.IsReachableFromEnd(use)); | 277 CHECK(marked.IsReachableFromEnd(use)); |
318 } | 278 } |
319 } | 279 } |
320 #endif | 280 #endif |
321 } | 281 } |
322 | 282 |
323 // Reduce the node on the top of the stack. | |
324 // If an input {i} is not yet visited or needs to be revisited, push {i} onto | |
325 // the stack and return. Otherwise, all inputs are visited, so apply | |
326 // reductions for {node} and pop it off the stack. | |
327 void ReduceTop() { | |
328 size_t height = stack_.size(); | |
329 Node* node = stack_.back(); | |
330 | |
331 if (node->IsDead()) return Pop(); // Node was killed while on stack. | |
332 | |
333 TRACE("ControlReduce: #%d:%s\n", node->id(), node->op()->mnemonic()); | |
334 | |
335 // Recurse on an input if necessary. | |
336 for (Node* const input : node->inputs()) { | |
337 DCHECK(input); | |
338 if (Recurse(input)) return; | |
339 } | |
340 | |
341 // All inputs should be visited or on stack. Apply reductions to node. | |
342 Node* replacement = ReduceNode(node); | |
343 if (replacement != node) ReplaceNode(node, replacement); | |
344 | |
345 // After reducing the node, pop it off the stack. | |
346 CHECK_EQ(static_cast<int>(height), static_cast<int>(stack_.size())); | |
347 Pop(); | |
348 | |
349 // If there was a replacement, reduce it after popping {node}. | |
350 if (replacement != node) Recurse(replacement); | |
351 } | |
352 | |
353 void EnsureStateSize(size_t id) { | |
354 if (id >= state_.size()) { | |
355 state_.resize((3 * id) / 2, kUnvisited); | |
356 } | |
357 } | |
358 | |
359 // Push a node onto the stack if its state is {kUnvisited} or {kRevisit}. | |
360 bool Recurse(Node* node) { | |
361 size_t id = static_cast<size_t>(node->id()); | |
362 EnsureStateSize(id); | |
363 if (state_[id] != kRevisit && state_[id] != kUnvisited) return false; | |
364 Push(node); | |
365 return true; | |
366 } | |
367 | |
368 void Push(Node* node) { | |
369 state_[node->id()] = kOnStack; | |
370 stack_.push_back(node); | |
371 } | |
372 | |
373 void Pop() { | |
374 int pos = static_cast<int>(stack_.size()) - 1; | |
375 DCHECK_GE(pos, 0); | |
376 DCHECK_EQ(kOnStack, state_[stack_[pos]->id()]); | |
377 state_[stack_[pos]->id()] = kVisited; | |
378 stack_.pop_back(); | |
379 } | |
380 | |
381 // Queue a node to be revisited if it has been visited once already. | |
382 void Revisit(Node* node) { | |
383 size_t id = static_cast<size_t>(node->id()); | |
384 if (id < state_.size() && state_[id] == kVisited) { | |
385 TRACE(" Revisit #%d:%s\n", node->id(), node->op()->mnemonic()); | |
386 state_[id] = kRevisit; | |
387 revisit_.push_back(node); | |
388 } | |
389 } | |
390 | |
391 // Mark {node} as visited. | |
392 void MarkAsVisited(Node* node) { | |
393 size_t id = static_cast<size_t>(node->id()); | |
394 EnsureStateSize(id); | |
395 state_[id] = kVisited; | |
396 } | |
397 | |
398 Node* dead() { return jsgraph_->DeadControl(); } | |
399 | |
400 //=========================================================================== | 283 //=========================================================================== |
401 // Reducer implementation: perform reductions on a node. | 284 // Reducer implementation: perform reductions on a node. |
402 //=========================================================================== | 285 //=========================================================================== |
403 Node* ReduceNode(Node* node) { | 286 Reduction Reduce(Node* node) override { |
404 if (node->op()->ControlInputCount() == 1 || | 287 if (node->op()->ControlInputCount() == 1 || |
405 node->opcode() == IrOpcode::kLoop) { | 288 node->opcode() == IrOpcode::kLoop) { |
406 // If a node has only one control input and it is dead, replace with dead. | 289 // If a node has only one control input and it is dead, replace with dead. |
407 Node* control = NodeProperties::GetControlInput(node); | 290 Node* control = NodeProperties::GetControlInput(node); |
408 if (control->opcode() == IrOpcode::kDead) { | 291 if (control->opcode() == IrOpcode::kDead) { |
409 TRACE("ControlDead: #%d:%s\n", node->id(), node->op()->mnemonic()); | 292 TRACE("ControlDead: #%d:%s\n", node->id(), node->op()->mnemonic()); |
410 return control; | 293 return Replace(control); |
411 } | 294 } |
412 } | 295 } |
413 | 296 |
| 297 Node* result = node; |
414 // Reduce branches, phis, and merges. | 298 // Reduce branches, phis, and merges. |
415 switch (node->opcode()) { | 299 switch (node->opcode()) { |
416 case IrOpcode::kBranch: | 300 case IrOpcode::kBranch: |
417 return ReduceBranch(node); | 301 result = ReduceBranch(node); |
| 302 break; |
418 case IrOpcode::kIfTrue: | 303 case IrOpcode::kIfTrue: |
419 return ReduceIfProjection(node, kTrue); | 304 result = ReduceIfProjection(node, kTrue); |
| 305 break; |
420 case IrOpcode::kIfFalse: | 306 case IrOpcode::kIfFalse: |
421 return ReduceIfProjection(node, kFalse); | 307 result = ReduceIfProjection(node, kFalse); |
422 case IrOpcode::kLoop: | 308 break; |
| 309 case IrOpcode::kLoop: // fallthrough |
423 case IrOpcode::kMerge: | 310 case IrOpcode::kMerge: |
424 return ReduceMerge(node); | 311 result = ReduceMerge(node); |
| 312 break; |
425 case IrOpcode::kSelect: | 313 case IrOpcode::kSelect: |
426 return ReduceSelect(node); | 314 result = ReduceSelect(node); |
| 315 break; |
427 case IrOpcode::kPhi: | 316 case IrOpcode::kPhi: |
428 case IrOpcode::kEffectPhi: | 317 case IrOpcode::kEffectPhi: |
429 return ReducePhi(node); | 318 result = ReducePhi(node); |
| 319 break; |
430 default: | 320 default: |
431 return node; | 321 break; |
432 } | 322 } |
| 323 |
| 324 return result == node ? NoChange() : Replace(result); |
433 } | 325 } |
434 | 326 |
435 // Try to statically fold a condition. | 327 // Try to statically fold a condition. |
436 Decision DecideCondition(Node* cond, bool recurse = true) { | 328 Decision DecideCondition(Node* cond, bool recurse = true) { |
437 switch (cond->opcode()) { | 329 switch (cond->opcode()) { |
438 case IrOpcode::kInt32Constant: | 330 case IrOpcode::kInt32Constant: |
439 return Int32Matcher(cond).Is(0) ? kFalse : kTrue; | 331 return Int32Matcher(cond).Is(0) ? kFalse : kTrue; |
440 case IrOpcode::kInt64Constant: | 332 case IrOpcode::kInt64Constant: |
441 return Int64Matcher(cond).Is(0) ? kFalse : kTrue; | 333 return Int64Matcher(cond).Is(0) ? kFalse : kTrue; |
442 case IrOpcode::kNumberConstant: | 334 case IrOpcode::kNumberConstant: |
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
536 if (live == 0) return dead(); // no remaining inputs. | 428 if (live == 0) return dead(); // no remaining inputs. |
537 | 429 |
538 // Gather phis and effect phis to be edited. | 430 // Gather phis and effect phis to be edited. |
539 NodeVector phis(zone_); | 431 NodeVector phis(zone_); |
540 for (Node* const use : node->uses()) { | 432 for (Node* const use : node->uses()) { |
541 if (NodeProperties::IsPhi(use)) phis.push_back(use); | 433 if (NodeProperties::IsPhi(use)) phis.push_back(use); |
542 } | 434 } |
543 | 435 |
544 if (live == 1) { | 436 if (live == 1) { |
545 // All phis are redundant. Replace them with their live input. | 437 // All phis are redundant. Replace them with their live input. |
546 for (Node* const phi : phis) ReplaceNode(phi, phi->InputAt(live_index)); | 438 for (Node* const phi : phis) { |
| 439 Replace(phi, phi->InputAt(live_index)); |
| 440 } |
547 // The merge itself is redundant. | 441 // The merge itself is redundant. |
548 return node->InputAt(live_index); | 442 return node->InputAt(live_index); |
549 } | 443 } |
550 | 444 |
551 DCHECK_LE(2, live); | 445 DCHECK_LE(2, live); |
552 | 446 |
553 if (live < node->InputCount()) { | 447 if (live < node->InputCount()) { |
554 // Edit phis in place, removing dead inputs and revisiting them. | 448 // Edit phis in place, removing dead inputs and revisiting them. |
555 for (Node* const phi : phis) { | 449 for (Node* const phi : phis) { |
556 TRACE(" PhiInMerge: #%d:%s (%d live)\n", phi->id(), | 450 TRACE(" PhiInMerge: #%d:%s (%d live)\n", phi->id(), |
(...skipping 19 matching lines...) Expand all Loading... |
576 // No phis. Remove the branch altogether. | 470 // No phis. Remove the branch altogether. |
577 TRACE(" DeadDiamond: #%d:Branch #%d:IfTrue #%d:IfFalse\n", | 471 TRACE(" DeadDiamond: #%d:Branch #%d:IfTrue #%d:IfFalse\n", |
578 matcher.Branch()->id(), matcher.IfTrue()->id(), | 472 matcher.Branch()->id(), matcher.IfTrue()->id(), |
579 matcher.IfFalse()->id()); | 473 matcher.IfFalse()->id()); |
580 return control; | 474 return control; |
581 } else { | 475 } else { |
582 // A small number of phis. Replace with selects. | 476 // A small number of phis. Replace with selects. |
583 Node* cond = matcher.Branch()->InputAt(0); | 477 Node* cond = matcher.Branch()->InputAt(0); |
584 for (Node* phi : phis) { | 478 for (Node* phi : phis) { |
585 Node* select = graph()->NewNode( | 479 Node* select = graph()->NewNode( |
586 common_->Select(OpParameter<MachineType>(phi), | 480 common()->Select(OpParameter<MachineType>(phi), |
587 BranchHintOf(matcher.Branch()->op())), | 481 BranchHintOf(matcher.Branch()->op())), |
588 cond, matcher.TrueInputOf(phi), matcher.FalseInputOf(phi)); | 482 cond, matcher.TrueInputOf(phi), matcher.FalseInputOf(phi)); |
589 TRACE(" MatchSelect: #%d:Branch #%d:IfTrue #%d:IfFalse -> #%d\n", | 483 TRACE(" MatchSelect: #%d:Branch #%d:IfTrue #%d:IfFalse -> #%d\n", |
590 matcher.Branch()->id(), matcher.IfTrue()->id(), | 484 matcher.Branch()->id(), matcher.IfTrue()->id(), |
591 matcher.IfFalse()->id(), select->id()); | 485 matcher.IfFalse()->id(), select->id()); |
592 ReplaceNode(phi, select); | 486 Replace(phi, select); |
593 } | 487 } |
594 return control; | 488 return control; |
595 } | 489 } |
596 } | 490 } |
597 } | 491 } |
598 | 492 |
599 return node; | 493 return node; |
600 } | 494 } |
601 | 495 |
602 bool CheckPhisForSelect(const NodeVector& phis) { | 496 bool CheckPhisForSelect(const NodeVector& phis) { |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
634 } | 528 } |
635 // compact remaining inputs. | 529 // compact remaining inputs. |
636 int total = live; | 530 int total = live; |
637 for (int i = merge->InputCount(); i < node->InputCount(); i++) { | 531 for (int i = merge->InputCount(); i < node->InputCount(); i++) { |
638 if (total != i) node->ReplaceInput(total, node->InputAt(i)); | 532 if (total != i) node->ReplaceInput(total, node->InputAt(i)); |
639 total++; | 533 total++; |
640 } | 534 } |
641 DCHECK_EQ(total, live + node->InputCount() - merge->InputCount()); | 535 DCHECK_EQ(total, live + node->InputCount() - merge->InputCount()); |
642 DCHECK_NE(total, node->InputCount()); | 536 DCHECK_NE(total, node->InputCount()); |
643 node->TrimInputCount(total); | 537 node->TrimInputCount(total); |
644 node->set_op(common_->ResizeMergeOrPhi(node->op(), live)); | 538 node->set_op(common()->ResizeMergeOrPhi(node->op(), live)); |
645 } | 539 } |
646 | |
647 // Replace uses of {node} with {replacement} and revisit the uses. | |
648 void ReplaceNode(Node* node, Node* replacement) { | |
649 if (node == replacement) return; | |
650 TRACE(" Replace: #%d:%s with #%d:%s\n", node->id(), node->op()->mnemonic(), | |
651 replacement->id(), replacement->op()->mnemonic()); | |
652 for (Node* const use : node->uses()) { | |
653 // Don't revisit this node if it refers to itself. | |
654 if (use != node) Revisit(use); | |
655 } | |
656 node->ReplaceUses(replacement); | |
657 node->Kill(); | |
658 } | |
659 | |
660 Graph* graph() { return jsgraph_->graph(); } | |
661 }; | 540 }; |
662 | 541 |
663 | 542 |
664 void ControlReducer::ReduceGraph(Zone* zone, JSGraph* jsgraph, | 543 void ControlReducer::ReduceGraph(Zone* zone, JSGraph* jsgraph, |
665 CommonOperatorBuilder* common, | |
666 int max_phis_for_select) { | 544 int max_phis_for_select) { |
667 ControlReducerImpl impl(zone, jsgraph, common); | 545 GraphReducer graph_reducer(jsgraph->graph(), zone); |
| 546 ControlReducerImpl impl(zone, jsgraph); |
668 impl.max_phis_for_select_ = max_phis_for_select; | 547 impl.max_phis_for_select_ = max_phis_for_select; |
669 impl.Reduce(); | 548 graph_reducer.AddReducer(&impl); |
| 549 graph_reducer.ReduceGraph(); |
670 } | 550 } |
671 | 551 |
672 | 552 |
673 void ControlReducer::TrimGraph(Zone* zone, JSGraph* jsgraph) { | 553 void ControlReducer::TrimGraph(Zone* zone, JSGraph* jsgraph) { |
674 ControlReducerImpl impl(zone, jsgraph, NULL); | 554 ControlReducerImpl impl(zone, jsgraph); |
675 impl.Trim(); | 555 impl.Trim(); |
676 } | 556 } |
677 | 557 |
678 | 558 |
679 Node* ControlReducer::ReduceMerge(JSGraph* jsgraph, | 559 namespace { |
680 CommonOperatorBuilder* common, Node* node, | 560 |
| 561 class DummyObserver final : public Reducer::Observer { |
| 562 public: |
| 563 void Replace(Node* node, Node* replacement) final { |
| 564 node->ReplaceUses(replacement); |
| 565 } |
| 566 void Revisit(Node* node) final {} |
| 567 }; |
| 568 |
| 569 } // namespace |
| 570 |
| 571 |
| 572 Node* ControlReducer::ReduceMerge(JSGraph* jsgraph, Node* node, |
681 int max_phis_for_select) { | 573 int max_phis_for_select) { |
682 Zone zone; | 574 Zone zone; |
683 ControlReducerImpl impl(&zone, jsgraph, common); | 575 DummyObserver observer; |
| 576 ControlReducerImpl impl(&zone, jsgraph); |
684 impl.max_phis_for_select_ = max_phis_for_select; | 577 impl.max_phis_for_select_ = max_phis_for_select; |
| 578 impl.set_observer(&observer); |
685 return impl.ReduceMerge(node); | 579 return impl.ReduceMerge(node); |
686 } | 580 } |
687 | 581 |
688 | 582 |
689 Node* ControlReducer::ReducePhiForTesting(JSGraph* jsgraph, | 583 Node* ControlReducer::ReducePhiForTesting(JSGraph* jsgraph, Node* node) { |
690 CommonOperatorBuilder* common, | |
691 Node* node) { | |
692 Zone zone; | 584 Zone zone; |
693 ControlReducerImpl impl(&zone, jsgraph, common); | 585 DummyObserver observer; |
| 586 ControlReducerImpl impl(&zone, jsgraph); |
| 587 impl.set_observer(&observer); |
694 return impl.ReducePhi(node); | 588 return impl.ReducePhi(node); |
695 } | 589 } |
696 | 590 |
697 | 591 |
698 Node* ControlReducer::ReduceIfNodeForTesting(JSGraph* jsgraph, | 592 Node* ControlReducer::ReduceIfNodeForTesting(JSGraph* jsgraph, Node* node) { |
699 CommonOperatorBuilder* common, | |
700 Node* node) { | |
701 Zone zone; | 593 Zone zone; |
702 ControlReducerImpl impl(&zone, jsgraph, common); | 594 DummyObserver observer; |
| 595 ControlReducerImpl impl(&zone, jsgraph); |
| 596 impl.set_observer(&observer); |
703 switch (node->opcode()) { | 597 switch (node->opcode()) { |
704 case IrOpcode::kIfTrue: | 598 case IrOpcode::kIfTrue: |
705 return impl.ReduceIfProjection(node, kTrue); | 599 return impl.ReduceIfProjection(node, kTrue); |
706 case IrOpcode::kIfFalse: | 600 case IrOpcode::kIfFalse: |
707 return impl.ReduceIfProjection(node, kFalse); | 601 return impl.ReduceIfProjection(node, kFalse); |
708 default: | 602 default: |
709 return node; | 603 return node; |
710 } | 604 } |
711 } | 605 } |
712 } | 606 |
713 } | 607 } // namespace compiler |
714 } // namespace v8::internal::compiler | 608 } // namespace internal |
| 609 } // namespace v8 |
OLD | NEW |