| OLD | NEW | 
|---|
| 1 // Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file | 
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a | 
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. | 
| 4 | 4 | 
| 5 library dart2js.cps_ir.let_sinking; | 5 library dart2js.cps_ir.let_sinking; | 
| 6 | 6 | 
| 7 import 'cps_ir_nodes.dart'; | 7 import 'cps_ir_nodes.dart'; | 
| 8 import 'optimizers.dart'; | 8 import 'optimizers.dart'; | 
|  | 9 import 'loop_hierarchy.dart'; | 
| 9 | 10 | 
| 10 /// Sinks single-use primitives to the use when this is safe and profitable. | 11 /// Sinks single-use primitives to the use when this is safe and profitable. | 
| 11 /// | 12 /// | 
| 12 /// To avoid sinking non-constant primitives into loops, this pass performs a | 13 /// To avoid sinking non-constant primitives into loops, this pass performs a | 
| 13 /// control-flow analysis to determine the effective nesting of loops. | 14 /// control-flow analysis to determine the effective nesting of loops. | 
| 14 /// | 15 /// | 
| 15 /// In the example below, the value 'p' can be sunk to its use site in the | 16 /// In the example below, the value 'p' can be sunk to its use site in the | 
| 16 /// 'else' branch because that branch is not effectively part of a loop, | 17 /// 'else' branch because that branch is not effectively part of a loop, | 
| 17 /// despite being lexically nested in a recursive continuation. | 18 /// despite being lexically nested in a recursive continuation. | 
| 18 /// | 19 /// | 
| (...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 109   } | 110   } | 
| 110 | 111 | 
| 111   Expression getEnclosingExpression(Node node) { | 112   Expression getEnclosingExpression(Node node) { | 
| 112     while (node is! Expression) { | 113     while (node is! Expression) { | 
| 113       node = node.parent; | 114       node = node.parent; | 
| 114     } | 115     } | 
| 115     return node; | 116     return node; | 
| 116   } | 117   } | 
| 117 } | 118 } | 
| 118 | 119 | 
| 119 /// Determines the effective nesting of loops. |  | 
| 120 /// |  | 
| 121 /// The effective nesting of loops is different from the lexical nesting, since |  | 
| 122 /// recursive continuations can generally contain all the code following |  | 
| 123 /// after the loop in addition to the looping code itself. |  | 
| 124 /// |  | 
| 125 /// For example, the 'else' branch below is not effectively part of the loop: |  | 
| 126 /// |  | 
| 127 ///   let rec kont x = |  | 
| 128 ///     if (<loop condition>) |  | 
| 129 ///       <loop body> |  | 
| 130 ///       InvokeContinuation kont x' |  | 
| 131 ///     else |  | 
| 132 ///       <after loop> |  | 
| 133 ///       return p.foo() |  | 
| 134 /// |  | 
| 135 /// We use the term "loop" to mean recursive continuation. |  | 
| 136 /// The `null` value is used to represent a context not part of any loop. |  | 
| 137 class LoopHierarchy { |  | 
| 138   /// Nesting depth of the given loop. |  | 
| 139   Map<Continuation, int> loopDepth = <Continuation, int>{}; |  | 
| 140 |  | 
| 141   /// The innermost loop (other than itself) that may be invoked recursively |  | 
| 142   /// as a result of invoking the given continuation. |  | 
| 143   Map<Continuation, Continuation> loopTarget = <Continuation, Continuation>{}; |  | 
| 144 |  | 
| 145   /// Current nesting depth. |  | 
| 146   int currentDepth = 0; |  | 
| 147 |  | 
| 148   /// Computes the loop hierarchy for the given function. |  | 
| 149   /// |  | 
| 150   /// Parent pointers must be computed for [node]. |  | 
| 151   LoopHierarchy(FunctionDefinition node) { |  | 
| 152     _processBlock(node.body, null); |  | 
| 153   } |  | 
| 154 |  | 
| 155   /// Returns the innermost loop which [cont] is effectively part of. |  | 
| 156   Continuation getLoopHeader(Continuation cont) { |  | 
| 157     return cont.isRecursive ? cont : loopTarget[cont]; |  | 
| 158   } |  | 
| 159 |  | 
| 160   /// Marks the innermost loop as a subloop of the other loop. |  | 
| 161   /// |  | 
| 162   /// Returns the innermost loop. |  | 
| 163   /// |  | 
| 164   /// Both continuations, [c1] and [c2] may be null (i.e. no loop). |  | 
| 165   /// |  | 
| 166   /// A loop is said to be a subloop of an enclosing loop if it can invoke |  | 
| 167   /// that loop recursively. This information is stored in [loopTarget]. |  | 
| 168   /// |  | 
| 169   /// This method is only invoked with two distinct loops if there is a |  | 
| 170   /// point that can reach a recursive invocation of both loops. |  | 
| 171   /// This implies that one loop is nested in the other, because they must |  | 
| 172   /// both be in scope at that point. |  | 
| 173   Continuation _markInnerLoop(Continuation c1, Continuation c2) { |  | 
| 174     assert(c1 == null || c1.isRecursive); |  | 
| 175     assert(c2 == null || c2.isRecursive); |  | 
| 176     if (c1 == null) return c2; |  | 
| 177     if (c2 == null) return c1; |  | 
| 178     if (c1 == c2) return c1; |  | 
| 179     if (loopDepth[c1] > loopDepth[c2]) { |  | 
| 180       loopTarget[c1] = _markInnerLoop(loopTarget[c1], c2); |  | 
| 181       return c1; |  | 
| 182     } else { |  | 
| 183       loopTarget[c2] = _markInnerLoop(loopTarget[c2], c1); |  | 
| 184       return c2; |  | 
| 185     } |  | 
| 186   } |  | 
| 187 |  | 
| 188   /// Analyzes the body of [cont] and returns the innermost loop |  | 
| 189   /// that can be invoked recursively from [cont] (other than [cont] itself). |  | 
| 190   /// |  | 
| 191   /// [catchLoop] is the innermost loop that can be invoked recursively |  | 
| 192   /// from the current exception handler. |  | 
| 193   Continuation _processContinuation(Continuation cont, Continuation catchLoop) { |  | 
| 194     if (cont.isRecursive) { |  | 
| 195       ++currentDepth; |  | 
| 196       loopDepth[cont] = currentDepth; |  | 
| 197       Continuation target = _processBlock(cont.body, catchLoop); |  | 
| 198       _markInnerLoop(loopTarget[cont], target); |  | 
| 199       --currentDepth; |  | 
| 200     } else { |  | 
| 201       loopTarget[cont] = _processBlock(cont.body, catchLoop); |  | 
| 202     } |  | 
| 203     return loopTarget[cont]; |  | 
| 204   } |  | 
| 205 |  | 
| 206   bool _isCallContinuation(Continuation cont) { |  | 
| 207     return cont.hasExactlyOneUse && cont.firstRef.parent is CallExpression; |  | 
| 208   } |  | 
| 209 |  | 
| 210   /// Analyzes a basic block and returns the innermost loop that |  | 
| 211   /// can be invoked recursively from that block. |  | 
| 212   Continuation _processBlock(Expression node, Continuation catchLoop) { |  | 
| 213     List<Continuation> callContinuations = <Continuation>[]; |  | 
| 214     for (; node is! TailExpression; node = node.next) { |  | 
| 215       if (node is LetCont) { |  | 
| 216         for (Continuation cont in node.continuations) { |  | 
| 217           if (!_isCallContinuation(cont)) { |  | 
| 218             // Process non-call continuations at the binding site, so they |  | 
| 219             // their loop target is known at all use sites. |  | 
| 220             _processContinuation(cont, catchLoop); |  | 
| 221           } else { |  | 
| 222             // To avoid deep recursion, do not analyze call continuations |  | 
| 223             // recursively. This basic block traversal steps into the |  | 
| 224             // call contiunation after visiting its use site. We store the |  | 
| 225             // continuations in a list so we can set the loop target once |  | 
| 226             // it is known. |  | 
| 227             callContinuations.add(cont); |  | 
| 228           } |  | 
| 229         } |  | 
| 230       } else if (node is LetHandler) { |  | 
| 231         catchLoop = _processContinuation(node.handler, catchLoop); |  | 
| 232       } |  | 
| 233     } |  | 
| 234     Continuation target; |  | 
| 235     if (node is InvokeContinuation) { |  | 
| 236       if (node.isRecursive) { |  | 
| 237         target = node.continuation.definition; |  | 
| 238       } else { |  | 
| 239         target = loopTarget[node.continuation.definition]; |  | 
| 240       } |  | 
| 241     } else if (node is Branch) { |  | 
| 242       target = _markInnerLoop( |  | 
| 243           loopTarget[node.trueContinuation.definition], |  | 
| 244           loopTarget[node.falseContinuation.definition]); |  | 
| 245     } else { |  | 
| 246       assert(node is Unreachable || node is Throw); |  | 
| 247     } |  | 
| 248     target = _markInnerLoop(target, catchLoop); |  | 
| 249     for (Continuation cont in callContinuations) { |  | 
| 250       // Store the loop target on each call continuation in the basic block. |  | 
| 251       // Because we walk over call continuations as part of the basic block |  | 
| 252       // traversal, these do not get their loop target set otherwise. |  | 
| 253       loopTarget[cont] = target; |  | 
| 254     } |  | 
| 255     return target; |  | 
| 256   } |  | 
| 257 } |  | 
| OLD | NEW | 
|---|