| 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 |