| OLD | NEW |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, 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.statement_rewriter; | 5 library tree_ir.optimization.statement_rewriter; |
| 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 /** | 10 /** |
| 11 * Performs the following transformations on the tree: | 11 * Translates to direct-style. |
| 12 * |
| 13 * In addition to the general IR constraints (see [CheckTreeIntegrity]), |
| 14 * the input is assumed to satisfy the following criteria: |
| 15 * |
| 16 * All expressions other than those nested in [Assign] or [ExpressionStatement] |
| 17 * must be simple. A [VariableUse] and [This] is a simple expression. |
| 18 * The right-hand of an [Assign] may not be an [Assign]. |
| 19 * |
| 20 * Moreover, every variable must either be an SSA variable or a mutable |
| 21 * variable, and must satisfy the corresponding criteria: |
| 22 * |
| 23 * SSA VARIABLE: |
| 24 * An SSA variable must have a unique definition site, which is either an |
| 25 * assignment or label. In case of a label, its target must act as the unique |
| 26 * reaching definition of that variable at all uses of the variable and at |
| 27 * all other label targets where the variable is in scope. |
| 28 * |
| 29 * (The second criterion is to ensure that we can move a use of an SSA variable |
| 30 * across a label without changing its reaching definition). |
| 31 * |
| 32 * MUTABLE VARIABLE: |
| 33 * Uses of mutable variables are considered complex expressions, and hence must |
| 34 * not be nested in other expressions. Assignments to mutable variables must |
| 35 * have simple right-hand sides. |
| 36 * |
| 37 * ---- |
| 38 * |
| 39 * This pass performs the following transformations on the tree: |
| 12 * - Assignment inlining | 40 * - Assignment inlining |
| 13 * - Assignment expression propagation | 41 * - Assignment expression propagation |
| 14 * - If-to-conditional conversion | 42 * - If-to-conditional conversion |
| 15 * - Flatten nested ifs | 43 * - Flatten nested ifs |
| 16 * - Break inlining | 44 * - Break inlining |
| 17 * - Redirect breaks | 45 * - Redirect breaks |
| 18 * | 46 * |
| 19 * The above transformations all eliminate statements from the tree, and may | 47 * The above transformations all eliminate statements from the tree, and may |
| 20 * introduce redexes of each other. | 48 * introduce redexes of each other. |
| 21 * | 49 * |
| (...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 106 * {... jump L1 ...} | 134 * {... jump L1 ...} |
| 107 * | 135 * |
| 108 * This may trigger a flattening of nested ifs in case the eliminated label | 136 * This may trigger a flattening of nested ifs in case the eliminated label |
| 109 * separated two ifs. | 137 * separated two ifs. |
| 110 */ | 138 */ |
| 111 class StatementRewriter extends Transformer implements Pass { | 139 class StatementRewriter extends Transformer implements Pass { |
| 112 String get passName => 'Statement rewriter'; | 140 String get passName => 'Statement rewriter'; |
| 113 | 141 |
| 114 @override | 142 @override |
| 115 void rewrite(FunctionDefinition node) { | 143 void rewrite(FunctionDefinition node) { |
| 144 node.parameters.forEach(pushDominatingAssignment); |
| 116 node.body = visitStatement(node.body); | 145 node.body = visitStatement(node.body); |
| 146 node.parameters.forEach(popDominatingAssignment); |
| 117 } | 147 } |
| 118 | 148 |
| 119 /// The most recently evaluated impure expressions, with the most recent | 149 /// The most recently evaluated impure expressions, with the most recent |
| 120 /// expression being last. | 150 /// expression being last. |
| 121 /// | 151 /// |
| 122 /// Most importantly, this contains [Assign] expressions that we attempt to | 152 /// Most importantly, this contains [Assign] expressions that we attempt to |
| 123 /// inline at their use site. It also contains other impure expressions that | 153 /// inline at their use site. It also contains other impure expressions that |
| 124 /// we can propagate to a variable use if they are known to return the value | 154 /// we can propagate to a variable use if they are known to return the value |
| 125 /// of that variable. | 155 /// of that variable. |
| 126 /// | 156 /// |
| (...skipping 11 matching lines...) Expand all Loading... |
| 138 | 168 |
| 139 /// Substitution map for labels. Any break to a label L should be substituted | 169 /// Substitution map for labels. Any break to a label L should be substituted |
| 140 /// for a break to L' if L maps to L'. | 170 /// for a break to L' if L maps to L'. |
| 141 Map<Label, Jump> labelRedirects = <Label, Jump>{}; | 171 Map<Label, Jump> labelRedirects = <Label, Jump>{}; |
| 142 | 172 |
| 143 /// Number of uses of the given variable that are still unseen. | 173 /// Number of uses of the given variable that are still unseen. |
| 144 /// Used to detect the first use of a variable (since we do backwards | 174 /// Used to detect the first use of a variable (since we do backwards |
| 145 /// traversal, the first use is the last one seen). | 175 /// traversal, the first use is the last one seen). |
| 146 Map<Variable, int> unseenUses = <Variable, int>{}; | 176 Map<Variable, int> unseenUses = <Variable, int>{}; |
| 147 | 177 |
| 178 /// Number of assignments to a given variable that dominate the current |
| 179 /// position. |
| 180 /// |
| 181 /// Pure expressions will not be inlined if it uses a variable with more than |
| 182 /// one dominating assignment, because the reaching definition of the used |
| 183 /// variable might have changed since it was put in the environment. |
| 184 final Map<Variable, int> dominatingAssignments = <Variable, int>{}; |
| 185 |
| 148 /// Rewriter for methods. | 186 /// Rewriter for methods. |
| 149 StatementRewriter() : constantEnvironment = <Variable, Expression>{}; | 187 StatementRewriter() : constantEnvironment = <Variable, Expression>{}; |
| 150 | 188 |
| 151 /// Rewriter for nested functions. | 189 /// Rewriter for nested functions. |
| 152 StatementRewriter.nested(StatementRewriter parent) | 190 StatementRewriter.nested(StatementRewriter parent) |
| 153 : constantEnvironment = parent.constantEnvironment, | 191 : constantEnvironment = parent.constantEnvironment, |
| 154 unseenUses = parent.unseenUses; | 192 unseenUses = parent.unseenUses; |
| 155 | 193 |
| 156 /// A set of labels that can be safely inlined at their use. | 194 /// A set of labels that can be safely inlined at their use. |
| 157 /// | 195 /// |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 189 } | 227 } |
| 190 | 228 |
| 191 /// If the given expression always returns the value of one of its | 229 /// If the given expression always returns the value of one of its |
| 192 /// subexpressions, and that subexpression is a variable use, returns that | 230 /// subexpressions, and that subexpression is a variable use, returns that |
| 193 /// variable. Otherwise `null`. | 231 /// variable. Otherwise `null`. |
| 194 Variable getRightHand(Expression e) { | 232 Variable getRightHand(Expression e) { |
| 195 Expression value = getValueSubexpression(e); | 233 Expression value = getValueSubexpression(e); |
| 196 return value is VariableUse ? value.variable : null; | 234 return value is VariableUse ? value.variable : null; |
| 197 } | 235 } |
| 198 | 236 |
| 237 /// True if the given expression (taken from [constantEnvironment]) uses a |
| 238 /// variable that might have been reassigned since [node] was evaluated. |
| 239 bool hasUnsafeVariableUse(Expression node) { |
| 240 bool wasFound = false; |
| 241 VariableUseVisitor.visit(node, (VariableUse use) { |
| 242 if (dominatingAssignments[use.variable] > 1) { |
| 243 wasFound = true; |
| 244 } |
| 245 }); |
| 246 return wasFound; |
| 247 } |
| 248 |
| 249 void pushDominatingAssignment(Variable variable) { |
| 250 if (variable != null) { |
| 251 dominatingAssignments.putIfAbsent(variable, () => 0); |
| 252 ++dominatingAssignments[variable]; |
| 253 } |
| 254 } |
| 255 |
| 256 void popDominatingAssignment(Variable variable) { |
| 257 if (variable != null) { |
| 258 --dominatingAssignments[variable]; |
| 259 } |
| 260 } |
| 261 |
| 199 @override | 262 @override |
| 200 Expression visitVariableUse(VariableUse node) { | 263 Expression visitVariableUse(VariableUse node) { |
| 201 // Count of number of unseen uses remaining. | 264 // Count of number of unseen uses remaining. |
| 202 unseenUses.putIfAbsent(node.variable, () => node.variable.readCount); | 265 unseenUses.putIfAbsent(node.variable, () => node.variable.readCount); |
| 203 --unseenUses[node.variable]; | 266 --unseenUses[node.variable]; |
| 204 | 267 |
| 205 // We traverse the tree right-to-left, so when we have seen all uses, | 268 // We traverse the tree right-to-left, so when we have seen all uses, |
| 206 // it means we are looking at the first use. | 269 // it means we are looking at the first use. |
| 207 assert(unseenUses[node.variable] < node.variable.readCount); | 270 assert(unseenUses[node.variable] < node.variable.readCount); |
| 208 assert(unseenUses[node.variable] >= 0); | 271 assert(unseenUses[node.variable] >= 0); |
| 209 bool isFirstUse = unseenUses[node.variable] == 0; | 272 bool isFirstUse = unseenUses[node.variable] == 0; |
| 210 | 273 |
| 211 // Propagate constant to use site. | 274 // Propagate constant to use site. |
| 212 Expression constant = constantEnvironment[node.variable]; | 275 Expression constant = constantEnvironment[node.variable]; |
| 213 if (constant != null) { | 276 if (constant != null && !hasUnsafeVariableUse(constant)) { |
| 214 --node.variable.readCount; | 277 --node.variable.readCount; |
| 215 return visitExpression(constant); | 278 return visitExpression(constant); |
| 216 } | 279 } |
| 217 | 280 |
| 218 // Try to propagate another expression into this variable use. | 281 // Try to propagate another expression into this variable use. |
| 219 if (!environment.isEmpty) { | 282 if (!environment.isEmpty) { |
| 220 Expression binding = environment.last; | 283 Expression binding = environment.last; |
| 221 | 284 |
| 222 // Is this variable assigned by the most recently evaluated impure | 285 // Is this variable assigned by the most recently evaluated impure |
| 223 // expression? | 286 // expression? |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 294 } | 357 } |
| 295 | 358 |
| 296 /// True if [node] is an assignment that can be propagated as a constant. | 359 /// True if [node] is an assignment that can be propagated as a constant. |
| 297 bool isEffectivelyConstantAssignment(Expression node) { | 360 bool isEffectivelyConstantAssignment(Expression node) { |
| 298 return node is Assign && | 361 return node is Assign && |
| 299 node.variable.writeCount == 1 && | 362 node.variable.writeCount == 1 && |
| 300 isEffectivelyConstant(node.value); | 363 isEffectivelyConstant(node.value); |
| 301 } | 364 } |
| 302 | 365 |
| 303 Statement visitExpressionStatement(ExpressionStatement stmt) { | 366 Statement visitExpressionStatement(ExpressionStatement stmt) { |
| 367 Variable leftHand = getLeftHand(stmt.expression); |
| 368 pushDominatingAssignment(leftHand); |
| 304 if (isEffectivelyConstantAssignment(stmt.expression) && | 369 if (isEffectivelyConstantAssignment(stmt.expression) && |
| 305 !usesRecentlyAssignedVariable(stmt.expression)) { | 370 !usesRecentlyAssignedVariable(stmt.expression)) { |
| 306 Assign assign = stmt.expression; | 371 Assign assign = stmt.expression; |
| 307 // Handle constant assignments specially. | 372 // Handle constant assignments specially. |
| 308 // They are always safe to propagate (though we should avoid duplication). | 373 // They are always safe to propagate (though we should avoid duplication). |
| 309 // Moreover, they should not prevent other expressions from propagating. | 374 // Moreover, they should not prevent other expressions from propagating. |
| 310 if (assign.variable.readCount == 1) { | 375 if (assign.variable.readCount == 1) { |
| 311 // A single-use constant should always be propagted to its use site. | 376 // A single-use constant should always be propagated to its use site. |
| 312 constantEnvironment[assign.variable] = assign.value; | 377 constantEnvironment[assign.variable] = assign.value; |
| 313 --assign.variable.writeCount; | 378 Statement next = visitStatement(stmt.next); |
| 314 return visitStatement(stmt.next); | 379 popDominatingAssignment(leftHand); |
| 380 if (assign.variable.readCount > 0) { |
| 381 // The assignment could not be propagated. |
| 382 assign.value = visitExpression(assign.value); |
| 383 stmt.next = next; |
| 384 return stmt; |
| 385 } else { |
| 386 --assign.variable.writeCount; |
| 387 return next; |
| 388 } |
| 315 } else { | 389 } else { |
| 316 // With more than one use, we cannot propagate the constant. | 390 // With more than one use, we cannot propagate the constant. |
| 317 // Visit the following statement without polluting [environment] so | 391 // Visit the following statement without polluting [environment] so |
| 318 // that any preceding non-constant assignments might still propagate. | 392 // that any preceding non-constant assignments might still propagate. |
| 319 stmt.next = visitStatement(stmt.next); | 393 stmt.next = visitStatement(stmt.next); |
| 394 popDominatingAssignment(leftHand); |
| 320 assign.value = visitExpression(assign.value); | 395 assign.value = visitExpression(assign.value); |
| 321 return stmt; | 396 return stmt; |
| 322 } | 397 } |
| 323 } | 398 } |
| 324 // Try to propagate the expression, and block previous impure expressions | 399 // Try to propagate the expression, and block previous impure expressions |
| 325 // until this has propagated. | 400 // until this has propagated. |
| 326 environment.add(stmt.expression); | 401 environment.add(stmt.expression); |
| 327 stmt.next = visitStatement(stmt.next); | 402 stmt.next = visitStatement(stmt.next); |
| 403 popDominatingAssignment(leftHand); |
| 328 if (!environment.isEmpty && environment.last == stmt.expression) { | 404 if (!environment.isEmpty && environment.last == stmt.expression) { |
| 329 // Retain the expression statement. | 405 // Retain the expression statement. |
| 330 environment.removeLast(); | 406 environment.removeLast(); |
| 331 stmt.expression = visitExpression(stmt.expression); | 407 stmt.expression = visitExpression(stmt.expression); |
| 332 return stmt; | 408 return stmt; |
| 333 } else { | 409 } else { |
| 334 // Expression was propagated into the successor. | 410 // Expression was propagated into the successor. |
| 335 return stmt.next; | 411 return stmt.next; |
| 336 } | 412 } |
| 337 } | 413 } |
| (...skipping 204 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 542 // Not introduced yet | 618 // Not introduced yet |
| 543 throw "Unexpected WhileCondition in StatementRewriter"; | 619 throw "Unexpected WhileCondition in StatementRewriter"; |
| 544 } | 620 } |
| 545 | 621 |
| 546 Statement visitTry(Try node) { | 622 Statement visitTry(Try node) { |
| 547 inEmptyEnvironment(() { | 623 inEmptyEnvironment(() { |
| 548 Set<Label> saved = safeForInlining; | 624 Set<Label> saved = safeForInlining; |
| 549 safeForInlining = new Set<Label>(); | 625 safeForInlining = new Set<Label>(); |
| 550 node.tryBody = visitStatement(node.tryBody); | 626 node.tryBody = visitStatement(node.tryBody); |
| 551 safeForInlining = saved; | 627 safeForInlining = saved; |
| 628 node.catchParameters.forEach(pushDominatingAssignment); |
| 552 node.catchBody = visitStatement(node.catchBody); | 629 node.catchBody = visitStatement(node.catchBody); |
| 630 node.catchParameters.forEach(popDominatingAssignment); |
| 553 }); | 631 }); |
| 554 return node; | 632 return node; |
| 555 } | 633 } |
| 556 | 634 |
| 557 Expression visitConstant(Constant node) { | 635 Expression visitConstant(Constant node) { |
| 558 return node; | 636 return node; |
| 559 } | 637 } |
| 560 | 638 |
| 561 Expression visitThis(This node) { | 639 Expression visitThis(This node) { |
| 562 return node; | 640 return node; |
| (...skipping 185 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 748 // expressions if the statements could not be combined. | 826 // expressions if the statements could not be combined. |
| 749 | 827 |
| 750 // Combine the expressions. | 828 // Combine the expressions. |
| 751 CombinedExpressions values = | 829 CombinedExpressions values = |
| 752 combineAsConditional(s.expression, t.expression, condition); | 830 combineAsConditional(s.expression, t.expression, condition); |
| 753 | 831 |
| 754 // Put this into the environment and try to combine the statements. | 832 // Put this into the environment and try to combine the statements. |
| 755 // We are not in risk of reprocessing the original subexpressions because | 833 // We are not in risk of reprocessing the original subexpressions because |
| 756 // the combined expression will always hide them inside a Conditional. | 834 // the combined expression will always hide them inside a Conditional. |
| 757 environment.add(values.combined); | 835 environment.add(values.combined); |
| 836 |
| 837 Variable leftHand = getLeftHand(values.combined); |
| 838 pushDominatingAssignment(leftHand); |
| 758 Statement next = combineStatements(s.next, t.next); | 839 Statement next = combineStatements(s.next, t.next); |
| 840 popDominatingAssignment(leftHand); |
| 759 | 841 |
| 760 if (next == null) { | 842 if (next == null) { |
| 761 // Statements could not be combined. | 843 // Statements could not be combined. |
| 762 // Restore the environment and uncombine expressions again. | 844 // Restore the environment and uncombine expressions again. |
| 763 environment.removeLast(); | 845 environment.removeLast(); |
| 764 values.uncombine(); | 846 values.uncombine(); |
| 765 return null; | 847 return null; |
| 766 } else if (!environment.isEmpty && environment.last == values.combined) { | 848 } else if (!environment.isEmpty && environment.last == values.combined) { |
| 767 // Statements were combined but the combined expression could not be | 849 // Statements were combined but the combined expression could not be |
| 768 // propagated. Leave it as an expression statement here. | 850 // propagated. Leave it as an expression statement here. |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 823 CombinedExpressions values = combineExpressions(s.value, t.value); | 905 CombinedExpressions values = combineExpressions(s.value, t.value); |
| 824 if (values != null) { | 906 if (values != null) { |
| 825 return new Return(values.combined); | 907 return new Return(values.combined); |
| 826 } | 908 } |
| 827 } | 909 } |
| 828 if (s is ExpressionStatement && t is ExpressionStatement) { | 910 if (s is ExpressionStatement && t is ExpressionStatement) { |
| 829 CombinedExpressions values = | 911 CombinedExpressions values = |
| 830 combineExpressions(s.expression, t.expression); | 912 combineExpressions(s.expression, t.expression); |
| 831 if (values == null) return null; | 913 if (values == null) return null; |
| 832 environment.add(values.combined); | 914 environment.add(values.combined); |
| 915 Variable leftHand = getLeftHand(values.combined); |
| 916 pushDominatingAssignment(leftHand); |
| 833 Statement next = combineStatements(s.next, t.next); | 917 Statement next = combineStatements(s.next, t.next); |
| 918 popDominatingAssignment(leftHand); |
| 834 if (next == null) { | 919 if (next == null) { |
| 835 // The successors could not be combined. | 920 // The successors could not be combined. |
| 836 // Restore the environment and uncombine the values again. | 921 // Restore the environment and uncombine the values again. |
| 837 assert(environment.last == values.combined); | 922 assert(environment.last == values.combined); |
| 838 environment.removeLast(); | 923 environment.removeLast(); |
| 839 values.uncombine(); | 924 values.uncombine(); |
| 840 return null; | 925 return null; |
| 841 } else if (!environment.isEmpty && environment.last == values.combined) { | 926 } else if (!environment.isEmpty && environment.last == values.combined) { |
| 842 // The successors were combined but the combined expressions were not | 927 // The successors were combined but the combined expressions were not |
| 843 // propagated. Leave the combined expression as a statement. | 928 // propagated. Leave the combined expression as a statement. |
| (...skipping 195 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1039 IsVariableUsedVisitor(this.variable); | 1124 IsVariableUsedVisitor(this.variable); |
| 1040 | 1125 |
| 1041 visitVariableUse(VariableUse node) { | 1126 visitVariableUse(VariableUse node) { |
| 1042 if (node.variable == variable) { | 1127 if (node.variable == variable) { |
| 1043 wasFound = true; | 1128 wasFound = true; |
| 1044 } | 1129 } |
| 1045 } | 1130 } |
| 1046 | 1131 |
| 1047 visitInnerFunction(FunctionDefinition node) {} | 1132 visitInnerFunction(FunctionDefinition node) {} |
| 1048 } | 1133 } |
| 1134 |
| 1135 typedef VariableUseCallback(VariableUse use); |
| 1136 |
| 1137 class VariableUseVisitor extends RecursiveVisitor { |
| 1138 VariableUseCallback callback; |
| 1139 |
| 1140 VariableUseVisitor(this.callback); |
| 1141 |
| 1142 visitVariableUse(VariableUse use) => callback(use); |
| 1143 |
| 1144 visitInnerFunction(FunctionDefinition node) {} |
| 1145 |
| 1146 static void visit(Expression node, VariableUseCallback callback) { |
| 1147 new VariableUseVisitor(callback).visitExpression(node); |
| 1148 } |
| 1149 } |
| OLD | NEW |