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