Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 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 | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 library dart2js.cps_ir.let_sinking; | |
| 6 | |
| 7 import 'cps_ir_nodes.dart'; | |
| 8 import 'optimizers.dart'; | |
| 9 | |
| 10 /// Sinks single-use primitives to the use when this is safe and profitable. | |
| 11 /// | |
| 12 /// To avoid sinking non-constant primitives into loops, this pass performs a | |
| 13 /// control-flow analysis to determine the effective nesting of loops, see | |
| 14 /// [LoopHierarchy]. | |
|
floitsch
2015/07/17 17:19:45
I think it's always nice to have an example. (does
asgerf
2015/07/20 09:10:56
Added an example based on the one from LoopHierarc
| |
| 15 class LetSinker extends RecursiveVisitor implements Pass { | |
| 16 String get passName => 'Let sinking'; | |
| 17 | |
| 18 LoopHierarchy loopHierarchy; | |
| 19 List<Continuation> stack = <Continuation>[]; | |
| 20 Set<LetPrim> sunkNodes = new Set<LetPrim>(); | |
| 21 | |
| 22 void rewrite(FunctionDefinition node) { | |
| 23 new ParentVisitor().visit(node); | |
| 24 loopHierarchy = new LoopHierarchy(node); | |
|
floitsch
2015/07/17 17:19:45
I'm ok this way, but have a *slight* preference to
asgerf
2015/07/20 09:10:56
Acknowledged.
I usually do that if the class is c
| |
| 25 visit(node.body); | |
| 26 } | |
| 27 | |
| 28 void visitLetPrim(LetPrim node) { | |
| 29 // Visit the body, wherein this primitive may be sunk to its use site. | |
| 30 visit(node.body); | |
| 31 | |
| 32 if (node.primitive != null) { | |
| 33 // The primitive could not be sunk. Sink dependencies to this location. | |
| 34 visit(node.primitive); | |
| 35 } else { | |
| 36 // The primitive was sunk. Destroy the old LetPrim. | |
| 37 InteriorNode parent = node.parent; | |
| 38 parent.body = node.body; | |
| 39 node.body.parent = parent; | |
| 40 } | |
| 41 } | |
| 42 | |
| 43 void processReference(Reference ref) { | |
| 44 Definition definition = ref.definition; | |
| 45 if (definition is Primitive && | |
| 46 definition is! Parameter && | |
| 47 definition.hasExactlyOneUse && | |
| 48 definition.isSafeForReordering) { | |
| 49 // Check if use is in the same loop. | |
| 50 LetPrim binding = definition.parent; | |
| 51 Continuation bindingLoop = loopHierarchy.getLoopHeader(binding); | |
| 52 Expression use = getEnclosingExpression(ref.parent); | |
| 53 Continuation useLoop = loopHierarchy.getLoopHeader(use); | |
| 54 if (bindingLoop == useLoop || definition is Constant) { | |
| 55 // Sink the definition. | |
| 56 | |
| 57 binding.primitive = null; // Mark old binding for deletion. | |
| 58 LetPrim newBinding = new LetPrim(definition); | |
| 59 definition.parent = newBinding; | |
| 60 InteriorNode useParent = use.parent; | |
| 61 useParent.body = newBinding; | |
| 62 newBinding.body = use; | |
| 63 use.parent = newBinding; | |
| 64 newBinding.parent = useParent; | |
| 65 | |
| 66 // Now that the final binding location has been found, sink the | |
| 67 // dependencies of the definition down here as well. | |
| 68 visit(definition); | |
| 69 } | |
| 70 } | |
| 71 } | |
| 72 | |
| 73 Expression getEnclosingExpression(Node node) { | |
| 74 while (node is! Expression) { | |
| 75 node = node.parent; | |
| 76 } | |
| 77 return node; | |
| 78 } | |
| 79 } | |
| 80 | |
| 81 /// Determines the effective nesting of loops. | |
| 82 /// | |
| 83 /// The effective nesting of loops is different from the lexical nesting, since | |
| 84 /// recursive continuations can generally contain all the code following | |
| 85 /// after the loop in addition to the looping code itself. | |
| 86 /// | |
| 87 /// For example, the 'else' branch below is not effectively part of the loop: | |
| 88 /// | |
| 89 /// let rec kont x = | |
| 90 /// if (<loop condition>) | |
| 91 /// <loop body> | |
| 92 /// InvokeContinuation kont x' | |
| 93 /// else | |
| 94 /// <after loop> | |
| 95 /// return p.foo() | |
| 96 /// | |
| 97 /// We use the term "loop" to mean recursive continuation. | |
| 98 /// The `null` value is used to represent a context not part of any loop. | |
| 99 class LoopHierarchy { | |
| 100 /// Nesting depth of the given loop. | |
| 101 Map<Continuation, int> loopDepth = <Continuation, int>{}; | |
| 102 | |
| 103 /// The innermost loop (other than itself) that may be invoked recursively | |
| 104 /// as a result of invoking the given continuation. | |
| 105 Map<Continuation, Continuation> loopTarget = <Continuation, Continuation>{}; | |
| 106 | |
| 107 /// Current nesting depth. | |
| 108 int currentDepth = 0; | |
| 109 | |
| 110 /// Computes the loop loop hierarchy for the given function. | |
| 111 /// | |
| 112 /// Parent pointers must be computed for [node]. | |
| 113 LoopHierarchy(FunctionDefinition node) { | |
| 114 _processBlock(node.body, null); | |
| 115 } | |
| 116 | |
| 117 /// Returns the innermost loop which [node] is effectively part of. | |
| 118 Continuation getLoopHeader(Expression exp) { | |
| 119 Node node = exp.parent; | |
| 120 while (node != null && node is! Continuation) { | |
| 121 node = node.parent; | |
| 122 } | |
| 123 if (node is Continuation) { | |
| 124 if (node.isRecursive) | |
|
floitsch
2015/07/17 17:19:45
multi-line ifs should have {}
asgerf
2015/07/20 09:10:56
Done.
| |
| 125 return node; | |
| 126 else | |
| 127 return loopTarget[node]; | |
| 128 } | |
| 129 return null; | |
| 130 } | |
| 131 | |
| 132 /// Returns the innermost of two loops and marks its as being | |
| 133 /// a subloop in the other loop. | |
| 134 /// | |
| 135 /// A loop is said to be a subloop of an enclosing loop if it can invoke | |
| 136 /// that loop recursively. This information is stored in [loopTarget]. | |
| 137 /// | |
| 138 /// This method is only invoked with two distinct loops if there is a | |
| 139 /// point that can reach a recursive invocation of both loops. | |
| 140 /// This implies that one loop is nested in the other, because they must | |
| 141 /// both be in scope at that point. | |
| 142 Continuation _determineInnerLoop(Continuation c1, Continuation c2) { | |
|
floitsch
2015/07/17 17:19:45
The main-goal of this function is to mark nesting,
asgerf
2015/07/20 09:10:56
Done.
| |
| 143 assert(c1 == null || c1.isRecursive); | |
| 144 assert(c2 == null || c2.isRecursive); | |
| 145 if (c1 == null) return c2; | |
| 146 if (c2 == null) return c1; | |
| 147 if (c1 == c2) return c1; | |
| 148 if (loopDepth[c1] > loopDepth[c2]) { | |
| 149 loopTarget[c1] = _determineInnerLoop(loopTarget[c1], c2); | |
| 150 return c1; | |
| 151 } else { | |
| 152 loopTarget[c2] = _determineInnerLoop(loopTarget[c2], c1); | |
| 153 return c2; | |
| 154 } | |
| 155 } | |
| 156 | |
| 157 /// Analyzes the body of [cont] and returns the innermost loop | |
| 158 /// that can be invoked recursively can be invoked from [cont] | |
|
floitsch
2015/07/17 17:19:45
-can be invoked-
asgerf
2015/07/20 09:10:56
Done.
| |
| 159 /// (other than [cont] itself). | |
| 160 /// | |
| 161 /// [catchLoop] is the innermost loop that can be invoked recursively | |
| 162 /// from the current exception handler. | |
| 163 Continuation _processContinuation(Continuation cont, Continuation catchLoop) { | |
| 164 if (cont.isRecursive) { | |
| 165 ++currentDepth; | |
| 166 loopDepth[cont] = currentDepth; | |
| 167 Continuation target = _processBlock(cont.body, catchLoop); | |
| 168 _determineInnerLoop(loopTarget[cont], target); | |
| 169 --currentDepth; | |
| 170 } else { | |
| 171 loopTarget[cont] = _processBlock(cont.body, catchLoop); | |
| 172 } | |
| 173 return loopTarget[cont]; | |
| 174 } | |
| 175 | |
| 176 bool _isCallContinuation(Continuation cont) { | |
| 177 return cont.hasExactlyOneUse && cont.firstRef.parent is CallExpression; | |
| 178 } | |
| 179 | |
| 180 /// Analyzes a basic block and returns the innermost loop that | |
| 181 /// can be invoked recursively from that block. | |
| 182 Continuation _processBlock(Expression node, Continuation catchLoop) { | |
| 183 List<Continuation> callContinuations = <Continuation>[]; | |
| 184 for (; node is! TailExpression; node = node.next) { | |
| 185 if (node is LetCont) { | |
| 186 for (Continuation cont in node.continuations) { | |
| 187 // Do not process call continuations at the binding site. | |
| 188 // We step into call continuations as part of the same basic block. | |
|
floitsch
2015/07/17 17:19:45
I don't understand this comment.
asgerf
2015/07/20 09:10:56
Refactored code and comment.
The "is CallExpressi
| |
| 189 if (!_isCallContinuation(cont)) { | |
| 190 _processContinuation(cont, catchLoop); | |
| 191 } | |
| 192 } | |
| 193 } else if (node is CallExpression) { | |
| 194 callContinuations.add(node.continuation.definition); | |
| 195 } else if (node is LetHandler) { | |
| 196 catchLoop = _processContinuation(node.handler, catchLoop); | |
| 197 } | |
| 198 } | |
| 199 Continuation target; | |
| 200 if (node is InvokeContinuation) { | |
| 201 if (node.isRecursive) { | |
| 202 target = node.continuation.definition; | |
| 203 } else { | |
| 204 target = loopTarget[node.continuation.definition]; | |
| 205 } | |
| 206 } else if (node is Branch) { | |
| 207 target = _determineInnerLoop( | |
| 208 loopTarget[node.trueContinuation.definition], | |
| 209 loopTarget[node.falseContinuation.definition]); | |
| 210 } else { | |
| 211 assert(node is Unreachable || node is Throw); | |
| 212 } | |
| 213 target = _determineInnerLoop(target, catchLoop); | |
| 214 for (Continuation cont in callContinuations) { | |
| 215 // Store the loop target on each call continuation in the basic block. | |
| 216 // Because we walk over call continuations as part of the basic block | |
| 217 // traversal, these do not get their loop target set by otherwise. | |
|
floitsch
2015/07/17 17:19:45
-by-
asgerf
2015/07/20 09:10:56
Done.
| |
| 218 loopTarget[cont] = target; | |
| 219 } | |
| 220 return target; | |
| 221 } | |
| 222 } | |
| OLD | NEW |