Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 the V8 project authors. All rights reserved. | 1 // Copyright 2014 the V8 project authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "src/compiler/common-operator.h" | 5 #include "src/compiler/common-operator.h" |
| 6 #include "src/compiler/control-reducer.h" | 6 #include "src/compiler/control-reducer.h" |
| 7 #include "src/compiler/graph.h" | 7 #include "src/compiler/graph.h" |
| 8 #include "src/compiler/js-graph.h" | 8 #include "src/compiler/js-graph.h" |
| 9 #include "src/compiler/node-matchers.h" | 9 #include "src/compiler/node-matchers.h" |
| 10 #include "src/compiler/node-properties-inl.h" | 10 #include "src/compiler/node-properties-inl.h" |
| 11 #include "src/zone-containers.h" | 11 #include "src/zone-containers.h" |
| 12 | 12 |
| 13 namespace v8 { | 13 namespace v8 { |
| 14 namespace internal { | 14 namespace internal { |
| 15 namespace compiler { | 15 namespace compiler { |
| 16 | 16 |
| 17 enum VisitState { kUnvisited, kOnStack, kRevisit, kVisited }; | 17 enum VisitState { kUnvisited = 0, kOnStack = 1, kRevisit = 2, kVisited = 3 }; |
| 18 enum Reachability { kFromStart = 8 }; | |
| 18 | 19 |
| 19 #define TRACE(x) \ | 20 #define TRACE(x) \ |
| 20 if (FLAG_trace_turbo) PrintF x | 21 if (FLAG_trace_turbo) PrintF x |
| 21 | 22 |
| 22 class ControlReducerImpl { | 23 class ControlReducerImpl { |
| 23 public: | 24 public: |
| 24 ControlReducerImpl(Zone* zone, JSGraph* jsgraph, | 25 ControlReducerImpl(Zone* zone, JSGraph* jsgraph, |
| 25 CommonOperatorBuilder* common) | 26 CommonOperatorBuilder* common) |
| 26 : zone_(zone), | 27 : zone_(zone), |
| 27 jsgraph_(jsgraph), | 28 jsgraph_(jsgraph), |
| 28 common_(common), | 29 common_(common), |
| 29 state_(jsgraph->graph()->NodeCount(), kUnvisited, zone_), | 30 state_(jsgraph->graph()->NodeCount(), kUnvisited, zone_), |
| 30 stack_(zone_), | 31 stack_(zone_), |
| 31 revisit_(zone_), | 32 revisit_(zone_), |
| 32 dead_(NULL) {} | 33 dead_(NULL) {} |
| 33 | 34 |
| 34 Zone* zone_; | 35 Zone* zone_; |
| 35 JSGraph* jsgraph_; | 36 JSGraph* jsgraph_; |
| 36 CommonOperatorBuilder* common_; | 37 CommonOperatorBuilder* common_; |
| 37 ZoneVector<VisitState> state_; | 38 ZoneVector<VisitState> state_; |
| 38 ZoneDeque<Node*> stack_; | 39 ZoneDeque<Node*> stack_; |
| 39 ZoneDeque<Node*> revisit_; | 40 ZoneDeque<Node*> revisit_; |
| 40 Node* dead_; | 41 Node* dead_; |
| 41 | 42 |
| 42 void Trim() { | 43 void Reduce() { |
| 43 // Mark all nodes reachable from end. | 44 Push(graph()->end()); |
| 45 do { | |
| 46 // Process the node on the top of the stack, potentially pushing more | |
| 47 // or popping the node off the stack. | |
| 48 ReduceTop(); | |
| 49 // If the stack becomes empty, revisit any nodes in the revisit queue. | |
| 50 // If no nodes in the revisit queue, try removing dead loops. | |
| 51 // If no dead loops, then finish. | |
| 52 } while (!stack_.empty() || TryRevisit() || RepairAndRemoveLoops()); | |
| 53 } | |
| 54 | |
| 55 bool TryRevisit() { | |
| 56 while (!revisit_.empty()) { | |
| 57 Node* n = revisit_.back(); | |
| 58 revisit_.pop_back(); | |
| 59 if (state_[n->id()] == kRevisit) { // state can change while in queue. | |
| 60 Push(n); | |
| 61 return true; | |
| 62 } | |
| 63 } | |
| 64 return false; | |
| 65 } | |
| 66 | |
| 67 // Repair the graph after the possible creation of non-terminating or dead | |
| 68 // loops. Removing dead loops can produce more opportunities for reduction. | |
| 69 bool RepairAndRemoveLoops() { | |
| 70 // TODO(turbofan): we can skip this if the graph has no loops, but | |
| 71 // we have to be careful about proper loop detection during reduction. | |
| 72 | |
| 73 // Gather all nodes backwards-reachable from end (through inputs). | |
| 74 state_.assign(graph()->NodeCount(), kUnvisited); | |
| 44 NodeVector nodes(zone_); | 75 NodeVector nodes(zone_); |
| 45 state_.assign(jsgraph_->graph()->NodeCount(), kUnvisited); | 76 AddNodesReachableFromEnd(nodes); |
| 46 Push(jsgraph_->graph()->end()); | 77 |
| 78 // Walk forward through control nodes, looking for back edges to nodes | |
| 79 // that are not connected to end. Those are non-terminating loops (NTLs). | |
| 80 Node* start = graph()->start(); | |
| 81 ZoneVector<byte> fw_reachability(graph()->NodeCount(), 0, zone_); | |
| 82 fw_reachability[start->id()] = kFromStart | kOnStack; | |
| 83 stack_.push_back(start); | |
| 84 | |
| 47 while (!stack_.empty()) { | 85 while (!stack_.empty()) { |
| 48 Node* node = stack_[stack_.size() - 1]; | 86 Node* node = stack_.back(); |
| 49 stack_.pop_back(); | 87 TRACE(("ControlFw: #%d:%s\n", node->id(), node->op()->mnemonic())); |
| 50 state_[node->id()] = kVisited; | 88 bool pop = true; |
| 51 nodes.push_back(node); | 89 for (UseIter i = node->uses().begin(); i != node->uses().end(); ++i) { |
| 90 Node* succ = *i; | |
| 91 byte reach = fw_reachability[succ->id()]; | |
| 92 if ((reach & kOnStack) != 0 && state_[succ->id()] != kVisited) { | |
| 93 // {succ} is on stack and not reachable from end. | |
| 94 ConnectNTL(nodes, succ); | |
| 95 fw_reachability.resize(graph()->NodeCount(), 0); | |
| 96 pop = false; // continue traversing inputs to this node. | |
| 97 break; | |
| 98 } | |
| 99 if ((reach & kFromStart) == 0 && | |
| 100 IrOpcode::IsControlOpcode(succ->opcode())) { | |
| 101 // {succ} is a control node and not yet reached from start. | |
| 102 fw_reachability[succ->id()] |= kFromStart | kOnStack; | |
| 103 stack_.push_back(succ); | |
| 104 pop = false; // "recurse" into successor control node. | |
| 105 break; | |
| 106 } | |
| 107 } | |
| 108 if (pop) { | |
| 109 fw_reachability[node->id()] &= ~kOnStack; | |
| 110 stack_.pop_back(); | |
| 111 } | |
| 112 } | |
| 113 | |
| 114 // Trim references from dead nodes to live nodes first. | |
| 115 jsgraph_->GetCachedNodes(&nodes); | |
| 116 TrimNodes(nodes); | |
| 117 | |
| 118 // Any control nodes not reachable from start are dead, even loops. | |
| 119 for (size_t i = 0; i < nodes.size(); i++) { | |
| 120 Node* node = nodes[i]; | |
| 121 byte reach = fw_reachability[node->id()]; | |
| 122 if ((reach & kFromStart) == 0 && | |
| 123 IrOpcode::IsControlOpcode(node->opcode())) { | |
| 124 ReplaceNode(node, dead()); // uses will be added to revisit queue. | |
| 125 } | |
| 126 } | |
| 127 return TryRevisit(); // try to push a node onto the stack. | |
| 128 } | |
| 129 | |
| 130 // Connect {loop}, the header of a non-terminating loop, to the end node. | |
| 131 void ConnectNTL(NodeVector& nodes, Node* loop) { | |
| 132 TRACE(("ConnectNTL: #%d:%s\n", loop->id(), loop->op()->mnemonic())); | |
| 133 CHECK_EQ(IrOpcode::kLoop, loop->opcode()); | |
| 134 Node* to_add = loop; | |
| 135 Node* end = graph()->end(); | |
| 136 CHECK_EQ(IrOpcode::kEnd, end->opcode()); | |
| 137 Node* merge = end->InputAt(0); | |
| 138 if (merge->opcode() != IrOpcode::kMerge) { | |
| 139 if (merge->opcode() == IrOpcode::kDead) { | |
| 140 // end died; just connect end to the loop. | |
| 141 end->ReplaceInput(0, loop); | |
| 142 to_add = loop; | |
| 143 } else { | |
| 144 // Introduce a new merge for the end. | |
| 145 merge = graph()->NewNode(common_->Merge(2), merge, loop); | |
| 146 state_.resize(graph()->NodeCount(), kUnvisited); | |
| 147 end->ReplaceInput(0, merge); | |
| 148 to_add = merge; | |
| 149 } | |
| 150 } else { | |
| 151 // Append a new input to the final merge at the end. | |
| 152 merge->AppendInput(graph()->zone(), loop); | |
| 153 merge->set_op(common_->Merge(merge->InputCount())); | |
| 154 } | |
| 155 nodes.push_back(to_add); | |
| 156 state_[to_add->id()] = kVisited; | |
| 157 AddBackwardsReachableNodes(nodes, nodes.size() - 1); | |
| 158 } | |
| 159 | |
| 160 void AddNodesReachableFromEnd(NodeVector& nodes) { | |
| 161 Node* end = graph()->end(); | |
| 162 if (!end->IsDead()) { | |
| 163 state_[end->id()] = kVisited; | |
| 164 nodes.push_back(end); | |
| 165 AddBackwardsReachableNodes(nodes, nodes.size() - 1); | |
| 166 } | |
| 167 } | |
| 168 | |
| 169 void AddBackwardsReachableNodes(NodeVector& nodes, size_t cursor) { | |
| 170 while (cursor < nodes.size()) { | |
| 171 Node* node = nodes[cursor++]; | |
| 52 for (InputIter i = node->inputs().begin(); i != node->inputs().end(); | 172 for (InputIter i = node->inputs().begin(); i != node->inputs().end(); |
| 53 ++i) { | 173 ++i) { |
| 54 Recurse(*i); // pushes node onto the stack if necessary. | 174 Node* input = *i; |
| 175 if (state_[input->id()] != kVisited) { | |
| 176 state_[input->id()] = kVisited; | |
| 177 nodes.push_back(input); | |
| 178 } | |
| 55 } | 179 } |
| 56 } | 180 } |
| 181 } | |
| 182 | |
| 183 void Trim() { | |
| 184 // Gather all nodes backwards-reachable from end through inputs. | |
| 185 state_.assign(graph()->NodeCount(), kUnvisited); | |
| 186 NodeVector nodes(zone_); | |
| 187 AddNodesReachableFromEnd(nodes); | |
| 188 | |
| 57 // Process cached nodes in the JSGraph too. | 189 // Process cached nodes in the JSGraph too. |
| 58 jsgraph_->GetCachedNodes(&nodes); | 190 jsgraph_->GetCachedNodes(&nodes); |
| 191 TrimNodes(nodes); | |
| 192 } | |
| 193 | |
| 194 void TrimNodes(NodeVector& nodes) { | |
| 59 // Remove dead->live edges. | 195 // Remove dead->live edges. |
| 60 for (size_t j = 0; j < nodes.size(); j++) { | 196 for (size_t j = 0; j < nodes.size(); j++) { |
| 61 Node* node = nodes[j]; | 197 Node* node = nodes[j]; |
| 62 for (UseIter i = node->uses().begin(); i != node->uses().end();) { | 198 for (UseIter i = node->uses().begin(); i != node->uses().end();) { |
| 63 size_t id = static_cast<size_t>((*i)->id()); | 199 size_t id = static_cast<size_t>((*i)->id()); |
| 64 if (state_[id] != kVisited) { | 200 if (state_[id] != kVisited) { |
| 65 TRACE(("DeadLink: #%d:%s(%d) -> #%d:%s\n", (*i)->id(), | 201 TRACE(("DeadLink: #%d:%s(%d) -> #%d:%s\n", (*i)->id(), |
| 66 (*i)->op()->mnemonic(), i.index(), node->id(), | 202 (*i)->op()->mnemonic(), i.index(), node->id(), |
| 67 node->op()->mnemonic())); | 203 node->op()->mnemonic())); |
| 68 i.UpdateToAndIncrement(NULL); | 204 i.UpdateToAndIncrement(NULL); |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 80 CHECK_NE(NULL, *i); | 216 CHECK_NE(NULL, *i); |
| 81 } | 217 } |
| 82 for (UseIter i = node->uses().begin(); i != node->uses().end(); ++i) { | 218 for (UseIter i = node->uses().begin(); i != node->uses().end(); ++i) { |
| 83 size_t id = static_cast<size_t>((*i)->id()); | 219 size_t id = static_cast<size_t>((*i)->id()); |
| 84 CHECK_EQ(kVisited, state_[id]); | 220 CHECK_EQ(kVisited, state_[id]); |
| 85 } | 221 } |
| 86 } | 222 } |
| 87 #endif | 223 #endif |
| 88 } | 224 } |
| 89 | 225 |
| 226 // Reduce the node on the top of the stack. | |
| 227 // If an input {i} is not yet visited or needs to be revisited, push {i} onto | |
| 228 // the stack and return. Otherwise, all inputs are visited, so apply | |
| 229 // reductions for {node} and pop it off the stack. | |
| 230 void ReduceTop() { | |
| 231 int height = stack_.size(); | |
| 232 Node* node = stack_.back(); | |
| 233 | |
| 234 if (node->IsDead()) return Pop(); // Node was killed while on stack. | |
| 235 | |
| 236 TRACE(("ControlReduce: #%d:%s\n", node->id(), node->op()->mnemonic())); | |
| 237 | |
| 238 // Recurse on an input if necessary. | |
| 239 for (InputIter i = node->inputs().begin(); i != node->inputs().end(); ++i) { | |
|
Benedikt Meurer
2014/10/23 10:45:25
for (Node* const input : node->inputs()) if (Recur
titzer
2014/10/23 11:23:44
Done.
| |
| 240 if (Recurse(*i)) return; | |
| 241 } | |
| 242 | |
| 243 // All inputs should be visited or on stack. Apply reductions to node. | |
| 244 Node* replacement = ReduceNode(node); | |
| 245 if (replacement != node) ReplaceNode(node, replacement); | |
| 246 | |
| 247 // After reducing the node, pop it off the stack. | |
| 248 CHECK_EQ(height, stack_.size()); | |
|
Benedikt Meurer
2014/10/23 10:45:25
Windows builder will probably complain about this
titzer
2014/10/23 11:23:44
Fixed with static_cast
| |
| 249 Pop(); | |
| 250 | |
| 251 // If there was a replacement, reduce it after popping {node}. | |
| 252 if (replacement != node) Recurse(replacement); | |
| 253 } | |
| 254 | |
| 90 // Push a node onto the stack if its state is {kUnvisited} or {kRevisit}. | 255 // Push a node onto the stack if its state is {kUnvisited} or {kRevisit}. |
| 91 bool Recurse(Node* node) { | 256 bool Recurse(Node* node) { |
| 92 size_t id = static_cast<size_t>(node->id()); | 257 size_t id = static_cast<size_t>(node->id()); |
| 93 if (id < state_.size()) { | 258 if (id < state_.size()) { |
| 94 if (state_[id] != kRevisit && state_[id] != kUnvisited) return false; | 259 if (state_[id] != kRevisit && state_[id] != kUnvisited) return false; |
| 95 } else { | 260 } else { |
| 96 state_.resize((3 * id) / 2, kUnvisited); | 261 state_.resize((3 * id) / 2, kUnvisited); |
| 97 } | 262 } |
| 98 Push(node); | 263 Push(node); |
| 99 return true; | 264 return true; |
| 100 } | 265 } |
| 101 | 266 |
| 102 void Push(Node* node) { | 267 void Push(Node* node) { |
| 103 state_[node->id()] = kOnStack; | 268 state_[node->id()] = kOnStack; |
| 104 stack_.push_back(node); | 269 stack_.push_back(node); |
| 105 } | 270 } |
| 271 | |
| 272 void Pop() { | |
| 273 int pos = stack_.size() - 1; | |
| 274 DCHECK_GE(pos, 0); | |
| 275 DCHECK_EQ(kOnStack, state_[stack_[pos]->id()]); | |
| 276 state_[stack_[pos]->id()] = kVisited; | |
| 277 stack_.pop_back(); | |
| 278 } | |
| 279 | |
| 280 // Queue a node to be revisited if it has been visited once already. | |
| 281 void Revisit(Node* node) { | |
| 282 size_t id = static_cast<size_t>(node->id()); | |
| 283 if (id < state_.size() && state_[id] == kVisited) { | |
| 284 TRACE((" Revisit #%d:%s\n", node->id(), node->op()->mnemonic())); | |
| 285 state_[id] = kRevisit; | |
| 286 revisit_.push_back(node); | |
| 287 } | |
| 288 } | |
| 289 | |
| 290 Node* dead() { | |
| 291 if (dead_ == NULL) dead_ = graph()->NewNode(common_->Dead()); | |
| 292 return dead_; | |
| 293 } | |
| 294 | |
| 295 //=========================================================================== | |
| 296 // Reducer implementation: perform reductions on a node. | |
| 297 //=========================================================================== | |
| 298 Node* ReduceNode(Node* node) { | |
| 299 if (OperatorProperties::GetControlInputCount(node->op()) == 1) { | |
| 300 // If a node has only one control input and it is dead, replace with dead. | |
| 301 Node* control = NodeProperties::GetControlInput(node); | |
| 302 if (control->opcode() == IrOpcode::kDead) { | |
| 303 TRACE(("ControlDead: #%d:%s\n", node->id(), node->op()->mnemonic())); | |
| 304 return control; | |
| 305 } | |
| 306 } | |
| 307 | |
| 308 // Reduce branches, phis, and merges. | |
| 309 switch (node->opcode()) { | |
| 310 case IrOpcode::kBranch: | |
| 311 return ReduceBranch(node); | |
| 312 case IrOpcode::kLoop: | |
| 313 case IrOpcode::kMerge: | |
| 314 return ReduceMerge(node); | |
| 315 case IrOpcode::kPhi: | |
| 316 case IrOpcode::kEffectPhi: | |
| 317 return ReducePhi(node); | |
| 318 default: | |
| 319 return node; | |
| 320 } | |
| 321 } | |
| 322 | |
| 323 // Reduce redundant phis. | |
| 324 Node* ReducePhi(Node* node) { | |
| 325 int n = node->InputCount(); | |
| 326 DCHECK_EQ(1, OperatorProperties::GetControlInputCount(node->op())); | |
| 327 if (node->opcode() == IrOpcode::kPhi) { | |
| 328 DCHECK_EQ(n, 1 + OperatorProperties::GetValueInputCount(node->op())); | |
| 329 } | |
| 330 if (node->opcode() == IrOpcode::kEffectPhi) { | |
| 331 DCHECK_EQ(n, 1 + OperatorProperties::GetEffectInputCount(node->op())); | |
| 332 } | |
| 333 if (n <= 1) return dead(); // No non-control inputs. | |
| 334 if (n == 2) return node->InputAt(0); // Only one non-control input. | |
| 335 | |
| 336 Node* replacement = NULL; | |
| 337 Node::Inputs inputs = node->inputs(); | |
| 338 for (InputIter it = inputs.begin(); n > 1; --n, ++it) { | |
| 339 Node* input = *it; | |
| 340 if (input->opcode() == IrOpcode::kDead) continue; // ignore dead inputs. | |
| 341 if (input != node && input != replacement) { // non-redundant input. | |
| 342 if (replacement != NULL) return node; | |
| 343 replacement = input; | |
| 344 } | |
| 345 } | |
| 346 return replacement == NULL ? dead() : replacement; | |
| 347 } | |
| 348 | |
| 349 // Reduce merges by trimming away dead inputs from the merge and phis. | |
| 350 Node* ReduceMerge(Node* node) { | |
| 351 // Count the number of live inputs. | |
| 352 int live = 0; | |
| 353 int live_index = 0; | |
| 354 for (InputIter i = node->inputs().begin(); i != node->inputs().end(); ++i) { | |
| 355 if ((*i)->opcode() != IrOpcode::kDead) { | |
| 356 live++; | |
| 357 live_index = i.index(); | |
| 358 } | |
| 359 } | |
| 360 | |
| 361 if (live > 1 && live == node->InputCount()) return node; // nothing to do. | |
| 362 | |
| 363 TRACE(("ReduceMerge: #%d:%s (%d live)\n", node->id(), | |
| 364 node->op()->mnemonic(), live)); | |
| 365 | |
| 366 if (live == 0) return dead(); // no remaining inputs. | |
| 367 | |
| 368 // Gather phis and effect phis to be edited. | |
| 369 ZoneDeque<Node*> phis(zone_); | |
|
Benedikt Meurer
2014/10/23 10:45:25
Hm, I'd like to have a ZoneStack here, i.e. a clas
titzer
2014/10/23 11:23:44
Order doesn't really matter here, so I could as we
| |
| 370 for (UseIter i = node->uses().begin(); i != node->uses().end(); ++i) { | |
|
Benedikt Meurer
2014/10/23 10:45:25
for (Node* const to : node->uses()) { ... }
titzer
2014/10/23 11:23:44
Done.
| |
| 371 Node* to = *i; | |
| 372 if (to->opcode() == IrOpcode::kPhi || | |
| 373 to->opcode() == IrOpcode::kEffectPhi) { | |
| 374 phis.push_back(to); | |
| 375 } | |
| 376 } | |
| 377 | |
| 378 // Remove dead inputs from merge and corresponding phis. | |
| 379 while (!phis.empty()) { | |
| 380 Node* phi = phis.back(); | |
| 381 phis.pop_back(); | |
| 382 TRACE((" PhiInMerge: #%d:%s (%d live)\n", phi->id(), | |
| 383 phi->op()->mnemonic(), live)); | |
| 384 if (live == 1) { | |
| 385 Node* result = phi->InputAt(live_index); | |
| 386 ReplaceNode(phi, result); // phi is redundant. | |
| 387 } else { | |
| 388 RemoveDeadInputs(node, phi); | |
| 389 Revisit(phi); // re-reduce every phi. | |
| 390 } | |
| 391 } | |
| 392 | |
| 393 // If only one input is remaining, remove the merge altogether. | |
| 394 if (live == 1) return node->InputAt(live_index); | |
| 395 RemoveDeadInputs(node, node); | |
| 396 return node; | |
| 397 } | |
| 398 | |
| 399 // Reduce branches with constant inputs. | |
| 400 Node* ReduceBranch(Node* node) { | |
| 401 Node* cond = node->InputAt(0); | |
| 402 bool is_true; | |
| 403 switch (cond->opcode()) { | |
| 404 case IrOpcode::kInt32Constant: | |
| 405 is_true = !Int32Matcher(cond).Is(0); | |
| 406 break; | |
| 407 case IrOpcode::kNumberConstant: | |
| 408 is_true = !NumberMatcher(cond).Is(0); | |
| 409 break; | |
| 410 case IrOpcode::kHeapConstant: { | |
| 411 Handle<Object> object = | |
| 412 HeapObjectMatcher<Object>(cond).Value().handle(); | |
| 413 if (object->IsTrue()) | |
| 414 is_true = true; | |
| 415 else if (object->IsFalse()) | |
| 416 is_true = false; | |
| 417 else | |
| 418 return node; // TODO(turbofan): fold branches on strings, objects. | |
| 419 break; | |
| 420 } | |
| 421 default: | |
| 422 return node; | |
| 423 } | |
| 424 | |
| 425 TRACE(("BranchReduce: #%d:%s = %s\n", node->id(), node->op()->mnemonic(), | |
| 426 is_true ? "true" : "false")); | |
| 427 | |
| 428 // Replace IfTrue and IfFalse projections from this branch. | |
| 429 Node* control = NodeProperties::GetControlInput(node); | |
| 430 for (UseIter i = node->uses().begin(); i != node->uses().end();) { | |
| 431 Node* to = *i; | |
| 432 if (to->opcode() == IrOpcode::kIfTrue) { | |
| 433 TRACE((" IfTrue: #%d:%s\n", to->id(), to->op()->mnemonic())); | |
| 434 i.UpdateToAndIncrement(NULL); | |
| 435 ReplaceNode(to, is_true ? control : dead()); | |
| 436 } else if (to->opcode() == IrOpcode::kIfFalse) { | |
| 437 TRACE((" IfFalse: #%d:%s\n", to->id(), to->op()->mnemonic())); | |
| 438 i.UpdateToAndIncrement(NULL); | |
| 439 ReplaceNode(to, is_true ? dead() : control); | |
| 440 } else { | |
| 441 ++i; | |
| 442 } | |
| 443 } | |
| 444 return control; | |
| 445 } | |
| 446 | |
| 447 // Remove inputs to {node} corresponding to the dead inputs to {merge} | |
| 448 // and compact the remaining inputs, updating the operator. | |
| 449 void RemoveDeadInputs(Node* merge, Node* node) { | |
| 450 int pos = 0; | |
| 451 for (int i = 0; i < node->InputCount(); i++) { | |
| 452 // skip dead inputs. | |
| 453 if (i < merge->InputCount() && | |
| 454 merge->InputAt(i)->opcode() == IrOpcode::kDead) | |
| 455 continue; | |
| 456 // compact live inputs. | |
| 457 if (pos != i) node->ReplaceInput(pos, node->InputAt(i)); | |
| 458 pos++; | |
| 459 } | |
| 460 node->TrimInputCount(pos); | |
| 461 if (node->opcode() == IrOpcode::kPhi) { | |
| 462 node->set_op(common_->Phi(OpParameter<MachineType>(node->op()), pos - 1)); | |
| 463 } else if (node->opcode() == IrOpcode::kEffectPhi) { | |
| 464 node->set_op(common_->EffectPhi(pos - 1)); | |
| 465 } else if (node->opcode() == IrOpcode::kMerge) { | |
| 466 node->set_op(common_->Merge(pos)); | |
| 467 } else if (node->opcode() == IrOpcode::kLoop) { | |
| 468 node->set_op(common_->Loop(pos)); | |
| 469 } else { | |
| 470 UNREACHABLE(); | |
| 471 } | |
| 472 } | |
| 473 | |
| 474 void ReplaceNode(Node* node, Node* replacement) { | |
| 475 if (node == replacement) return; | |
| 476 TRACE((" Replace: #%d:%s with #%d:%s\n", node->id(), | |
| 477 node->op()->mnemonic(), replacement->id(), | |
| 478 replacement->op()->mnemonic())); | |
| 479 for (UseIter i = node->uses().begin(); i != node->uses().end(); ++i) { | |
| 480 // Don't revisit this node if it refers to itself. | |
| 481 if (*i != node) Revisit(*i); | |
| 482 } | |
| 483 node->ReplaceUses(replacement); | |
| 484 node->Kill(); | |
| 485 } | |
| 486 | |
| 487 Graph* graph() { return jsgraph_->graph(); } | |
| 106 }; | 488 }; |
| 107 | 489 |
| 490 | |
| 108 void ControlReducer::ReduceGraph(Zone* zone, JSGraph* jsgraph, | 491 void ControlReducer::ReduceGraph(Zone* zone, JSGraph* jsgraph, |
| 109 CommonOperatorBuilder* common) { | 492 CommonOperatorBuilder* common) { |
| 110 ControlReducerImpl impl(zone, jsgraph, NULL); | 493 ControlReducerImpl impl(zone, jsgraph, common); |
| 111 // Only trim the graph for now. Control reduction can reduce non-terminating | 494 // Only trim the graph for now. Control reduction can reduce non-terminating |
| 112 // loops to graphs that are unschedulable at the moment. | 495 // loops to graphs that are unschedulable at the moment. |
| 496 impl.Reduce(); | |
| 113 impl.Trim(); | 497 impl.Trim(); |
| 114 } | 498 } |
| 115 | 499 |
| 116 | 500 |
| 117 void ControlReducer::TrimGraph(Zone* zone, JSGraph* jsgraph) { | 501 void ControlReducer::TrimGraph(Zone* zone, JSGraph* jsgraph) { |
| 118 ControlReducerImpl impl(zone, jsgraph, NULL); | 502 ControlReducerImpl impl(zone, jsgraph, NULL); |
| 119 impl.Trim(); | 503 impl.Trim(); |
| 120 } | 504 } |
| 505 | |
| 506 | |
| 507 Node* ControlReducer::ReducePhiForTesting(JSGraph* jsgraph, | |
| 508 CommonOperatorBuilder* common, | |
| 509 Node* node) { | |
| 510 Zone zone(jsgraph->graph()->zone()->isolate()); | |
| 511 ControlReducerImpl impl(&zone, jsgraph, common); | |
| 512 return impl.ReducePhi(node); | |
| 513 } | |
| 514 | |
| 515 | |
| 516 Node* ControlReducer::ReduceMergeForTesting(JSGraph* jsgraph, | |
| 517 CommonOperatorBuilder* common, | |
| 518 Node* node) { | |
| 519 Zone zone(jsgraph->graph()->zone()->isolate()); | |
| 520 ControlReducerImpl impl(&zone, jsgraph, common); | |
| 521 return impl.ReduceMerge(node); | |
| 522 } | |
| 523 | |
| 524 | |
| 525 Node* ControlReducer::ReduceBranchForTesting(JSGraph* jsgraph, | |
| 526 CommonOperatorBuilder* common, | |
| 527 Node* node) { | |
| 528 Zone zone(jsgraph->graph()->zone()->isolate()); | |
| 529 ControlReducerImpl impl(&zone, jsgraph, common); | |
| 530 return impl.ReduceBranch(node); | |
| 531 } | |
| 121 } | 532 } |
| 122 } | 533 } |
| 123 } // namespace v8::internal::compiler | 534 } // namespace v8::internal::compiler |
| OLD | NEW |