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 import 'optimization.dart' show Pass; | |
| 6 import '../tree_ir_nodes.dart'; | |
| 7 | |
| 8 /// Pulls assignment expressions to the top of the function body so they can be | |
| 9 /// translated into declaration-site variable initializaters. | |
| 10 /// | |
| 11 /// This reverts the assignment expression propagation performed by | |
| 12 /// [StatementRewriter] in cases where it not beneficial. | |
| 13 /// | |
| 14 /// EXAMPLE: | |
| 15 /// | |
| 16 /// var x = foo(), | |
| 17 /// y = bar(x); | |
| 18 /// | |
| 19 /// ==> [StatementRewriter] | |
| 20 /// | |
| 21 /// var x, | |
| 22 /// y = bar(x = foo()); | |
| 23 /// | |
| 24 /// ==> [PullIntoInitializers] restores the initializer for x | |
| 25 /// | |
| 26 /// var x = foo(), | |
| 27 /// y = bar(x); | |
| 28 /// | |
| 29 /// | |
| 30 /// Sometimes the assignment propagation will trigger another optimization | |
| 31 /// in the [StatementRewriter] which then prevents [PullIntoInitializers] from | |
| 32 /// restoring the initializer. This is acceptable, since most optimizations | |
| 33 /// at that level are better than restoring an initializer. | |
| 34 /// | |
| 35 /// EXAMPLE: | |
| 36 /// | |
| 37 /// var x = foo(), | |
| 38 /// y = bar(); | |
| 39 /// baz(x, y, y); | |
| 40 /// | |
| 41 /// ==> [StatementRewriter] | |
| 42 /// | |
| 43 /// var y; | |
| 44 /// baz(foo(), y = bar(), y); | |
| 45 /// | |
| 46 /// [PullIntoInitializers] cannot pull `y` into an initializer because | |
| 47 /// the impure expressions `foo()` and `bar()` would then be swapped. | |
| 48 /// | |
| 49 class PullIntoInitializers implements Pass { | |
| 50 String get passName => 'Pull into initializers'; | |
| 51 | |
| 52 void rewrite(RootNode node) { | |
| 53 node.replaceEachBody((Statement body) { | |
| 54 return new BodyRewriter().rewriteBody(node.parameters, body); | |
| 55 }); | |
| 56 } | |
| 57 } | |
| 58 | |
| 59 class BodyRewriter { | |
| 60 Set<Variable> assignedVariables = new Set<Variable>(); | |
| 61 | |
| 62 /// The fragment between [first] and [last] holds the statements | |
| 63 /// we pulled into the initializer block. | |
|
Kevin Millikin (Google)
2015/04/14 08:41:00
Here and below 'initializer block' is non-standard
asgerf
2015/04/14 10:12:14
Done.
| |
| 64 /// | |
| 65 /// It is empty when both are `null`. | |
| 66 Statement first, last; | |
| 67 | |
| 68 /// Appends a statement to the initializer block. | |
| 69 void append(Statement node) { | |
| 70 if (first == null) { | |
| 71 first = last = node; | |
| 72 } else { | |
| 73 last.next = node; | |
| 74 last = node; | |
| 75 } | |
| 76 } | |
| 77 | |
| 78 /// Pulls assignment expressions from [node] into the initializer block | |
| 79 /// by calling [append]. | |
| 80 /// | |
| 81 /// Returns a transformed expression where the pulled assignments are | |
| 82 /// replaced by variable uses. | |
| 83 Expression rewriteExpression(Expression node) { | |
| 84 return new ExpressionToInitializerVisitor(this).visitExpression(node); | |
| 85 } | |
| 86 | |
| 87 Statement rewriteBody(List<Variable> parameters, Statement node) { | |
| 88 assignedVariables.addAll(parameters); | |
| 89 | |
| 90 // [node] represents the first statement after the initializer block. | |
| 91 // Repeatedly pull assignment statements into the initializer block. | |
| 92 while (node is ExpressionStatement) { | |
| 93 ExpressionStatement stmt = node; | |
| 94 stmt.expression = rewriteExpression(stmt.expression); | |
| 95 if (stmt.expression is VariableUse) { | |
| 96 // The entire expression was pulled into an initializer. | |
| 97 // This can happen when the expression was an assignment that was | |
| 98 // pulled into the initializer block and replaced by a variable use. | |
| 99 // Discard the statement and try to pull in more initializers from | |
| 100 // the next statement. | |
| 101 destroyVariableUse(stmt.expression); | |
| 102 node = stmt.next; | |
|
Kevin Millikin (Google)
2015/04/14 08:41:00
It's a bit clearer to introduce a Statement 'curre
asgerf
2015/04/14 10:12:14
I don't like stale variables that you are likely t
| |
| 103 } else { | |
| 104 // The whole expression could not be pulled into an initializer, so we | |
| 105 // have reached the end of the initializer block. | |
| 106 break; | |
| 107 } | |
| 108 } | |
| 109 | |
| 110 // [If] and [Return] statements terminate the initializer block, but the | |
| 111 // initial expression they contain may be pulled up into an initializer. | |
| 112 // It's ok to pull an assignment across a label so look for the first | |
| 113 // non-labeled statement and try to pull its initial subexpression. | |
| 114 Statement entryNode = unfoldLabeledStatements(node); | |
| 115 if (entryNode is If) { | |
| 116 entryNode.condition = rewriteExpression(entryNode.condition); | |
| 117 } else if (entryNode is Return) { | |
| 118 entryNode.value = rewriteExpression(entryNode.value); | |
| 119 } | |
| 120 | |
| 121 // Join the initializer block with the rest of the body. | |
| 122 append(node); | |
|
Kevin Millikin (Google)
2015/04/14 08:41:00
The code is backwards from the comment, so I'd mak
asgerf
2015/04/14 10:12:14
Removed the comment. Even less necessary after the
| |
| 123 | |
| 124 assert(first != null); // Because we just appended node. | |
| 125 return first; | |
| 126 } | |
| 127 | |
| 128 void destroyVariableUse(VariableUse node) { | |
| 129 --node.variable.readCount; | |
| 130 } | |
| 131 | |
| 132 Statement unfoldLabeledStatements(Statement node) { | |
| 133 while (node is LabeledStatement) { | |
| 134 node = (node as LabeledStatement).body; | |
| 135 } | |
| 136 return node; | |
| 137 } | |
| 138 } | |
| 139 | |
| 140 | |
| 141 class ExpressionToInitializerVisitor extends ExpressionVisitor<Expression> { | |
|
Kevin Millikin (Google)
2015/04/14 08:41:00
On the one hand, this class only has one bit of st
asgerf
2015/04/14 10:12:14
I don't think it's simpler, but either way is fine
| |
| 142 BodyRewriter parent; | |
| 143 | |
| 144 /// True if an impure expression has been returned by visitExpression. | |
| 145 /// | |
| 146 /// Expressions cannot be pulled into an initializer if this might reorder | |
| 147 /// impure expressions. | |
| 148 /// | |
| 149 /// A visit method may not be called while this flag is set, meaning all | |
| 150 /// visitor methods must check the flag between visiting subexpressions. | |
| 151 bool seenImpure = false; | |
| 152 | |
| 153 ExpressionToInitializerVisitor(this.parent); | |
| 154 | |
| 155 Expression visitAssign(Assign node) { | |
| 156 assert(!seenImpure); | |
| 157 node.value = visitExpression(node.value); | |
| 158 if (!parent.assignedVariables.add(node.variable)) { | |
| 159 // This is not the first assignment to the variable, so it cannot be | |
| 160 // pulled into an initializer. | |
| 161 // We have to leave the assignment here, and assignments are impure. | |
| 162 seenImpure = true; | |
| 163 return node; | |
| 164 } else { | |
| 165 // Pull the assignment into an initializer. | |
| 166 // We will leave behind a variable use, which is pure, so we can | |
| 167 // disregard any impure expressions seen in the right-side. | |
|
Kevin Millikin (Google)
2015/04/14 08:41:00
'right-hand side' or 'right side' (no hyphen).
asgerf
2015/04/14 10:12:14
Done.
| |
| 168 seenImpure = false; | |
| 169 parent.append(new ExpressionStatement(node, null)); | |
| 170 return new VariableUse(node.variable); | |
| 171 } | |
| 172 } | |
| 173 | |
| 174 void rewriteList(List<Expression> list) { | |
| 175 for (int i = 0; i < list.length; i++) { | |
| 176 list[i] = visitExpression(list[i]); | |
| 177 if (seenImpure) return; | |
| 178 } | |
| 179 } | |
| 180 | |
| 181 Expression visitInvokeStatic(InvokeStatic node) { | |
| 182 rewriteList(node.arguments); | |
| 183 seenImpure = true; | |
| 184 return node; | |
| 185 } | |
| 186 | |
| 187 Expression visitInvokeMethod(InvokeMethod node) { | |
| 188 node.receiver = visitExpression(node.receiver); | |
| 189 if (seenImpure) return node; | |
| 190 rewriteList(node.arguments); | |
| 191 seenImpure = true; | |
| 192 return node; | |
| 193 } | |
| 194 | |
| 195 Expression visitInvokeMethodDirectly(InvokeMethodDirectly node) { | |
| 196 node.receiver = visitExpression(node.receiver); | |
| 197 if (seenImpure) return node; | |
| 198 rewriteList(node.arguments); | |
| 199 seenImpure = true; | |
| 200 return node; | |
| 201 } | |
| 202 | |
| 203 Expression visitInvokeConstructor(InvokeConstructor node) { | |
| 204 rewriteList(node.arguments); | |
| 205 seenImpure = true; | |
| 206 return node; | |
| 207 } | |
| 208 | |
| 209 Expression visitConcatenateStrings(ConcatenateStrings node) { | |
| 210 rewriteList(node.arguments); | |
| 211 seenImpure = true; | |
| 212 return node; | |
| 213 } | |
| 214 | |
| 215 Expression visitTypeExpression(TypeExpression node) { | |
| 216 rewriteList(node.arguments); | |
| 217 return node; | |
| 218 } | |
| 219 | |
| 220 Expression visitConditional(Conditional node) { | |
| 221 node.condition = visitExpression(node.condition); | |
| 222 if (seenImpure) return node; | |
| 223 node.thenExpression = visitExpression(node.thenExpression); | |
| 224 if (seenImpure) return node; | |
| 225 node.elseExpression = visitExpression(node.elseExpression); | |
| 226 return node; | |
| 227 } | |
| 228 | |
| 229 Expression visitLogicalOperator(LogicalOperator node) { | |
| 230 node.left = visitExpression(node.left); | |
| 231 if (seenImpure) return node; | |
| 232 node.right = visitExpression(node.right); | |
| 233 return node; | |
| 234 } | |
| 235 | |
| 236 Expression visitLiteralList(LiteralList node) { | |
| 237 rewriteList(node.values); | |
| 238 if (node.type != null) seenImpure = true; // Type casts can throw. | |
| 239 return node; | |
| 240 } | |
| 241 | |
| 242 Expression visitLiteralMap(LiteralMap node) { | |
| 243 for (LiteralMapEntry entry in node.entries) { | |
| 244 entry.key = visitExpression(entry.key); | |
| 245 if (seenImpure) return node; | |
| 246 entry.value = visitExpression(entry.value); | |
| 247 if (seenImpure) return node; | |
| 248 } | |
| 249 if (node.type != null) seenImpure = true; // Type casts can throw. | |
| 250 return node; | |
| 251 } | |
| 252 | |
| 253 Expression visitTypeOperator(TypeOperator node) { | |
| 254 node.receiver = visitExpression(node.receiver); | |
| 255 if (!node.isTypeTest) seenImpure = true; // Type cast can throw. | |
| 256 return node; | |
| 257 } | |
| 258 | |
| 259 void visitInnerFunction(FunctionDefinition node) { | |
| 260 node.body = new BodyRewriter().rewriteBody(node.parameters, node.body); | |
| 261 } | |
| 262 | |
| 263 Expression visitFunctionExpression(FunctionExpression node) { | |
| 264 visitInnerFunction(node.definition); | |
| 265 return node; | |
| 266 } | |
| 267 | |
| 268 Expression visitGetField(GetField node) { | |
| 269 node.object = visitExpression(node.object); | |
| 270 seenImpure = true; | |
| 271 return node; | |
| 272 } | |
| 273 | |
| 274 Expression visitSetField(SetField node) { | |
| 275 node.object = visitExpression(node.object); | |
| 276 if (seenImpure) return node; | |
| 277 node.value = visitExpression(node.value); | |
| 278 seenImpure = true; | |
| 279 return node; | |
| 280 } | |
| 281 | |
| 282 Expression visitCreateBox(CreateBox node) { | |
| 283 return node; | |
| 284 } | |
| 285 | |
| 286 Expression visitCreateInstance(CreateInstance node) { | |
| 287 rewriteList(node.arguments); | |
| 288 return node; | |
| 289 } | |
| 290 | |
| 291 Expression visitReifyRuntimeType(ReifyRuntimeType node) { | |
| 292 node.value = visitExpression(node.value); | |
| 293 return node; | |
| 294 } | |
| 295 | |
| 296 Expression visitReadTypeVariable(ReadTypeVariable node) { | |
| 297 node.target = visitExpression(node.target); | |
| 298 return node; | |
| 299 } | |
| 300 | |
| 301 Expression visitConstant(Constant node) { | |
| 302 return node; | |
| 303 } | |
| 304 | |
| 305 Expression visitThis(This node) { | |
| 306 return node; | |
| 307 } | |
| 308 | |
| 309 Expression visitReifyTypeVar(ReifyTypeVar node) { | |
| 310 return node; | |
| 311 } | |
| 312 | |
| 313 Expression visitNot(Not node) { | |
| 314 node.operand = visitExpression(node.operand); | |
| 315 return node; | |
| 316 } | |
| 317 | |
| 318 Expression visitVariableUse(VariableUse node) { | |
| 319 return node; | |
| 320 } | |
| 321 } | |
| OLD | NEW |