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

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

Issue 2246623002: Delete CPS IR (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 4 years, 4 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 // 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.redundant_join_elimination;
6
7 import 'cps_ir_nodes.dart';
8 import 'optimizers.dart';
9
10 /// Eliminates redundant join points.
11 ///
12 /// A redundant join point is a continuation that immediately branches
13 /// based on one of its parameters, and that parameter is a constant value
14 /// at every invocation. Each invocation is redirected to jump directly
15 /// to the branch target.
16 ///
17 /// Internally in this pass, parameters are treated as names with lexical
18 /// scoping, and a given parameter "name" may be declared by more than
19 /// one continuation. The reference chains for parameters are therefore
20 /// meaningless during this pass, until repaired by [AlphaRenamer] at
21 /// the end.
22 class RedundantJoinEliminator extends TrampolineRecursiveVisitor
23 implements Pass {
24 String get passName => 'Redundant join elimination';
25
26 final Set<Branch> workSet = new Set<Branch>();
27
28 void rewrite(FunctionDefinition node) {
29 visit(node);
30
31 while (workSet.isNotEmpty) {
32 Branch branch = workSet.first;
33 workSet.remove(branch);
34 rewriteBranch(branch);
35 }
36
37 new AlphaRenamer().visit(node);
38 }
39
40 void processBranch(Branch node) {
41 workSet.add(node);
42 }
43
44 /// Returns the body of [node], ignoring all LetCont nodes.
45 Expression getEffectiveBody(InteriorNode node) {
46 while (true) {
47 Expression body = node.body;
48 if (body is LetCont) {
49 node = body;
50 } else {
51 return body;
52 }
53 }
54 }
55
56 /// Returns the parent of [node], ignoring all LetCont nodes.
57 InteriorNode getEffectiveParent(Expression node) {
58 while (true) {
59 Node parent = node.parent;
60 if (parent is LetCont) {
61 node = parent;
62 } else {
63 return parent;
64 }
65 }
66 }
67
68 void rewriteBranch(Branch branch) {
69 InteriorNode parent = getEffectiveParent(branch);
70 if (parent is! Continuation) return;
71 Continuation branchCont = parent;
72
73 // Other optimizations take care of single-use continuations.
74 if (!branchCont.hasMultipleUses) return;
75
76 // It might be beneficial to rewrite calls to recursive continuations,
77 // but we currently do not support this.
78 if (branchCont.isRecursive) return;
79
80 // Check that the branching condition is a parameter on the
81 // enclosing continuation.
82 // Note: Do not use the parent pointer for this check, because parameters
83 // are temporarily shared between different continuations during this pass.
84 Primitive condition = branch.condition;
85 int parameterIndex = branchCont.parameters.indexOf(condition);
86 if (parameterIndex == -1) return;
87
88 // Check that all callers hit a fixed branch, and count the number
89 // of times each branch is hit.
90 // We know all callers are InvokeContinuations because they are the only
91 // valid uses of a multi-use continuation.
92 int trueHits = 0, falseHits = 0;
93 InvokeContinuation trueCall, falseCall;
94 for (Reference ref = branchCont.firstRef; ref != null; ref = ref.next) {
95 InvokeContinuation invoke = ref.parent;
96 Primitive argument = invoke.argument(parameterIndex);
97 if (argument is! Constant) return; // Branching condition is unknown.
98 Constant constant = argument;
99 if (isTruthyConstant(constant.value, strict: branch.isStrictCheck)) {
100 ++trueHits;
101 trueCall = invoke;
102 } else {
103 ++falseHits;
104 falseCall = invoke;
105 }
106 }
107
108 // The optimization is now known to be safe, but it only pays off if
109 // one of the callers can inline its target, since otherwise we end up
110 // replacing a boolean variable with a labeled break.
111 // TODO(asgerf): The labeled break might be better? Evaluate.
112 if (!(trueHits == 1 && !trueCall.isEscapingTry ||
113 falseHits == 1 && !falseCall.isEscapingTry)) {
114 return;
115 }
116
117 // Lift any continuations bound inside branchCont so they are in scope at
118 // the call sites. When lifting, the parameters of branchCont fall out of
119 // scope, so they are added as parameters on each lifted continuation.
120 // Schematically:
121 //
122 // (LetCont (branchCont (x1, x2, x3) =
123 // (LetCont (innerCont (y) = ...) in
124 // [... innerCont(y') ...]))
125 //
126 // =>
127 //
128 // (LetCont (innerCont (y, x1, x2, x3) = ...) in
129 // (LetCont (branchCont (x1, x2, x3) =
130 // [... innerCont(y', x1, x2, x3) ...])
131 //
132 // Parameter objects become shared between branchCont and the lifted
133 // continuations. [AlphaRenamer] will clean up at the end of this pass.
134 LetCont outerLetCont = branchCont.parent;
135 while (branchCont.body is LetCont) {
136 LetCont innerLetCont = branchCont.body;
137 for (Continuation innerCont in innerLetCont.continuations) {
138 innerCont.parameters.addAll(branchCont.parameters);
139 for (Reference ref = innerCont.firstRef; ref != null; ref = ref.next) {
140 Expression use = ref.parent;
141 if (use is InvokeContinuation) {
142 for (Parameter param in branchCont.parameters) {
143 use.argumentRefs
144 .add(new Reference<Primitive>(param)..parent = use);
145 }
146 } else {
147 // The branch will be eliminated, so don't worry about updating it.
148 assert(use == branch);
149 }
150 }
151 }
152 innerLetCont.remove();
153 innerLetCont.insertAbove(outerLetCont);
154 }
155
156 assert(branchCont.body == branch);
157
158 Continuation trueCont = branch.trueContinuation;
159 Continuation falseCont = branch.falseContinuation;
160
161 assert(branchCont != trueCont);
162 assert(branchCont != falseCont);
163
164 // Rewrite every invocation of branchCont to call either the true or false
165 // branch directly. Since these were lifted out above branchCont, they are
166 // now in scope.
167 // Since trueCont and falseCont were branch targets, they originally
168 // had no parameters, and so after the lifting, their parameters are
169 // exactly the same as those accepted by branchCont.
170 while (branchCont.firstRef != null) {
171 Reference reference = branchCont.firstRef;
172 InvokeContinuation invoke = branchCont.firstRef.parent;
173 Constant condition = invoke.argument(parameterIndex);
174 if (isTruthyConstant(condition.value, strict: branch.isStrictCheck)) {
175 invoke.continuationRef.changeTo(trueCont);
176 } else {
177 invoke.continuationRef.changeTo(falseCont);
178 }
179 assert(branchCont.firstRef != reference);
180 }
181
182 // Remove the now-unused branchCont continuation.
183 assert(branchCont.hasNoUses);
184 branch.trueContinuationRef.unlink();
185 branch.falseContinuationRef.unlink();
186 outerLetCont.continuations.remove(branchCont);
187 if (outerLetCont.continuations.isEmpty) {
188 outerLetCont.remove();
189 }
190
191 // We may have created new redundant join points in the two branches.
192 enqueueContinuation(trueCont);
193 enqueueContinuation(falseCont);
194 }
195
196 void enqueueContinuation(Continuation cont) {
197 Expression body = getEffectiveBody(cont);
198 if (body is Branch) {
199 workSet.add(body);
200 }
201 }
202 }
203
204 /// Ensures parameter objects are not shared between different continuations,
205 /// akin to alpha-renaming variables so every variable is named uniquely.
206 /// For example:
207 ///
208 /// LetCont (k1 x = (return x)) in
209 /// LetCont (k2 x = (InvokeContinuation k3 x)) in ...
210 /// =>
211 /// LetCont (k1 x = (return x)) in
212 /// LetCont (k2 x' = (InvokeContinuation k3 x')) in ...
213 ///
214 /// After lifting LetConts in the main pass above, parameter objects can have
215 /// multiple bindings. Each reference implicitly refers to the binding that
216 /// is currently in scope.
217 ///
218 /// This returns the IR to its normal form after redundant joins have been
219 /// eliminated.
220 class AlphaRenamer extends TrampolineRecursiveVisitor {
221 Map<Parameter, Parameter> renaming = <Parameter, Parameter>{};
222
223 processContinuation(Continuation cont) {
224 if (cont.isReturnContinuation) return;
225
226 List<Parameter> shadowedKeys = <Parameter>[];
227 List<Parameter> shadowedValues = <Parameter>[];
228
229 // Create new parameters and update the environment.
230 for (int i = 0; i < cont.parameters.length; ++i) {
231 Parameter param = cont.parameters[i];
232 shadowedKeys.add(param);
233 shadowedValues.add(renaming.remove(param));
234 // If the parameter appears to belong to another continuation,
235 // create a new parameter object for this continuation.
236 if (param.parent != cont) {
237 Parameter newParam = new Parameter(param.hint);
238 newParam.type = param.type;
239 renaming[param] = newParam;
240 cont.parameters[i] = newParam;
241 newParam.parent = cont;
242 }
243 }
244
245 pushAction(() {
246 // Restore the original environment.
247 for (int i = 0; i < cont.parameters.length; ++i) {
248 renaming.remove(cont.parameters[i]);
249 if (shadowedValues[i] != null) {
250 renaming[shadowedKeys[i]] = shadowedValues[i];
251 }
252 }
253 });
254 }
255
256 processReference(Reference ref) {
257 Parameter target = renaming[ref.definition];
258 if (target != null) {
259 ref.changeTo(target);
260 }
261 }
262 }
OLDNEW
« no previous file with comments | « pkg/compiler/lib/src/cps_ir/path_based_optimizer.dart ('k') | pkg/compiler/lib/src/cps_ir/redundant_phi.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698