Chromium Code Reviews| 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 part of tree_ir.optimization; | 5 part of tree_ir.optimization; |
| 6 | 6 |
| 7 /** | 7 /** |
| 8 * Performs the following transformations on the tree: | 8 * Performs the following transformations on the tree: |
| 9 * - Assignment inlining | 9 * - Assignment inlining |
| 10 * - Assignment expression propagation | 10 * - Assignment expression propagation |
| (...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 130 List<Expression> environment = <Expression>[]; | 130 List<Expression> environment = <Expression>[]; |
| 131 | 131 |
| 132 /// Binding environment for variables that are assigned to effectively | 132 /// Binding environment for variables that are assigned to effectively |
| 133 /// constant expressions (see [isEffectivelyConstant]). | 133 /// constant expressions (see [isEffectivelyConstant]). |
| 134 final Map<Variable, Expression> constantEnvironment; | 134 final Map<Variable, Expression> constantEnvironment; |
| 135 | 135 |
| 136 /// Substitution map for labels. Any break to a label L should be substituted | 136 /// Substitution map for labels. Any break to a label L should be substituted |
| 137 /// for a break to L' if L maps to L'. | 137 /// for a break to L' if L maps to L'. |
| 138 Map<Label, Jump> labelRedirects = <Label, Jump>{}; | 138 Map<Label, Jump> labelRedirects = <Label, Jump>{}; |
| 139 | 139 |
| 140 /// Number of uses seen so far. Used to detect the first use of a variable | 140 /// Number of uses of the given variable that are still unseen. |
| 141 /// (since we do backwards traversal, the first use is the last one seen). | 141 /// Used to detect the first use of a variable (since we do backwards |
| 142 Map<Variable, int> seenUses = <Variable, int>{}; | 142 /// traversal, the first use is the last one seen). |
| 143 Map<Variable, int> unseenUses = <Variable, int>{}; | |
| 143 | 144 |
| 144 /// Rewriter for methods. | 145 /// Rewriter for methods. |
| 145 StatementRewriter({this.isDartMode}) | 146 StatementRewriter({this.isDartMode}) |
| 146 : constantEnvironment = <Variable, Expression>{} { | 147 : constantEnvironment = <Variable, Expression>{} { |
| 147 assert(isDartMode != null); | 148 assert(isDartMode != null); |
| 148 } | 149 } |
| 149 | 150 |
| 150 /// Rewriter for nested functions. | 151 /// Rewriter for nested functions. |
| 151 StatementRewriter.nested(StatementRewriter parent) | 152 StatementRewriter.nested(StatementRewriter parent) |
| 152 : constantEnvironment = parent.constantEnvironment, | 153 : constantEnvironment = parent.constantEnvironment, |
| 153 seenUses = parent.seenUses, | 154 unseenUses = parent.unseenUses, |
| 154 isDartMode = parent.isDartMode; | 155 isDartMode = parent.isDartMode; |
| 155 | 156 |
| 156 /// A set of labels that can be safely inlined at their use. | 157 /// A set of labels that can be safely inlined at their use. |
| 157 /// | 158 /// |
| 158 /// The successor statements for labeled statements that have only one break | 159 /// The successor statements for labeled statements that have only one break |
| 159 /// from them are normally rewritten inline at the site of the break. This | 160 /// from them are normally rewritten inline at the site of the break. This |
| 160 /// is not safe if the code would be moved inside the scope of an exception | 161 /// is not safe if the code would be moved inside the scope of an exception |
| 161 /// handler (i.e., if the code would be moved into a try from outside it). | 162 /// handler (i.e., if the code would be moved into a try from outside it). |
| 162 Set<Label> safeForInlining = new Set<Label>(); | 163 Set<Label> safeForInlining = new Set<Label>(); |
| 163 | 164 |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 196 /// If the given expression always returns the value of one of its | 197 /// If the given expression always returns the value of one of its |
| 197 /// subexpressions, and that subexpression is a variable use, returns that | 198 /// subexpressions, and that subexpression is a variable use, returns that |
| 198 /// variable. Otherwise `null`. | 199 /// variable. Otherwise `null`. |
| 199 Variable getRightHand(Expression e) { | 200 Variable getRightHand(Expression e) { |
| 200 Expression value = getValueSubexpression(e); | 201 Expression value = getValueSubexpression(e); |
| 201 return value is VariableUse ? value.variable : null; | 202 return value is VariableUse ? value.variable : null; |
| 202 } | 203 } |
| 203 | 204 |
| 204 @override | 205 @override |
| 205 Expression visitVariableUse(VariableUse node) { | 206 Expression visitVariableUse(VariableUse node) { |
| 206 // Count of number of uses seen so far. | 207 // Count of number of unseen uses remaining. |
| 207 seenUses[node.variable] = 1 + seenUses.putIfAbsent(node.variable, () => 0); | 208 unseenUses.putIfAbsent(node.variable, () => node.variable.readCount); |
| 209 --unseenUses[node.variable]; | |
| 208 | 210 |
| 209 // We traverse the tree right-to-left, so when we have seen all uses, | 211 // We traverse the tree right-to-left, so when we have seen all uses, |
| 210 // it means we are looking at the first use. | 212 // it means we are looking at the first use. |
| 211 assert(seenUses[node.variable] <= node.variable.readCount); | 213 assert(unseenUses[node.variable] < node.variable.readCount); |
| 212 bool isFirstUse = seenUses[node.variable] == node.variable.readCount; | 214 assert(unseenUses[node.variable] >= 0); |
| 215 bool isFirstUse = unseenUses[node.variable] == 0; | |
| 213 | 216 |
| 214 // Propagate constant to use site. | 217 // Propagate constant to use site. |
| 215 Expression constant = constantEnvironment[node.variable]; | 218 Expression constant = constantEnvironment[node.variable]; |
| 216 if (constant != null) { | 219 if (constant != null) { |
| 217 --node.variable.readCount; | 220 --node.variable.readCount; |
| 218 --seenUses[node.variable]; // Do not count the use we just destroyed. | |
|
asgerf
2015/04/16 12:03:55
I knew this was a bad idea and somehow I did it an
| |
| 219 return visitExpression(constant); | 221 return visitExpression(constant); |
| 220 } | 222 } |
| 221 | 223 |
| 222 // Try to propagate another expression into this variable use. | 224 // Try to propagate another expression into this variable use. |
| 223 if (!environment.isEmpty) { | 225 if (!environment.isEmpty) { |
| 224 Expression binding = environment.last; | 226 Expression binding = environment.last; |
| 225 | 227 |
| 226 // Is this variable assigned by the most recently evaluated impure | 228 // Is this variable assigned by the most recently evaluated impure |
| 227 // expression? | 229 // expression? |
| 228 // | 230 // |
| 229 // If so, propagate the assignment, e.g: | 231 // If so, propagate the assignment, e.g: |
| 230 // | 232 // |
| 231 // { x = foo(); bar(x, x) } ==> bar(x = foo(), x) | 233 // { x = foo(); bar(x, x) } ==> bar(x = foo(), x) |
| 232 // | 234 // |
| 233 // We must ensure that no other uses separate this use from the | 235 // We must ensure that no other uses separate this use from the |
| 234 // assignment. We therefore only propagate assignments into the first use. | 236 // assignment. We therefore only propagate assignments into the first use. |
| 235 // | 237 // |
| 236 // Note that if this is only use, `visitAssign` will then remove the | 238 // Note that if this is only use, `visitAssign` will then remove the |
| 237 // redundant assignment. | 239 // redundant assignment. |
| 238 if (getLeftHand(binding) == node.variable && isFirstUse) { | 240 if (getLeftHand(binding) == node.variable && isFirstUse) { |
| 239 environment.removeLast(); | 241 environment.removeLast(); |
| 240 --node.variable.readCount; | 242 --node.variable.readCount; |
| 241 --seenUses[node.variable]; // Do not count the use we just destroyed. | |
| 242 return visitExpression(binding); | 243 return visitExpression(binding); |
| 243 } | 244 } |
| 244 | 245 |
| 245 // Is the most recently evaluated impure expression known to have the | 246 // Is the most recently evaluated impure expression known to have the |
| 246 // value of this variable? | 247 // value of this variable? |
| 247 // | 248 // |
| 248 // If so, we can replace this use with the impure expression, e.g: | 249 // If so, we can replace this use with the impure expression, e.g: |
| 249 // | 250 // |
| 250 // { E.foo = x; bar(x) } ==> bar(E.foo = x) | 251 // { E.foo = x; bar(x) } ==> bar(E.foo = x) |
| 251 // | 252 // |
| 252 if (getRightHand(binding) == node.variable) { | 253 if (getRightHand(binding) == node.variable) { |
| 253 environment.removeLast(); | 254 environment.removeLast(); |
| 254 --node.variable.readCount; | 255 --node.variable.readCount; |
| 255 --seenUses[node.variable]; | |
| 256 return visitExpression(binding); | 256 return visitExpression(binding); |
| 257 } | 257 } |
| 258 } | 258 } |
| 259 | 259 |
| 260 // If the definition could not be propagated, leave the variable use. | 260 // If the definition could not be propagated, leave the variable use. |
| 261 return node; | 261 return node; |
| 262 } | 262 } |
| 263 | 263 |
| 264 /// Returns true if [exp] has no side effects and has a constant value within | 264 /// Returns true if [exp] has no side effects and has a constant value within |
| 265 /// any given activation of the enclosing method. | 265 /// any given activation of the enclosing method. |
| (...skipping 627 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 893 } | 893 } |
| 894 | 894 |
| 895 /// Combines two variable uses into one. | 895 /// Combines two variable uses into one. |
| 896 class CombinedUses implements CombinedExpressions { | 896 class CombinedUses implements CombinedExpressions { |
| 897 VariableUse use1, use2; | 897 VariableUse use1, use2; |
| 898 Expression combined; | 898 Expression combined; |
| 899 | 899 |
| 900 CombinedUses(this.use1, this.use2) { | 900 CombinedUses(this.use1, this.use2) { |
| 901 assert(use1.variable == use2.variable); | 901 assert(use1.variable == use2.variable); |
| 902 use1.variable.readCount -= 2; // Destroy both the original uses. | 902 use1.variable.readCount -= 2; // Destroy both the original uses. |
| 903 combined = new VariableUse(use1.variable); | 903 combined = new VariableUse(use1.variable); |
|
asgerf
2015/04/16 12:03:55
FYI this is where the bookkeeping was missing.
Up
| |
| 904 } | 904 } |
| 905 | 905 |
| 906 void uncombine() { | 906 void uncombine() { |
| 907 ++use1.variable.readCount; // Restore original reference count. | 907 ++use1.variable.readCount; // Restore original reference count. |
| 908 } | 908 } |
| 909 } | 909 } |
| 910 | 910 |
| 911 /// Result of combining two expressions that do not affect reference counting. | 911 /// Result of combining two expressions that do not affect reference counting. |
| 912 class GenericCombinedExpressions implements CombinedExpressions { | 912 class GenericCombinedExpressions implements CombinedExpressions { |
| 913 Expression combined; | 913 Expression combined; |
| 914 | 914 |
| 915 GenericCombinedExpressions(this.combined); | 915 GenericCombinedExpressions(this.combined); |
| 916 | 916 |
| 917 void uncombine() {} | 917 void uncombine() {} |
| 918 } | 918 } |
| OLD | NEW |