Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 library dart2js.cps_ir.loop_invariant_branch; | |
| 2 | |
| 3 import 'cps_ir_nodes.dart'; | |
| 4 import 'optimizers.dart'; | |
| 5 import 'loop_hierarchy.dart'; | |
| 6 import 'cps_fragment.dart'; | |
| 7 import 'redundant_join.dart' show AlphaRenamer; | |
| 8 | |
| 9 /// Hoists branches out of loops, where: | |
| 10 /// - the branch is at the entry point of a loop | |
| 11 /// - the branch condition is loop-invariant | |
| 12 /// - one arm of the branch is not effectively part of the loop | |
| 13 /// | |
| 14 /// Schematically: | |
| 15 /// | |
| 16 /// b = COND | |
| 17 /// while (true) { | |
| 18 /// if (b) | |
| 19 /// BRANCH (contains no continue to loop) | |
| 20 /// else | |
| 21 /// LOOP | |
| 22 /// } | |
| 23 /// | |
| 24 /// ==> | |
| 25 /// | |
| 26 /// b = COND | |
| 27 /// if (b) | |
| 28 /// BRANCH | |
| 29 /// else | |
| 30 /// while (true) | |
| 31 /// LOOP | |
| 32 /// | |
| 33 /// As in [RedundantJoinEliminator], parameters are treated as names with | |
| 34 /// lexical scoping during this pass, and a given parameter "name" may be | |
| 35 /// declared by more than one continuation. The reference chains for parameters | |
| 36 /// are therefore meaningless during this pass, until repaired by [AlphaRenamer] | |
| 37 /// at the end. | |
| 38 class LoopInvariantBranchMotion extends BlockVisitor implements Pass { | |
| 39 String get passName => 'Loop invariant branch motion'; | |
| 40 | |
| 41 LoopHierarchy loopHierarchy; | |
| 42 final Map<Primitive, Continuation> loopHeaderFor = | |
| 43 <Primitive, Continuation>{}; | |
| 44 final Map<Continuation, Continuation> catchLoopFor = | |
| 45 <Continuation, Continuation>{}; | |
| 46 Continuation currentLoopHeader; | |
| 47 Continuation currentCatchLoop; | |
| 48 List<Continuation> loops = <Continuation>[]; | |
| 49 bool wasHoisted = false; | |
| 50 | |
| 51 void rewrite(FunctionDefinition node) { | |
| 52 loopHierarchy = new LoopHierarchy(node); | |
| 53 BlockVisitor.traverseInPreOrder(node, this); | |
| 54 // Process loops bottom-up so a branch can be hoisted multiple times. | |
| 55 loops.reversed.forEach(hoistEntryCheck); | |
| 56 if (wasHoisted) { | |
| 57 new AlphaRenamer().visit(node); | |
| 58 } | |
| 59 } | |
| 60 | |
| 61 void visitLetHandler(LetHandler node) { | |
| 62 currentCatchLoop = loopHierarchy.getLoopHeader(node.handler); | |
| 63 } | |
| 64 | |
| 65 void visitContinuation(Continuation node) { | |
| 66 currentLoopHeader = loopHierarchy.getLoopHeader(node); | |
| 67 for (Parameter param in node.parameters) { | |
| 68 loopHeaderFor[param] = currentLoopHeader; | |
| 69 } | |
| 70 catchLoopFor[node] = currentCatchLoop; | |
| 71 if (node.isRecursive) { | |
| 72 loops.add(node); | |
| 73 } | |
| 74 } | |
| 75 | |
| 76 void visitLetPrim(LetPrim node) { | |
| 77 loopHeaderFor[node.primitive] = currentLoopHeader; | |
| 78 } | |
| 79 | |
| 80 void hoistEntryCheck(Continuation loop) { | |
| 81 // Keep hoisting branches out of the loop, there can be more than one. | |
| 82 while (tryHoistEntryCheck(loop)); | |
| 83 } | |
| 84 | |
| 85 Expression getEffectiveBody(Expression exp) { | |
| 86 // TODO(asgerf): We could also bypass constants here but constant pooling | |
| 87 // is likely to be a better solution for that. | |
| 88 while (exp is LetCont) { | |
| 89 exp = exp.next; | |
| 90 } | |
| 91 return exp; | |
| 92 } | |
| 93 | |
| 94 /// Adds [parameters] to [cont] and updates every invocation to pass the | |
| 95 /// corresponding parameter values as arguments. Thus, the parameters are | |
| 96 /// passed in explicitly instead of being captured. | |
| 97 /// | |
| 98 /// This only works because [AlphaRenamer] cleans up after this pass. | |
|
sra1
2016/01/09 01:07:14
'after this pass' -> 'at the end of this pass'.
asgerf
2016/01/11 19:07:07
Sorry, I thought I fixed this. I'll put up another
| |
| 99 /// | |
| 100 /// Schematically: | |
| 101 /// | |
| 102 /// let outer(x1, x2, x3) = | |
| 103 /// let inner(y) = BODY | |
| 104 /// [ .. inner(y') .. ] | |
| 105 /// | |
| 106 /// ==> (append parameters) | |
| 107 /// | |
| 108 /// let outer(x1, x2, x3) = | |
| 109 /// let inner(y, x1, x2, x3) = BODY | |
| 110 /// [ .. inner(y', x1, x2, x3) .. ] | |
| 111 /// | |
| 112 /// ==> (hoist, not performed by this method) | |
| 113 /// | |
| 114 /// let inner(y, x1, x2, x3) = BODY | |
| 115 /// let outer(x1, x2, x3) = | |
| 116 /// [ .. inner(y', x1, x2, x3) .. ] | |
| 117 /// | |
| 118 void appendParameters(Continuation cont, List<Parameter> parameters) { | |
| 119 cont.parameters.addAll(parameters); | |
| 120 for (Reference ref = cont.firstRef; ref != null; ref = ref.next) { | |
| 121 Node use = ref.parent; | |
| 122 if (use is InvokeContinuation) { | |
| 123 for (Parameter loopParam in parameters) { | |
| 124 use.arguments.add(new Reference<Primitive>(loopParam)..parent = use); | |
| 125 } | |
| 126 } | |
| 127 } | |
| 128 } | |
| 129 | |
| 130 bool tryHoistEntryCheck(Continuation loop) { | |
| 131 // Check if this is a loop starting with a branch. | |
|
sra1
2016/01/09 01:07:14
It would be nice if there was a quick way to test
asgerf
2016/01/11 19:07:07
Acknowledged.
| |
| 132 Expression body = getEffectiveBody(loop.body); | |
| 133 if (body is! Branch) return false; | |
| 134 Branch branch = body; | |
| 135 | |
| 136 // Is the condition loop invariant? | |
| 137 Primitive condition = branch.condition.definition; | |
| 138 if (loopHeaderFor[condition] == loop) return false; | |
| 139 | |
| 140 Continuation trueCont = branch.trueContinuation.definition; | |
| 141 Continuation falseCont = branch.falseContinuation.definition; | |
| 142 Continuation hoistedCase; // The branch to hoist. | |
| 143 Continuation loopCase; // The branch that is part of the loop. | |
| 144 | |
| 145 // Check that one branch is part of the loop, and the other is an exit. | |
| 146 if (loopHierarchy.getLoopHeader(trueCont) != loop && | |
| 147 loopHierarchy.getLoopHeader(falseCont) == loop) { | |
| 148 hoistedCase = trueCont; | |
| 149 loopCase = falseCont; | |
| 150 } else if (loopHierarchy.getLoopHeader(falseCont) != loop && | |
| 151 loopHierarchy.getLoopHeader(trueCont) == loop) { | |
| 152 hoistedCase = falseCont; | |
| 153 loopCase = trueCont; | |
| 154 } else { | |
| 155 return false; | |
| 156 } | |
| 157 | |
| 158 // Hoist non-loop continuations out of the loop. | |
| 159 // The hoisted branch can reference other continuations bound in the loop, | |
| 160 // so to stay in scope, those need to be hoisted as well. | |
| 161 // | |
| 162 // let b = COND | |
| 163 // let loop(x) = | |
| 164 // let join(y) = JOIN | |
| 165 // let hoistCase() = HOIST | |
| 166 // let loopCase() = LOOP | |
| 167 // branch b hoistCase loopCase | |
| 168 // in loop(i) | |
| 169 // | |
| 170 // ==> | |
| 171 // | |
| 172 // let b = COND | |
| 173 // let join(y,x) = JOIN | |
| 174 // let hoistCase(x) = HOIST | |
| 175 // let loop(x) = | |
| 176 // let loopCase() = LOOP | |
| 177 // branch b hoistCase loopCase | |
| 178 // in loop(i) | |
| 179 // | |
| 180 LetCont loopBinding = loop.parent; | |
| 181 Expression it = loop.body; | |
| 182 while (it is LetCont) { | |
| 183 LetCont let = it; | |
| 184 it = let.body; | |
| 185 for (Continuation cont in let.continuations) { | |
| 186 if (loopHierarchy.getEnclosingLoop(cont) != loop) { | |
| 187 appendParameters(cont, loop.parameters); | |
| 188 new LetCont(cont, null).insertAbove(loopBinding); | |
| 189 } | |
| 190 } | |
| 191 let.continuations.removeWhere((cont) => cont.parent != let); | |
| 192 if (let.continuations.isEmpty) { | |
| 193 let.remove(); | |
| 194 } | |
| 195 } | |
| 196 | |
| 197 // Create a new branch to call the hoisted continuation or the loop: | |
| 198 // | |
| 199 // let loop(x) = | |
| 200 // let loopCase() = LOOP | |
| 201 // branch b hoistCase loopCase | |
| 202 // in loop(i) | |
| 203 // | |
| 204 // ==> | |
| 205 // | |
| 206 // let newTrue() = hoistCase(i) | |
| 207 // let newFalse() = | |
| 208 // let loop(x) = | |
| 209 // let loopCase() = LOOP | |
| 210 // branch b hoistCase loopCase | |
| 211 // branch b newTrue newFalse | |
| 212 // | |
| 213 InvokeContinuation loopEntry = loopBinding.body; | |
| 214 List<Primitive> loopArgs = | |
| 215 loopEntry.arguments.map((ref) => ref.definition).toList(); | |
| 216 CpsFragment cps = new CpsFragment(); | |
| 217 cps.branch(condition, | |
| 218 strict: branch.isStrictCheck, | |
| 219 negate: hoistedCase == falseCont) | |
| 220 .invokeContinuation(hoistedCase, loopArgs); | |
| 221 | |
| 222 // The continuations created in the fragment need to have their loop header | |
| 223 // set so the loop hierarchy remains intact | |
| 224 loopHierarchy.update(cps, | |
| 225 exitLoop: loopHierarchy.getEnclosingLoop(loop), | |
| 226 catchLoop: catchLoopFor[loop]); | |
| 227 | |
| 228 // Insert above the loop. This will put the loop itself in a branch. | |
| 229 cps.insertAbove(loopBinding); | |
| 230 | |
| 231 // Replace the old branch with the loopCase, still bound inside the loop: | |
| 232 // | |
| 233 // let loop(x) = | |
| 234 // let loopCase() = LOOP | |
| 235 // branch b hoistCase loopCase | |
| 236 // in loop(i) | |
| 237 // | |
| 238 // ==> | |
| 239 // | |
| 240 // let loop(x) = | |
| 241 // let loopCase() = LOOP | |
| 242 // loopCase() | |
| 243 // in loop(i) | |
| 244 // | |
| 245 destroyAndReplace(branch, new InvokeContinuation(loopCase, [])); | |
| 246 | |
| 247 // Record that at least one branch was hoisted to trigger alpha renaming. | |
| 248 wasHoisted = true; | |
| 249 | |
| 250 return true; | |
| 251 } | |
| 252 } | |
| OLD | NEW |