| 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 extends ExpressionVisitor<Expression> { |
| 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. |
| 64 /// |
| 65 /// The *initializer block* is a sequence of [ExpressionStatement]s with |
| 66 /// [Assign]s that we create in the beginning of the body, with the intent |
| 67 /// that code generation will convert them to variable initializers. |
| 68 /// |
| 69 /// The block is empty when both are `null`. |
| 70 Statement first, last; |
| 71 |
| 72 /// True if an impure expression has been returned by visitExpression. |
| 73 /// |
| 74 /// Expressions cannot be pulled into an initializer if this might reorder |
| 75 /// impure expressions. |
| 76 /// |
| 77 /// A visit method may not be called while this flag is set, meaning all |
| 78 /// visitor methods must check the flag between visiting subexpressions. |
| 79 bool seenImpure; |
| 80 |
| 81 /// Appends a statement to the initializer block. |
| 82 void append(Statement node) { |
| 83 if (first == null) { |
| 84 first = last = node; |
| 85 } else { |
| 86 last.next = node; |
| 87 last = node; |
| 88 } |
| 89 } |
| 90 |
| 91 /// Pulls assignment expressions from [node] into the initializer block |
| 92 /// by calling [append]. |
| 93 /// |
| 94 /// Returns a transformed expression where the pulled assignments are |
| 95 /// replaced by variable uses. |
| 96 Expression rewriteExpression(Expression node) { |
| 97 seenImpure = false; |
| 98 return visitExpression(node); |
| 99 } |
| 100 |
| 101 Statement rewriteBody(List<Variable> parameters, Statement body) { |
| 102 assignedVariables.addAll(parameters); |
| 103 |
| 104 // [body] represents the first statement after the initializer block. |
| 105 // Repeatedly pull assignment statements into the initializer block. |
| 106 while (body is ExpressionStatement) { |
| 107 ExpressionStatement stmt = body; |
| 108 stmt.expression = rewriteExpression(stmt.expression); |
| 109 if (stmt.expression is VariableUse) { |
| 110 // The entire expression was pulled into an initializer. |
| 111 // This can happen when the expression was an assignment that was |
| 112 // pulled into the initializer block and replaced by a variable use. |
| 113 // Discard the statement and try to pull in more initializers from |
| 114 // the next statement. |
| 115 destroyVariableUse(stmt.expression); |
| 116 body = stmt.next; |
| 117 } else { |
| 118 // The whole expression could not be pulled into an initializer, so we |
| 119 // have reached the end of the initializer block. |
| 120 break; |
| 121 } |
| 122 } |
| 123 |
| 124 // [If] and [Return] statements terminate the initializer block, but the |
| 125 // initial expression they contain may be pulled up into an initializer. |
| 126 // It's ok to pull an assignment across a label so look for the first |
| 127 // non-labeled statement and try to pull its initial subexpression. |
| 128 Statement entryNode = unfoldLabeledStatements(body); |
| 129 if (entryNode is If) { |
| 130 entryNode.condition = rewriteExpression(entryNode.condition); |
| 131 } else if (entryNode is Return) { |
| 132 entryNode.value = rewriteExpression(entryNode.value); |
| 133 } |
| 134 |
| 135 append(body); |
| 136 assert(first != null); // Because we just appended the body. |
| 137 return first; |
| 138 } |
| 139 |
| 140 void destroyVariableUse(VariableUse node) { |
| 141 --node.variable.readCount; |
| 142 } |
| 143 |
| 144 Statement unfoldLabeledStatements(Statement node) { |
| 145 while (node is LabeledStatement) { |
| 146 node = (node as LabeledStatement).body; |
| 147 } |
| 148 return node; |
| 149 } |
| 150 |
| 151 Expression visitAssign(Assign node) { |
| 152 assert(!seenImpure); |
| 153 node.value = visitExpression(node.value); |
| 154 if (!assignedVariables.add(node.variable)) { |
| 155 // This is not the first assignment to the variable, so it cannot be |
| 156 // pulled into an initializer. |
| 157 // We have to leave the assignment here, and assignments are impure. |
| 158 seenImpure = true; |
| 159 return node; |
| 160 } else { |
| 161 // Pull the assignment into an initializer. |
| 162 // We will leave behind a variable use, which is pure, so we can |
| 163 // disregard any impure expressions seen in the right-hand side. |
| 164 seenImpure = false; |
| 165 append(new ExpressionStatement(node, null)); |
| 166 return new VariableUse(node.variable); |
| 167 } |
| 168 } |
| 169 |
| 170 void rewriteList(List<Expression> list) { |
| 171 for (int i = 0; i < list.length; i++) { |
| 172 list[i] = visitExpression(list[i]); |
| 173 if (seenImpure) return; |
| 174 } |
| 175 } |
| 176 |
| 177 Expression visitInvokeStatic(InvokeStatic node) { |
| 178 rewriteList(node.arguments); |
| 179 seenImpure = true; |
| 180 return node; |
| 181 } |
| 182 |
| 183 Expression visitInvokeMethod(InvokeMethod node) { |
| 184 node.receiver = visitExpression(node.receiver); |
| 185 if (seenImpure) return node; |
| 186 rewriteList(node.arguments); |
| 187 seenImpure = true; |
| 188 return node; |
| 189 } |
| 190 |
| 191 Expression visitInvokeMethodDirectly(InvokeMethodDirectly node) { |
| 192 node.receiver = visitExpression(node.receiver); |
| 193 if (seenImpure) return node; |
| 194 rewriteList(node.arguments); |
| 195 seenImpure = true; |
| 196 return node; |
| 197 } |
| 198 |
| 199 Expression visitInvokeConstructor(InvokeConstructor node) { |
| 200 rewriteList(node.arguments); |
| 201 seenImpure = true; |
| 202 return node; |
| 203 } |
| 204 |
| 205 Expression visitConcatenateStrings(ConcatenateStrings node) { |
| 206 rewriteList(node.arguments); |
| 207 seenImpure = true; |
| 208 return node; |
| 209 } |
| 210 |
| 211 Expression visitTypeExpression(TypeExpression node) { |
| 212 rewriteList(node.arguments); |
| 213 return node; |
| 214 } |
| 215 |
| 216 Expression visitConditional(Conditional node) { |
| 217 node.condition = visitExpression(node.condition); |
| 218 if (seenImpure) return node; |
| 219 node.thenExpression = visitExpression(node.thenExpression); |
| 220 if (seenImpure) return node; |
| 221 node.elseExpression = visitExpression(node.elseExpression); |
| 222 return node; |
| 223 } |
| 224 |
| 225 Expression visitLogicalOperator(LogicalOperator node) { |
| 226 node.left = visitExpression(node.left); |
| 227 if (seenImpure) return node; |
| 228 node.right = visitExpression(node.right); |
| 229 return node; |
| 230 } |
| 231 |
| 232 Expression visitLiteralList(LiteralList node) { |
| 233 rewriteList(node.values); |
| 234 if (node.type != null) seenImpure = true; // Type casts can throw. |
| 235 return node; |
| 236 } |
| 237 |
| 238 Expression visitLiteralMap(LiteralMap node) { |
| 239 for (LiteralMapEntry entry in node.entries) { |
| 240 entry.key = visitExpression(entry.key); |
| 241 if (seenImpure) return node; |
| 242 entry.value = visitExpression(entry.value); |
| 243 if (seenImpure) return node; |
| 244 } |
| 245 if (node.type != null) seenImpure = true; // Type casts can throw. |
| 246 return node; |
| 247 } |
| 248 |
| 249 Expression visitTypeOperator(TypeOperator node) { |
| 250 node.receiver = visitExpression(node.receiver); |
| 251 if (!node.isTypeTest) seenImpure = true; // Type cast can throw. |
| 252 return node; |
| 253 } |
| 254 |
| 255 void visitInnerFunction(FunctionDefinition node) { |
| 256 node.body = new BodyRewriter().rewriteBody(node.parameters, node.body); |
| 257 } |
| 258 |
| 259 Expression visitFunctionExpression(FunctionExpression node) { |
| 260 visitInnerFunction(node.definition); |
| 261 return node; |
| 262 } |
| 263 |
| 264 Expression visitGetField(GetField node) { |
| 265 node.object = visitExpression(node.object); |
| 266 seenImpure = true; |
| 267 return node; |
| 268 } |
| 269 |
| 270 Expression visitSetField(SetField node) { |
| 271 node.object = visitExpression(node.object); |
| 272 if (seenImpure) return node; |
| 273 node.value = visitExpression(node.value); |
| 274 seenImpure = true; |
| 275 return node; |
| 276 } |
| 277 |
| 278 Expression visitCreateBox(CreateBox node) { |
| 279 return node; |
| 280 } |
| 281 |
| 282 Expression visitCreateInstance(CreateInstance node) { |
| 283 rewriteList(node.arguments); |
| 284 return node; |
| 285 } |
| 286 |
| 287 Expression visitReifyRuntimeType(ReifyRuntimeType node) { |
| 288 node.value = visitExpression(node.value); |
| 289 return node; |
| 290 } |
| 291 |
| 292 Expression visitReadTypeVariable(ReadTypeVariable node) { |
| 293 node.target = visitExpression(node.target); |
| 294 return node; |
| 295 } |
| 296 |
| 297 Expression visitConstant(Constant node) { |
| 298 return node; |
| 299 } |
| 300 |
| 301 Expression visitThis(This node) { |
| 302 return node; |
| 303 } |
| 304 |
| 305 Expression visitReifyTypeVar(ReifyTypeVar node) { |
| 306 return node; |
| 307 } |
| 308 |
| 309 Expression visitNot(Not node) { |
| 310 node.operand = visitExpression(node.operand); |
| 311 return node; |
| 312 } |
| 313 |
| 314 Expression visitVariableUse(VariableUse node) { |
| 315 return node; |
| 316 } |
| 317 } |
| OLD | NEW |