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

Side by Side Diff: pkg/compiler/lib/src/tree_ir/optimization/pull_into_initializers.dart

Issue 1188783002: dart2js cps: Overhaul PullIntoInitializers. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Comments Created 5 years, 6 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
« no previous file with comments | « no previous file | tests/co19/co19-dart2js.status » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 tree_ir.optimization.pull_into_initializers; 5 library tree_ir.optimization.pull_into_initializers;
6 6
7 import 'optimization.dart' show Pass; 7 import 'optimization.dart' show Pass;
8 import '../tree_ir_nodes.dart'; 8 import '../tree_ir_nodes.dart';
9 9
10 /// Where a variable has been assigned.
11 enum AssignArea {
12 /// The variable is only assigned in the initializer block.
13 Initializer,
14
15 // The variable has at least one assignment outside the initializer block.
16 Anywhere,
17 }
18
10 /// Pulls assignment expressions to the top of the function body so they can be 19 /// Pulls assignment expressions to the top of the function body so they can be
11 /// translated into declaration-site variable initializaters. 20 /// translated into declaration-site variable initializaters.
12 /// 21 ///
13 /// This reverts the assignment expression propagation performed by 22 /// This reverts the assignment expression propagation performed by
14 /// [StatementRewriter] in cases where it not beneficial. 23 /// [StatementRewriter] in cases where it not beneficial.
15 /// 24 ///
16 /// EXAMPLE: 25 /// EXAMPLE:
17 /// 26 ///
18 /// var x = foo(), 27 /// var x = foo(),
19 /// y = bar(x); 28 /// y = bar(x);
(...skipping 21 matching lines...) Expand all
41 /// baz(x, y, y); 50 /// baz(x, y, y);
42 /// 51 ///
43 /// ==> [StatementRewriter] 52 /// ==> [StatementRewriter]
44 /// 53 ///
45 /// var y; 54 /// var y;
46 /// baz(foo(), y = bar(), y); 55 /// baz(foo(), y = bar(), y);
47 /// 56 ///
48 /// [PullIntoInitializers] cannot pull `y` into an initializer because 57 /// [PullIntoInitializers] cannot pull `y` into an initializer because
49 /// the impure expressions `foo()` and `bar()` would then be swapped. 58 /// the impure expressions `foo()` and `bar()` would then be swapped.
50 /// 59 ///
51 class PullIntoInitializers extends ExpressionVisitor<Expression> 60 class PullIntoInitializers extends RecursiveTransformer
52 implements Pass { 61 implements Pass {
53 String get passName => 'Pull into initializers'; 62 String get passName => 'Pull into initializers';
54 63
55 Set<Variable> assignedVariables = new Set<Variable>(); 64 /// Denotes where each variable is currently assigned.
65 ///
66 /// Variables without assignments are absent from the map.
67 Map<Variable, AssignArea> assignArea = <Variable, AssignArea>{};
56 68
57 /// The fragment between [first] and [last] holds the statements 69 /// The fragment between [first] and [last] holds the statements
58 /// we pulled into the initializer block. 70 /// we pulled into the initializer block.
59 /// 71 ///
60 /// The *initializer block* is a sequence of [ExpressionStatement]s with 72 /// The "initializer block" is a sequence of [ExpressionStatement]s with
61 /// [Assign]s that we create in the beginning of the body, with the intent 73 /// [Assign]s that we create in the beginning of the body, with the intent
62 /// that code generation will convert them to variable initializers. 74 /// that code generation will convert them to variable initializers.
63 /// 75 ///
64 /// The block is empty when both are `null`. 76 /// The block is empty when both are `null`.
65 Statement first, last; 77 Statement first, last;
66 78
67 /// True if an impure expression has been returned by visitExpression. 79 /// The number of impure expressions separating the current program point
80 /// from the initializer block.
68 /// 81 ///
69 /// Expressions cannot be pulled into an initializer if this might reorder 82 /// A pure expression is an expression that cannot throw, diverge, have side
70 /// impure expressions. 83 /// effects, or depend on mutable state.
71 /// 84 ///
72 /// A visit method may not be called while this flag is set, meaning all 85 /// As a special case, variable uses are also considered pure when their only
73 /// visitor methods must check the flag between visiting subexpressions. 86 /// reaching definition is an assignment in the initializer block.
74 bool seenImpure; 87 int impureCounter = 0;
88
89 /// The number of assignments separating the current program point from the
90 /// initializer block. Note that these are also counted as impure expressions.
91 ///
92 /// Assignments are given special treatment because hoisting an assignment
93 /// may change the reaching definitions of a variable use. The analysis may
94 /// already have considered such a use to be pure, and we must then ensure
95 /// that it remains pure.
96 int assignCounter = 0;
97
98 /// The number of branch points separating the current program point from
99 /// the initializer block.
100 ///
101 /// We do not pull expressions out of branches, not even pure ones, but
102 /// we sometimes want to traverse branches to check if they are pure.
103 int branchCounter = 0;
75 104
76 /// Appends a statement to the initializer block. 105 /// Appends a statement to the initializer block.
77 void append(Statement node) { 106 void append(Statement node) {
78 if (first == null) { 107 if (first == null) {
79 first = last = node; 108 first = last = node;
80 } else { 109 } else {
81 last.next = node; 110 last.next = node;
82 last = node; 111 last = node;
83 } 112 }
84 } 113 }
85 114
86 /// Pulls assignment expressions from [node] into the initializer block
87 /// by calling [append].
88 ///
89 /// Returns a transformed expression where the pulled assignments are
90 /// replaced by variable uses.
91 Expression rewriteExpression(Expression node) {
92 seenImpure = false;
93 return visitExpression(node);
94 }
95
96 void rewrite(FunctionDefinition node) { 115 void rewrite(FunctionDefinition node) {
97 Statement body = node.body; 116 for (Variable param in node.parameters) {
98 assignedVariables.addAll(node.parameters); 117 assignArea[param] = AssignArea.Initializer;
99
100 // [body] represents the first statement after the initializer block.
101 // Repeatedly pull assignment statements into the initializer block.
102 while (body is ExpressionStatement) {
103 ExpressionStatement stmt = body;
104 stmt.expression = rewriteExpression(stmt.expression);
105 if (stmt.expression is VariableUse) {
106 // The entire expression was pulled into an initializer.
107 // This can happen when the expression was an assignment that was
108 // pulled into the initializer block and replaced by a variable use.
109 // Discard the statement and try to pull in more initializers from
110 // the next statement.
111 destroyVariableUse(stmt.expression);
112 body = stmt.next;
113 } else {
114 // The whole expression could not be pulled into an initializer, so we
115 // have reached the end of the initializer block.
116 break;
117 }
118 } 118 }
119 119 Statement body = visitStatement(node.body);
120 // [If] and [Return] statements terminate the initializer block, but the
121 // initial expression they contain may be pulled up into an initializer.
122 // It's ok to pull an assignment across a label so look for the first
123 // non-labeled statement and try to pull its initial subexpression.
124 Statement entryNode = unfoldLabeledStatements(body);
125 if (entryNode is If) {
126 entryNode.condition = rewriteExpression(entryNode.condition);
127 } else if (entryNode is Return) {
128 entryNode.value = rewriteExpression(entryNode.value);
129 }
130
131 append(body); 120 append(body);
132 assert(first != null); // Because we just appended the body. 121 assert(first != null);
133
134 node.body = first; 122 node.body = first;
135 } 123 }
136 124
137 void destroyVariableUse(VariableUse node) { 125 void destroyVariableUse(VariableUse node) {
138 --node.variable.readCount; 126 --node.variable.readCount;
139 } 127 }
140 128
141 Statement unfoldLabeledStatements(Statement node) { 129 Statement visitExpressionStatement(ExpressionStatement node) {
142 while (node is LabeledStatement) { 130 node.expression = visitExpression(node.expression);
143 node = (node as LabeledStatement).body; 131 if (node.expression is VariableUse) {
144 } 132 // The entire expression was pulled into an initializer.
133 // This can happen when the expression was an assignment that was
134 // pulled into the initializer block and replaced by a variable use.
135 // Discard the statement and try to pull in more initializers from
136 // the next statement.
137 destroyVariableUse(node.expression);
138 return visitStatement(node.next);
139 }
140 node.next = visitStatement(node.next);
141 return node;
142 }
143
144 Statement visitIf(If node) {
145 node.condition = visitExpression(node.condition);
146 // We could traverse the branches and pull out pure expressions, but
147 // some pure expressions might be too slow for this to pay off.
148 // A CPS transform should decide when things get hoisted out of branches.
149 return node;
150 }
151
152 Statement visitLabeledStatement(LabeledStatement node) {
153 node.body = visitStatement(node.body);
154 // The 'next' statement might not always get reached, so do not try to
155 // pull expressions up from there.
156 return node;
157 }
158
159 Statement visitWhileTrue(WhileTrue node) {
160 return node;
161 }
162
163 Statement visitWhileCondition(WhileCondition node) {
164 return node;
165 }
166
167 Statement visitTry(Try node) {
145 return node; 168 return node;
146 } 169 }
147 170
148 Expression visitAssign(Assign node) { 171 Expression visitAssign(Assign node) {
149 assert(!seenImpure); 172 bool inImpureContext = impureCounter > 0;
173 bool inBranch = branchCounter > 0;
174
175 // Remember the number of impure expression seen yet, so we can tell if
176 // there are any impure expressions on the right-hand side.
177 int impureBefore = impureCounter;
178 int assignmentsBefore = assignCounter;
150 node.value = visitExpression(node.value); 179 node.value = visitExpression(node.value);
151 if (!assignedVariables.add(node.variable)) { 180 bool rightHandSideIsImpure = (impureCounter > impureBefore);
152 // This is not the first assignment to the variable, so it cannot be 181 bool rightHandSideHasAssign = (assignCounter > assignmentsBefore);
153 // pulled into an initializer. 182
154 // We have to leave the assignment here, and assignments are impure. 183 bool alreadyAssigned = assignArea.containsKey(node.variable);
155 seenImpure = true; 184
185 // An impure right-hand side cannot be pulled out of impure context.
186 // Expressions should not be pulled out of branches.
187 // If this is not the first assignment, it cannot be hoisted.
188 // If the right-hand side contains an unhoistable assignment, this
189 // assignment cannot be hoisted either.
190 if (inImpureContext && rightHandSideIsImpure ||
191 inBranch ||
192 alreadyAssigned ||
193 rightHandSideHasAssign) {
194 assignArea[node.variable] = AssignArea.Anywhere;
195 ++impureCounter;
196 ++assignCounter;
156 return node; 197 return node;
157 } else { 198 }
158 // Pull the assignment into an initializer. 199
159 // We will leave behind a variable use, which is pure, so we can 200 // Pull the assignment into the initializer. Any side-effects in the
160 // disregard any impure expressions seen in the right-hand side. 201 // right-hand side will move into the initializer block, so reset the
161 seenImpure = false; 202 // impure counter.
162 append(new ExpressionStatement(node, null)); 203 assignArea[node.variable] = AssignArea.Initializer;
163 return new VariableUse(node.variable); 204 impureCounter = impureBefore;
164 } 205 append(new ExpressionStatement(node, null));
165 } 206 return new VariableUse(node.variable);
166 207 }
167 void rewriteList(List<Expression> list) { 208
168 for (int i = 0; i < list.length; i++) { 209 Expression visitVariableUse(VariableUse node) {
169 list[i] = visitExpression(list[i]); 210 if (assignArea[node.variable] == AssignArea.Anywhere) {
170 if (seenImpure) return; 211 // There is a reaching definition outside the initializer block.
171 } 212 ++impureCounter;
172 } 213 }
173 214 return node;
174 Expression visitInvokeStatic(InvokeStatic node) { 215 }
175 rewriteList(node.arguments); 216
176 seenImpure = true; 217 void rewriteList(List<Expression> nodes) {
177 return node; 218 for (int i = 0; i < nodes.length; ++i) {
219 nodes[i] = visitExpression(nodes[i]);
220 }
178 } 221 }
179 222
180 Expression visitInvokeMethod(InvokeMethod node) { 223 Expression visitInvokeMethod(InvokeMethod node) {
181 node.receiver = visitExpression(node.receiver); 224 node.receiver = visitExpression(node.receiver);
182 if (seenImpure) return node; 225 if (!node.receiverIsNotNull) {
226 // If the receiver is null, the method lookup throws.
227 ++impureCounter;
228 }
183 rewriteList(node.arguments); 229 rewriteList(node.arguments);
184 seenImpure = true; 230 ++impureCounter;
231 return node;
232 }
233
234 Expression visitInvokeStatic(InvokeStatic node) {
235 super.visitInvokeStatic(node);
236 ++impureCounter;
185 return node; 237 return node;
186 } 238 }
187 239
188 Expression visitInvokeMethodDirectly(InvokeMethodDirectly node) { 240 Expression visitInvokeMethodDirectly(InvokeMethodDirectly node) {
189 node.receiver = visitExpression(node.receiver); 241 super.visitInvokeMethodDirectly(node);
190 if (seenImpure) return node; 242 ++impureCounter;
191 rewriteList(node.arguments);
192 seenImpure = true;
193 return node; 243 return node;
194 } 244 }
195 245
196 Expression visitInvokeConstructor(InvokeConstructor node) { 246 Expression visitInvokeConstructor(InvokeConstructor node) {
197 rewriteList(node.arguments); 247 super.visitInvokeConstructor(node);
198 seenImpure = true; 248 ++impureCounter;
199 return node;
200 }
201
202 Expression visitConcatenateStrings(ConcatenateStrings node) {
203 rewriteList(node.arguments);
204 seenImpure = true;
205 return node;
206 }
207
208 Expression visitTypeExpression(TypeExpression node) {
209 rewriteList(node.arguments);
210 return node; 249 return node;
211 } 250 }
212 251
213 Expression visitConditional(Conditional node) { 252 Expression visitConditional(Conditional node) {
214 node.condition = visitExpression(node.condition); 253 node.condition = visitExpression(node.condition);
215 if (seenImpure) return node; 254 // Visit the branches to detect impure subexpressions, but do not pull
255 // expressions out of the branch.
256 ++branchCounter;
216 node.thenExpression = visitExpression(node.thenExpression); 257 node.thenExpression = visitExpression(node.thenExpression);
217 if (seenImpure) return node;
218 node.elseExpression = visitExpression(node.elseExpression); 258 node.elseExpression = visitExpression(node.elseExpression);
259 --branchCounter;
219 return node; 260 return node;
220 } 261 }
221 262
222 Expression visitLogicalOperator(LogicalOperator node) { 263 Expression visitLogicalOperator(LogicalOperator node) {
223 node.left = visitExpression(node.left); 264 node.left = visitExpression(node.left);
224 if (seenImpure) return node; 265 ++branchCounter;
225 node.right = visitExpression(node.right); 266 node.right = visitExpression(node.right);
267 --branchCounter;
226 return node; 268 return node;
227 } 269 }
228 270
229 Expression visitLiteralList(LiteralList node) { 271 Expression visitLiteralList(LiteralList node) {
230 rewriteList(node.values); 272 super.visitLiteralList(node);
231 if (node.type != null) seenImpure = true; // Type casts can throw. 273 if (node.type != null) {
274 ++impureCounter; // Type casts can throw.
275 }
232 return node; 276 return node;
233 } 277 }
234 278
235 Expression visitLiteralMap(LiteralMap node) { 279 Expression visitLiteralMap(LiteralMap node) {
236 for (LiteralMapEntry entry in node.entries) { 280 super.visitLiteralMap(node);
237 entry.key = visitExpression(entry.key); 281 if (node.type != null) {
238 if (seenImpure) return node; 282 ++impureCounter; // Type casts can throw.
239 entry.value = visitExpression(entry.value); 283 }
240 if (seenImpure) return node;
241 }
242 if (node.type != null) seenImpure = true; // Type casts can throw.
243 return node; 284 return node;
244 } 285 }
245 286
246 Expression visitTypeOperator(TypeOperator node) { 287 Expression visitTypeOperator(TypeOperator node) {
247 node.value = visitExpression(node.value); 288 super.visitTypeOperator(node);
248 if (seenImpure) return node; 289 if (!node.isTypeTest) {
249 rewriteList(node.typeArguments); 290 ++impureCounter; // Type casts can throw.
250 if (!node.isTypeTest) seenImpure = true; // Type cast can throw. 291 }
292 return node;
293 }
294
295 Expression visitGetField(GetField node) {
296 super.visitGetField(node);
297 ++impureCounter;
298 return node;
299 }
300
301 Expression visitSetField(SetField node) {
302 super.visitSetField(node);
303 ++impureCounter;
304 return node;
305 }
306
307 Expression visitGetStatic(GetStatic node) {
308 ++impureCounter;
309 return node;
310 }
311
312 Expression visitSetStatic(SetStatic node) {
313 super.visitSetStatic(node);
314 ++impureCounter;
251 return node; 315 return node;
252 } 316 }
253 317
254 void visitInnerFunction(FunctionDefinition node) { 318 void visitInnerFunction(FunctionDefinition node) {
255 new PullIntoInitializers().rewrite(node); 319 new PullIntoInitializers().rewrite(node);
256 } 320 }
257 321
258 Expression visitFunctionExpression(FunctionExpression node) { 322 Expression visitFunctionExpression(FunctionExpression node) {
259 visitInnerFunction(node.definition); 323 visitInnerFunction(node.definition);
260 return node; 324 return node;
261 } 325 }
262 326
263 Expression visitGetField(GetField node) {
264 node.object = visitExpression(node.object);
265 seenImpure = true;
266 return node;
267 }
268
269 Expression visitSetField(SetField node) {
270 node.object = visitExpression(node.object);
271 if (seenImpure) return node;
272 node.value = visitExpression(node.value);
273 seenImpure = true;
274 return node;
275 }
276
277 Expression visitGetStatic(GetStatic node) {
278 seenImpure = true;
279 return node;
280 }
281
282 Expression visitSetStatic(SetStatic node) {
283 node.value = visitExpression(node.value);
284 seenImpure = true;
285 return node;
286 }
287
288 Expression visitCreateBox(CreateBox node) {
289 return node;
290 }
291
292 Expression visitCreateInstance(CreateInstance node) {
293 rewriteList(node.arguments);
294 return node;
295 }
296
297 Expression visitReifyRuntimeType(ReifyRuntimeType node) {
298 node.value = visitExpression(node.value);
299 return node;
300 }
301
302 Expression visitReadTypeVariable(ReadTypeVariable node) {
303 node.target = visitExpression(node.target);
304 return node;
305 }
306
307 Expression visitConstant(Constant node) {
308 return node;
309 }
310
311 Expression visitThis(This node) {
312 return node;
313 }
314
315 Expression visitNot(Not node) {
316 node.operand = visitExpression(node.operand);
317 return node;
318 }
319
320 Expression visitVariableUse(VariableUse node) {
321 return node;
322 }
323
324 Expression visitCreateInvocationMirror(CreateInvocationMirror node) {
325 rewriteList(node.arguments);
326 return node;
327 }
328
329 Expression visitApplyBuiltinOperator(ApplyBuiltinOperator node) { 327 Expression visitApplyBuiltinOperator(ApplyBuiltinOperator node) {
330 rewriteList(node.arguments); 328 rewriteList(node.arguments);
331 return node; 329 return node;
332 } 330 }
333 } 331 }
OLDNEW
« no previous file with comments | « no previous file | tests/co19/co19-dart2js.status » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698