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