| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 part of tree_ir.optimization; | |
| 6 | |
| 7 /// Eliminates moving assignments, such as w := v, by assigning directly to w | |
| 8 /// at the definition of v. | |
| 9 /// | |
| 10 /// This compensates for suboptimal register allocation, and merges closure | |
| 11 /// variables with local temporaries that were left behind when translating | |
| 12 /// out of CPS (where closure variables live in a separate space). | |
| 13 class CopyPropagator extends RecursiveVisitor with PassMixin { | |
| 14 String get passName => 'Copy propagation'; | |
| 15 | |
| 16 /// After visitStatement returns, [move] maps a variable v to an | |
| 17 /// assignment A of form w := v, under the following conditions: | |
| 18 /// - there are no reads or writes of w before A | |
| 19 /// - A is the only use of v | |
| 20 Map<Variable, Assign> move = <Variable, Assign>{}; | |
| 21 | |
| 22 /// Like [move], except w is the key instead of v. | |
| 23 Map<Variable, Assign> inverseMove = <Variable, Assign>{}; | |
| 24 | |
| 25 ExecutableElement currentElement; | |
| 26 | |
| 27 /// Number of try blocks enclosing the currently visited node. | |
| 28 int enclosingTrys = 0; | |
| 29 | |
| 30 void rewriteExecutableDefinition(ExecutableDefinition root) { | |
| 31 currentElement = root.element; | |
| 32 root.body = visitStatement(root.body); | |
| 33 } | |
| 34 | |
| 35 rewriteFunctionDefinition(FunctionDefinition node) { | |
| 36 if (node.isAbstract) return; | |
| 37 rewriteExecutableDefinition(node); | |
| 38 | |
| 39 // Try to propagate moving assignments into function parameters. | |
| 40 // For example: | |
| 41 // foo(x) { | |
| 42 // var v1 = x; | |
| 43 // BODY | |
| 44 // } | |
| 45 // ==> | |
| 46 // foo(v1) { | |
| 47 // BODY | |
| 48 // } | |
| 49 | |
| 50 // Variables must not occur more than once in the parameter list, so | |
| 51 // invalidate all moving assignments that would propagate a parameter | |
| 52 // into another parameter. For example: | |
| 53 // foo(x,y) { | |
| 54 // y = x; | |
| 55 // BODY | |
| 56 // } | |
| 57 // Cannot declare function as foo(x,x)! | |
| 58 node.parameters.forEach(invalidateMovingAssignment); | |
| 59 | |
| 60 // Now do the propagation. | |
| 61 for (int i = 0; i < node.parameters.length; i++) { | |
| 62 Variable param = node.parameters[i]; | |
| 63 Variable replacement = copyPropagateVariable(param); | |
| 64 replacement.element = param.element; // Preserve parameter name. | |
| 65 node.parameters[i] = replacement; | |
| 66 } | |
| 67 } | |
| 68 | |
| 69 rewriteConstructorDefinition(ConstructorDefinition node) { | |
| 70 if (node.isAbstract) return; | |
| 71 node.initializers.forEach(visitExpression); | |
| 72 rewriteExecutableDefinition(node); | |
| 73 | |
| 74 | |
| 75 // Try to propagate moving assignments into function parameters. | |
| 76 // For example: | |
| 77 // foo(x) { | |
| 78 // var v1 = x; | |
| 79 // BODY | |
| 80 // } | |
| 81 // ==> | |
| 82 // foo(v1) { | |
| 83 // BODY | |
| 84 // } | |
| 85 | |
| 86 // Variables must not occur more than once in the parameter list, so | |
| 87 // invalidate all moving assignments that would propagate a parameter | |
| 88 // into another parameter. For example: | |
| 89 // foo(x,y) { | |
| 90 // y = x; | |
| 91 // BODY | |
| 92 // } | |
| 93 // Cannot declare function as foo(x,x)! | |
| 94 node.parameters.forEach(invalidateMovingAssignment); | |
| 95 | |
| 96 // Now do the propagation. | |
| 97 for (int i = 0; i < node.parameters.length; i++) { | |
| 98 Variable param = node.parameters[i]; | |
| 99 Variable replacement = copyPropagateVariable(param); | |
| 100 replacement.element = param.element; // Preserve parameter name. | |
| 101 node.parameters[i] = replacement; | |
| 102 } | |
| 103 | |
| 104 } | |
| 105 | |
| 106 | |
| 107 Statement visitBasicBlock(Statement node) { | |
| 108 node = visitStatement(node); | |
| 109 move.clear(); | |
| 110 inverseMove.clear(); | |
| 111 return node; | |
| 112 } | |
| 113 | |
| 114 /// Remove an assignment of form [w] := v from the move maps. | |
| 115 void invalidateMovingAssignment(Variable w) { | |
| 116 Assign movingAssignment = inverseMove.remove(w); | |
| 117 if (movingAssignment != null) { | |
| 118 VariableUse value = movingAssignment.value; | |
| 119 move.remove(value.variable); | |
| 120 } | |
| 121 } | |
| 122 | |
| 123 visitVariableUse(VariableUse node) { | |
| 124 // We found a use of w; we can't propagate assignments across this use. | |
| 125 invalidateMovingAssignment(node.variable); | |
| 126 } | |
| 127 | |
| 128 /** | |
| 129 * Called when a definition of [v] is encountered. | |
| 130 * Attempts to propagate the assignment through a moving assignment. | |
| 131 * Returns the variable to be assigned into, defaulting to [v] itself if | |
| 132 * no optimization could be performed. | |
| 133 */ | |
| 134 Variable copyPropagateVariable(Variable v) { | |
| 135 Assign movingAssign = move[v]; | |
| 136 if (movingAssign != null) { | |
| 137 // We found the pattern: | |
| 138 // v := EXPR | |
| 139 // BLOCK (does not use w) | |
| 140 // w := v (only use of v) | |
| 141 // | |
| 142 // Rewrite to: | |
| 143 // w := EXPR | |
| 144 // BLOCK | |
| 145 // w := w (to be removed later) | |
| 146 Variable w = movingAssign.variable; | |
| 147 | |
| 148 // Make w := w. | |
| 149 // We can't remove the statement from here because we don't have | |
| 150 // parent pointers. So just make it a no-op so it can be removed later. | |
| 151 movingAssign.value = new VariableUse(w); | |
| 152 | |
| 153 // The intermediate variable 'v' should now be orphaned, so don't bother | |
| 154 // updating its read/write counters. | |
| 155 | |
| 156 // Make w := EXPR | |
| 157 ++w.writeCount; | |
| 158 return w; | |
| 159 } | |
| 160 return v; | |
| 161 } | |
| 162 | |
| 163 Statement visitAssign(Assign node) { | |
| 164 node.next = visitStatement(node.next); | |
| 165 node.variable = copyPropagateVariable(node.variable); | |
| 166 | |
| 167 // If a moving assignment w := v exists later, and we assign to w here, | |
| 168 // the moving assignment is no longer a candidate for copy propagation. | |
| 169 invalidateMovingAssignment(node.variable); | |
| 170 | |
| 171 visitExpression(node.value); | |
| 172 | |
| 173 // If this is a moving assignment w := v, with this being the only use of v, | |
| 174 // try to propagate it backwards. | |
| 175 // Do not propagate assignments where w is captured or if where are inside a | |
| 176 // try block, because then we can't isolate the uses of w to a single block. | |
| 177 // We currently do not support propagation if the assignment is a | |
| 178 // declaration. To support this we would need to ensure that the target | |
| 179 // assignment is turned into a declaration as well. | |
| 180 if (node.value is VariableUse && | |
| 181 !node.variable.isCaptured && | |
| 182 enclosingTrys == 0 && | |
| 183 !node.isDeclaration) { | |
| 184 VariableUse value = node.value; | |
| 185 if (value.variable.readCount == 1) { | |
| 186 move[value.variable] = node; | |
| 187 inverseMove[node.variable] = node; | |
| 188 } | |
| 189 } | |
| 190 | |
| 191 return node; | |
| 192 } | |
| 193 | |
| 194 Statement visitLabeledStatement(LabeledStatement node) { | |
| 195 node.next = visitBasicBlock(node.next); | |
| 196 node.body = visitStatement(node.body); | |
| 197 return node; | |
| 198 } | |
| 199 | |
| 200 Statement visitReturn(Return node) { | |
| 201 visitExpression(node.value); | |
| 202 return node; | |
| 203 } | |
| 204 | |
| 205 Statement visitBreak(Break node) { | |
| 206 return node; | |
| 207 } | |
| 208 | |
| 209 Statement visitContinue(Continue node) { | |
| 210 return node; | |
| 211 } | |
| 212 | |
| 213 Statement visitIf(If node) { | |
| 214 visitExpression(node.condition); | |
| 215 node.thenStatement = visitBasicBlock(node.thenStatement); | |
| 216 node.elseStatement = visitBasicBlock(node.elseStatement); | |
| 217 return node; | |
| 218 } | |
| 219 | |
| 220 Statement visitWhileTrue(WhileTrue node) { | |
| 221 node.body = visitBasicBlock(node.body); | |
| 222 return node; | |
| 223 } | |
| 224 | |
| 225 Statement visitWhileCondition(WhileCondition node) { | |
| 226 throw "WhileCondition before LoopRewriter"; | |
| 227 } | |
| 228 | |
| 229 Statement visitTry(Try node) { | |
| 230 enclosingTrys++; | |
| 231 node.tryBody = visitBasicBlock(node.tryBody); | |
| 232 enclosingTrys--; | |
| 233 node.catchBody = visitBasicBlock(node.catchBody); | |
| 234 return node; | |
| 235 } | |
| 236 | |
| 237 Statement visitFunctionDeclaration(FunctionDeclaration node) { | |
| 238 // Unlike var declarations, function declarations are not hoisted, so we | |
| 239 // can't do copy propagation of the variable. | |
| 240 new CopyPropagator().rewrite(node.definition); | |
| 241 node.next = visitStatement(node.next); | |
| 242 return node; | |
| 243 } | |
| 244 | |
| 245 Statement visitExpressionStatement(ExpressionStatement node) { | |
| 246 node.next = visitStatement(node.next); | |
| 247 visitExpression(node.expression); | |
| 248 return node; | |
| 249 } | |
| 250 | |
| 251 Statement visitSetField(SetField node) { | |
| 252 node.next = visitStatement(node.next); | |
| 253 visitExpression(node.value); | |
| 254 visitExpression(node.object); | |
| 255 return node; | |
| 256 } | |
| 257 | |
| 258 void visitFunctionExpression(FunctionExpression node) { | |
| 259 new CopyPropagator().rewrite(node.definition); | |
| 260 } | |
| 261 | |
| 262 void visitFieldInitializer(FieldInitializer node) { | |
| 263 visitStatement(node.body); | |
| 264 } | |
| 265 | |
| 266 } | |
| OLD | NEW |