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 AdvancedReducer { |
51 public: | 52 public: |
52 ControlReducerImpl(Zone* zone, JSGraph* jsgraph, | 53 Zone* zone_; |
53 CommonOperatorBuilder* common) | 54 JSGraph* jsgraph_; |
54 : zone_(zone), | 55 int max_phis_for_select_; |
| 56 |
| 57 ControlReducerImpl(Editor* editor, Zone* zone, JSGraph* jsgraph) |
| 58 : AdvancedReducer(editor), |
| 59 zone_(zone), |
55 jsgraph_(jsgraph), | 60 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 max_phis_for_select_(0) {} |
61 | 62 |
62 Zone* zone_; | 63 Graph* graph() { return jsgraph_->graph(); } |
63 JSGraph* jsgraph_; | 64 CommonOperatorBuilder* common() { return jsgraph_->common(); } |
64 CommonOperatorBuilder* common_; | 65 Node* dead() { return jsgraph_->DeadControl(); } |
65 ZoneVector<VisitState> state_; | |
66 ZoneDeque<Node*> stack_; | |
67 ZoneDeque<Node*> revisit_; | |
68 int max_phis_for_select_; | |
69 | 66 |
70 void Reduce() { | 67 // Finish reducing the graph by trimming nodes and/or connecting NTLs. |
71 Push(graph()->end()); | 68 bool Finish() final { |
72 do { | 69 bool done = true; |
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 | |
82 bool TryRevisit() { | |
83 while (!revisit_.empty()) { | |
84 Node* n = revisit_.back(); | |
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 | |
94 // Repair the graph after the possible creation of non-terminating or dead | |
95 // loops. Removing dead loops can produce more opportunities for reduction. | |
96 bool RepairAndRemoveLoops() { | |
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). | 70 // Gather all nodes backwards-reachable from end (through inputs). |
101 ReachabilityMarker marked(graph()); | 71 ReachabilityMarker marked(graph()); |
102 NodeVector nodes(zone_); | 72 NodeVector nodes(zone_); |
103 AddNodesReachableFromRoots(marked, nodes); | 73 AddNodesReachableFromRoots(marked, nodes); |
104 | 74 |
105 // Walk forward through control nodes, looking for back edges to nodes | 75 // Walk forward through control nodes, looking for back edges to nodes |
106 // that are not connected to end. Those are non-terminating loops (NTLs). | 76 // that are not connected to end. Those are non-terminating loops (NTLs). |
107 Node* start = graph()->start(); | 77 Node* start = graph()->start(); |
108 marked.Push(start); | 78 marked.Push(start); |
109 marked.SetReachableFromStart(start); | 79 marked.SetReachableFromStart(start); |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
157 } | 127 } |
158 } | 128 } |
159 | 129 |
160 // Trim references from dead nodes to live nodes first. | 130 // Trim references from dead nodes to live nodes first. |
161 TrimNodes(marked, nodes); | 131 TrimNodes(marked, nodes); |
162 | 132 |
163 // Any control nodes not reachable from start are dead, even loops. | 133 // Any control nodes not reachable from start are dead, even loops. |
164 for (size_t i = 0; i < nodes.size(); i++) { | 134 for (size_t i = 0; i < nodes.size(); i++) { |
165 Node* node = nodes[i]; | 135 Node* node = nodes[i]; |
166 if (node->op()->ControlOutputCount() > 0 && | 136 if (node->op()->ControlOutputCount() > 0 && |
167 !marked.IsReachableFromStart(node)) { | 137 !marked.IsReachableFromStart(node) && |
168 ReplaceNode(node, dead()); // uses will be added to revisit queue. | 138 node->opcode() != IrOpcode::kDead) { |
| 139 TRACE("Dead: #%d:%s\n", node->id(), node->op()->mnemonic()); |
| 140 node->ReplaceUses(dead()); |
| 141 done = false; |
169 } | 142 } |
170 } | 143 } |
171 return TryRevisit(); // try to push a node onto the stack. | 144 |
| 145 return done; |
172 } | 146 } |
173 | 147 |
174 // Connect {loop}, the header of a non-terminating loop, to the end node. | 148 // Connect {loop}, the header of a non-terminating loop, to the end node. |
175 Node* ConnectNTL(Node* loop) { | 149 Node* ConnectNTL(Node* loop) { |
176 TRACE("ConnectNTL: #%d:%s\n", loop->id(), loop->op()->mnemonic()); | 150 TRACE("ConnectNTL: #%d:%s\n", loop->id(), loop->op()->mnemonic()); |
177 DCHECK_EQ(IrOpcode::kLoop, loop->opcode()); | 151 DCHECK_EQ(IrOpcode::kLoop, loop->opcode()); |
178 | 152 |
179 Node* always = graph()->NewNode(common_->Always()); | 153 Node* always = graph()->NewNode(common()->Always()); |
180 // Mark the node as visited so that we can revisit later. | 154 Node* branch = graph()->NewNode(common()->Branch(), always, loop); |
181 MarkAsVisited(always); | 155 Node* if_true = graph()->NewNode(common()->IfTrue(), branch); |
| 156 Node* if_false = graph()->NewNode(common()->IfFalse(), branch); |
182 | 157 |
183 Node* branch = graph()->NewNode(common_->Branch(), always, loop); | 158 // 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_); | 159 NodeVector effects(zone_); |
197 for (auto edge : loop->use_edges()) { | 160 for (auto edge : loop->use_edges()) { |
198 DCHECK_EQ(loop, edge.to()); | 161 DCHECK_EQ(loop, edge.to()); |
199 DCHECK(NodeProperties::IsControlEdge(edge)); | 162 DCHECK(NodeProperties::IsControlEdge(edge)); |
200 if (edge.from() == branch) continue; | 163 if (edge.from() == branch) continue; |
201 switch (edge.from()->opcode()) { | 164 switch (edge.from()->opcode()) { |
202 case IrOpcode::kPhi: | 165 case IrOpcode::kPhi: |
203 break; | 166 break; |
204 case IrOpcode::kEffectPhi: | 167 case IrOpcode::kEffectPhi: |
205 effects.push_back(edge.from()); | 168 effects.push_back(edge.from()); |
206 break; | 169 break; |
207 default: | 170 default: |
208 // Update all control edges (except {branch}) pointing to the {loop}. | 171 // Update all control edges (except {branch}) pointing to the {loop}. |
209 edge.UpdateTo(if_true); | 172 edge.UpdateTo(if_true); |
210 break; | 173 break; |
211 } | 174 } |
212 } | 175 } |
213 | 176 |
214 // Compute effects for the Return. | 177 // Compute effects for the Return. |
215 Node* effect = graph()->start(); | 178 Node* effect = graph()->start(); |
216 int const effects_count = static_cast<int>(effects.size()); | 179 int const effects_count = static_cast<int>(effects.size()); |
217 if (effects_count == 1) { | 180 if (effects_count == 1) { |
218 effect = effects[0]; | 181 effect = effects[0]; |
219 } else if (effects_count > 1) { | 182 } else if (effects_count > 1) { |
220 effect = graph()->NewNode(common_->EffectSet(effects_count), | 183 effect = graph()->NewNode(common()->EffectSet(effects_count), |
221 effects_count, &effects.front()); | 184 effects_count, &effects.front()); |
222 // Mark the node as visited so that we can revisit later. | |
223 MarkAsVisited(effect); | |
224 } | 185 } |
225 | 186 |
226 // Add a return to connect the NTL to the end. | 187 // Add a return to connect the NTL to the end. |
227 Node* ret = graph()->NewNode( | 188 Node* ret = graph()->NewNode( |
228 common_->Return(), jsgraph_->UndefinedConstant(), effect, if_false); | 189 common()->Return(), jsgraph_->UndefinedConstant(), effect, if_false); |
229 // Mark the node as visited so that we can revisit later. | |
230 MarkAsVisited(ret); | |
231 | 190 |
232 Node* end = graph()->end(); | 191 Node* end = graph()->end(); |
233 CHECK_EQ(IrOpcode::kEnd, end->opcode()); | 192 if (end->opcode() == IrOpcode::kDead) { |
| 193 // End is actually the dead node. Make a new end. |
| 194 end = graph()->NewNode(common()->End(), ret); |
| 195 graph()->SetEnd(end); |
| 196 return end; |
| 197 } |
| 198 // End is not dead. |
234 Node* merge = end->InputAt(0); | 199 Node* merge = end->InputAt(0); |
235 if (merge == NULL || merge->opcode() == IrOpcode::kDead) { | 200 if (merge == NULL || merge->opcode() == IrOpcode::kDead) { |
236 // The end node died; just connect end to {ret}. | 201 // The end node died; just connect end to {ret}. |
237 end->ReplaceInput(0, ret); | 202 end->ReplaceInput(0, ret); |
238 } else if (merge->opcode() != IrOpcode::kMerge) { | 203 } else if (merge->opcode() != IrOpcode::kMerge) { |
239 // Introduce a final merge node for {end->InputAt(0)} and {ret}. | 204 // Introduce a final merge node for {end->InputAt(0)} and {ret}. |
240 merge = graph()->NewNode(common_->Merge(2), merge, ret); | 205 merge = graph()->NewNode(common()->Merge(2), merge, ret); |
241 end->ReplaceInput(0, merge); | 206 end->ReplaceInput(0, merge); |
242 ret = merge; | 207 ret = merge; |
243 // Mark the node as visited so that we can revisit later. | |
244 MarkAsVisited(merge); | |
245 } else { | 208 } else { |
246 // Append a new input to the final merge at the end. | 209 // Append a new input to the final merge at the end. |
247 merge->AppendInput(graph()->zone(), ret); | 210 merge->AppendInput(graph()->zone(), ret); |
248 merge->set_op(common_->Merge(merge->InputCount())); | 211 merge->set_op(common()->Merge(merge->InputCount())); |
249 } | 212 } |
250 return ret; | 213 return ret; |
251 } | 214 } |
252 | 215 |
253 void AddNodesReachableFromRoots(ReachabilityMarker& marked, | 216 void AddNodesReachableFromRoots(ReachabilityMarker& marked, |
254 NodeVector& nodes) { | 217 NodeVector& nodes) { |
255 jsgraph_->GetCachedNodes(&nodes); // Consider cached nodes roots. | 218 jsgraph_->GetCachedNodes(&nodes); // Consider cached nodes roots. |
256 Node* end = graph()->end(); | 219 Node* end = graph()->end(); |
257 marked.SetReachableFromEnd(end); | 220 marked.SetReachableFromEnd(end); |
258 if (!end->IsDead()) nodes.push_back(end); // Consider end to be a root. | 221 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()); | 276 FATAL(str.str().c_str()); |
314 } | 277 } |
315 } | 278 } |
316 for (Node* use : node->uses()) { | 279 for (Node* use : node->uses()) { |
317 CHECK(marked.IsReachableFromEnd(use)); | 280 CHECK(marked.IsReachableFromEnd(use)); |
318 } | 281 } |
319 } | 282 } |
320 #endif | 283 #endif |
321 } | 284 } |
322 | 285 |
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 //=========================================================================== | 286 //=========================================================================== |
401 // Reducer implementation: perform reductions on a node. | 287 // Reducer implementation: perform reductions on a node. |
402 //=========================================================================== | 288 //=========================================================================== |
403 Node* ReduceNode(Node* node) { | 289 Reduction Reduce(Node* node) override { |
404 if (node->op()->ControlInputCount() == 1 || | 290 if (node->op()->ControlInputCount() == 1 || |
405 node->opcode() == IrOpcode::kLoop) { | 291 node->opcode() == IrOpcode::kLoop) { |
406 // If a node has only one control input and it is dead, replace with dead. | 292 // If a node has only one control input and it is dead, replace with dead. |
407 Node* control = NodeProperties::GetControlInput(node); | 293 Node* control = NodeProperties::GetControlInput(node); |
408 if (control->opcode() == IrOpcode::kDead) { | 294 if (control->opcode() == IrOpcode::kDead) { |
409 TRACE("ControlDead: #%d:%s\n", node->id(), node->op()->mnemonic()); | 295 TRACE("ControlDead: #%d:%s\n", node->id(), node->op()->mnemonic()); |
410 return control; | 296 return Replace(control); |
411 } | 297 } |
412 } | 298 } |
413 | 299 |
| 300 Node* result = node; |
414 // Reduce branches, phis, and merges. | 301 // Reduce branches, phis, and merges. |
415 switch (node->opcode()) { | 302 switch (node->opcode()) { |
416 case IrOpcode::kBranch: | 303 case IrOpcode::kBranch: |
417 return ReduceBranch(node); | 304 result = ReduceBranch(node); |
| 305 break; |
418 case IrOpcode::kIfTrue: | 306 case IrOpcode::kIfTrue: |
419 return ReduceIfProjection(node, kTrue); | 307 result = ReduceIfProjection(node, kTrue); |
| 308 break; |
420 case IrOpcode::kIfFalse: | 309 case IrOpcode::kIfFalse: |
421 return ReduceIfProjection(node, kFalse); | 310 result = ReduceIfProjection(node, kFalse); |
422 case IrOpcode::kLoop: | 311 break; |
| 312 case IrOpcode::kLoop: // fallthrough |
423 case IrOpcode::kMerge: | 313 case IrOpcode::kMerge: |
424 return ReduceMerge(node); | 314 result = ReduceMerge(node); |
| 315 break; |
425 case IrOpcode::kSelect: | 316 case IrOpcode::kSelect: |
426 return ReduceSelect(node); | 317 result = ReduceSelect(node); |
| 318 break; |
427 case IrOpcode::kPhi: | 319 case IrOpcode::kPhi: |
428 case IrOpcode::kEffectPhi: | 320 case IrOpcode::kEffectPhi: |
429 return ReducePhi(node); | 321 result = ReducePhi(node); |
| 322 break; |
430 default: | 323 default: |
431 return node; | 324 break; |
432 } | 325 } |
| 326 |
| 327 return result == node ? NoChange() : Replace(result); |
433 } | 328 } |
434 | 329 |
435 // Try to statically fold a condition. | 330 // Try to statically fold a condition. |
436 Decision DecideCondition(Node* cond, bool recurse = true) { | 331 Decision DecideCondition(Node* cond, bool recurse = true) { |
437 switch (cond->opcode()) { | 332 switch (cond->opcode()) { |
438 case IrOpcode::kInt32Constant: | 333 case IrOpcode::kInt32Constant: |
439 return Int32Matcher(cond).Is(0) ? kFalse : kTrue; | 334 return Int32Matcher(cond).Is(0) ? kFalse : kTrue; |
440 case IrOpcode::kInt64Constant: | 335 case IrOpcode::kInt64Constant: |
441 return Int64Matcher(cond).Is(0) ? kFalse : kTrue; | 336 return Int64Matcher(cond).Is(0) ? kFalse : kTrue; |
442 case IrOpcode::kNumberConstant: | 337 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. | 431 if (live == 0) return dead(); // no remaining inputs. |
537 | 432 |
538 // Gather phis and effect phis to be edited. | 433 // Gather phis and effect phis to be edited. |
539 NodeVector phis(zone_); | 434 NodeVector phis(zone_); |
540 for (Node* const use : node->uses()) { | 435 for (Node* const use : node->uses()) { |
541 if (NodeProperties::IsPhi(use)) phis.push_back(use); | 436 if (NodeProperties::IsPhi(use)) phis.push_back(use); |
542 } | 437 } |
543 | 438 |
544 if (live == 1) { | 439 if (live == 1) { |
545 // All phis are redundant. Replace them with their live input. | 440 // All phis are redundant. Replace them with their live input. |
546 for (Node* const phi : phis) ReplaceNode(phi, phi->InputAt(live_index)); | 441 for (Node* const phi : phis) { |
| 442 Replace(phi, phi->InputAt(live_index)); |
| 443 } |
547 // The merge itself is redundant. | 444 // The merge itself is redundant. |
548 return node->InputAt(live_index); | 445 return node->InputAt(live_index); |
549 } | 446 } |
550 | 447 |
551 DCHECK_LE(2, live); | 448 DCHECK_LE(2, live); |
552 | 449 |
553 if (live < node->InputCount()) { | 450 if (live < node->InputCount()) { |
554 // Edit phis in place, removing dead inputs and revisiting them. | 451 // Edit phis in place, removing dead inputs and revisiting them. |
555 for (Node* const phi : phis) { | 452 for (Node* const phi : phis) { |
556 TRACE(" PhiInMerge: #%d:%s (%d live)\n", phi->id(), | 453 TRACE(" PhiInMerge: #%d:%s (%d live)\n", phi->id(), |
(...skipping 19 matching lines...) Expand all Loading... |
576 // No phis. Remove the branch altogether. | 473 // No phis. Remove the branch altogether. |
577 TRACE(" DeadDiamond: #%d:Branch #%d:IfTrue #%d:IfFalse\n", | 474 TRACE(" DeadDiamond: #%d:Branch #%d:IfTrue #%d:IfFalse\n", |
578 matcher.Branch()->id(), matcher.IfTrue()->id(), | 475 matcher.Branch()->id(), matcher.IfTrue()->id(), |
579 matcher.IfFalse()->id()); | 476 matcher.IfFalse()->id()); |
580 return control; | 477 return control; |
581 } else { | 478 } else { |
582 // A small number of phis. Replace with selects. | 479 // A small number of phis. Replace with selects. |
583 Node* cond = matcher.Branch()->InputAt(0); | 480 Node* cond = matcher.Branch()->InputAt(0); |
584 for (Node* phi : phis) { | 481 for (Node* phi : phis) { |
585 Node* select = graph()->NewNode( | 482 Node* select = graph()->NewNode( |
586 common_->Select(OpParameter<MachineType>(phi), | 483 common()->Select(OpParameter<MachineType>(phi), |
587 BranchHintOf(matcher.Branch()->op())), | 484 BranchHintOf(matcher.Branch()->op())), |
588 cond, matcher.TrueInputOf(phi), matcher.FalseInputOf(phi)); | 485 cond, matcher.TrueInputOf(phi), matcher.FalseInputOf(phi)); |
589 TRACE(" MatchSelect: #%d:Branch #%d:IfTrue #%d:IfFalse -> #%d\n", | 486 TRACE(" MatchSelect: #%d:Branch #%d:IfTrue #%d:IfFalse -> #%d\n", |
590 matcher.Branch()->id(), matcher.IfTrue()->id(), | 487 matcher.Branch()->id(), matcher.IfTrue()->id(), |
591 matcher.IfFalse()->id(), select->id()); | 488 matcher.IfFalse()->id(), select->id()); |
592 ReplaceNode(phi, select); | 489 Replace(phi, select); |
593 } | 490 } |
594 return control; | 491 return control; |
595 } | 492 } |
596 } | 493 } |
597 } | 494 } |
598 | 495 |
599 return node; | 496 return node; |
600 } | 497 } |
601 | 498 |
602 bool CheckPhisForSelect(const NodeVector& phis) { | 499 bool CheckPhisForSelect(const NodeVector& phis) { |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
634 } | 531 } |
635 // compact remaining inputs. | 532 // compact remaining inputs. |
636 int total = live; | 533 int total = live; |
637 for (int i = merge->InputCount(); i < node->InputCount(); i++) { | 534 for (int i = merge->InputCount(); i < node->InputCount(); i++) { |
638 if (total != i) node->ReplaceInput(total, node->InputAt(i)); | 535 if (total != i) node->ReplaceInput(total, node->InputAt(i)); |
639 total++; | 536 total++; |
640 } | 537 } |
641 DCHECK_EQ(total, live + node->InputCount() - merge->InputCount()); | 538 DCHECK_EQ(total, live + node->InputCount() - merge->InputCount()); |
642 DCHECK_NE(total, node->InputCount()); | 539 DCHECK_NE(total, node->InputCount()); |
643 node->TrimInputCount(total); | 540 node->TrimInputCount(total); |
644 node->set_op(common_->ResizeMergeOrPhi(node->op(), live)); | 541 node->set_op(common()->ResizeMergeOrPhi(node->op(), live)); |
645 } | 542 } |
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 }; | 543 }; |
662 | 544 |
663 | 545 |
664 void ControlReducer::ReduceGraph(Zone* zone, JSGraph* jsgraph, | 546 void ControlReducer::ReduceGraph(Zone* zone, JSGraph* jsgraph, |
665 CommonOperatorBuilder* common, | |
666 int max_phis_for_select) { | 547 int max_phis_for_select) { |
667 ControlReducerImpl impl(zone, jsgraph, common); | 548 GraphReducer graph_reducer(jsgraph->graph(), zone); |
| 549 ControlReducerImpl impl(&graph_reducer, zone, jsgraph); |
668 impl.max_phis_for_select_ = max_phis_for_select; | 550 impl.max_phis_for_select_ = max_phis_for_select; |
669 impl.Reduce(); | 551 graph_reducer.AddReducer(&impl); |
| 552 graph_reducer.ReduceGraph(); |
670 } | 553 } |
671 | 554 |
672 | 555 |
| 556 namespace { |
| 557 |
| 558 class DummyEditor final : public AdvancedReducer::Editor { |
| 559 public: |
| 560 void Replace(Node* node, Node* replacement) final { |
| 561 node->ReplaceUses(replacement); |
| 562 } |
| 563 void Revisit(Node* node) final {} |
| 564 }; |
| 565 |
| 566 } // namespace |
| 567 |
| 568 |
673 void ControlReducer::TrimGraph(Zone* zone, JSGraph* jsgraph) { | 569 void ControlReducer::TrimGraph(Zone* zone, JSGraph* jsgraph) { |
674 ControlReducerImpl impl(zone, jsgraph, NULL); | 570 DummyEditor editor; |
| 571 ControlReducerImpl impl(&editor, zone, jsgraph); |
675 impl.Trim(); | 572 impl.Trim(); |
676 } | 573 } |
677 | 574 |
678 | 575 |
679 Node* ControlReducer::ReduceMerge(JSGraph* jsgraph, | 576 Node* ControlReducer::ReduceMerge(JSGraph* jsgraph, Node* node, |
680 CommonOperatorBuilder* common, Node* node, | |
681 int max_phis_for_select) { | 577 int max_phis_for_select) { |
682 Zone zone; | 578 Zone zone; |
683 ControlReducerImpl impl(&zone, jsgraph, common); | 579 DummyEditor editor; |
| 580 ControlReducerImpl impl(&editor, &zone, jsgraph); |
684 impl.max_phis_for_select_ = max_phis_for_select; | 581 impl.max_phis_for_select_ = max_phis_for_select; |
685 return impl.ReduceMerge(node); | 582 return impl.ReduceMerge(node); |
686 } | 583 } |
687 | 584 |
688 | 585 |
689 Node* ControlReducer::ReducePhiForTesting(JSGraph* jsgraph, | 586 Node* ControlReducer::ReducePhiForTesting(JSGraph* jsgraph, Node* node) { |
690 CommonOperatorBuilder* common, | |
691 Node* node) { | |
692 Zone zone; | 587 Zone zone; |
693 ControlReducerImpl impl(&zone, jsgraph, common); | 588 DummyEditor editor; |
| 589 ControlReducerImpl impl(&editor, &zone, jsgraph); |
694 return impl.ReducePhi(node); | 590 return impl.ReducePhi(node); |
695 } | 591 } |
696 | 592 |
697 | 593 |
698 Node* ControlReducer::ReduceIfNodeForTesting(JSGraph* jsgraph, | 594 Node* ControlReducer::ReduceIfNodeForTesting(JSGraph* jsgraph, Node* node) { |
699 CommonOperatorBuilder* common, | |
700 Node* node) { | |
701 Zone zone; | 595 Zone zone; |
702 ControlReducerImpl impl(&zone, jsgraph, common); | 596 DummyEditor editor; |
| 597 ControlReducerImpl impl(&editor, &zone, jsgraph); |
703 switch (node->opcode()) { | 598 switch (node->opcode()) { |
704 case IrOpcode::kIfTrue: | 599 case IrOpcode::kIfTrue: |
705 return impl.ReduceIfProjection(node, kTrue); | 600 return impl.ReduceIfProjection(node, kTrue); |
706 case IrOpcode::kIfFalse: | 601 case IrOpcode::kIfFalse: |
707 return impl.ReduceIfProjection(node, kFalse); | 602 return impl.ReduceIfProjection(node, kFalse); |
708 default: | 603 default: |
709 return node; | 604 return node; |
710 } | 605 } |
711 } | 606 } |
712 } | 607 |
713 } | 608 } // namespace compiler |
714 } // namespace v8::internal::compiler | 609 } // namespace internal |
| 610 } // namespace v8 |
OLD | NEW |