Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(365)

Side by Side Diff: pkg/compiler/lib/src/cps_ir/loop_invariant_branch.dart

Issue 1573693002: dart2js cps: Hoist loop-invariant branches from loop entry. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698