| OLD | NEW |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 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 | 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 dart2js.cps_ir.gvn; | 5 library dart2js.cps_ir.gvn; |
| 6 | 6 |
| 7 import 'cps_ir_nodes.dart'; | 7 import 'cps_ir_nodes.dart'; |
| 8 import '../universe/side_effects.dart'; | 8 import '../universe/side_effects.dart'; |
| 9 import '../elements/elements.dart'; | 9 import '../elements/elements.dart'; |
| 10 import 'optimizers.dart' show Pass; | 10 import 'optimizers.dart' show Pass; |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 57 /// Effect numbers at the given join point. | 57 /// Effect numbers at the given join point. |
| 58 Map<Continuation, EffectNumbers> effectsAt = <Continuation, EffectNumbers>{}; | 58 Map<Continuation, EffectNumbers> effectsAt = <Continuation, EffectNumbers>{}; |
| 59 | 59 |
| 60 /// The effect numbers at the current position (during traversal). | 60 /// The effect numbers at the current position (during traversal). |
| 61 EffectNumbers effectNumbers = new EffectNumbers(); | 61 EffectNumbers effectNumbers = new EffectNumbers(); |
| 62 | 62 |
| 63 /// The loop currently enclosing the binding of a given primitive. | 63 /// The loop currently enclosing the binding of a given primitive. |
| 64 final Map<Primitive, Continuation> loopHeaderFor = | 64 final Map<Primitive, Continuation> loopHeaderFor = |
| 65 <Primitive, Continuation>{}; | 65 <Primitive, Continuation>{}; |
| 66 | 66 |
| 67 /// The loop to which a given trivial primitive can be hoisted. | |
| 68 final Map<Primitive, Continuation> potentialLoopHeaderFor = | |
| 69 <Primitive, Continuation>{}; | |
| 70 | |
| 71 /// The GVNs for primitives that have been hoisted outside the given loop. | 67 /// The GVNs for primitives that have been hoisted outside the given loop. |
| 72 /// | 68 /// |
| 73 /// These should be removed from the environment when exiting the loop. | 69 /// These should be removed from the environment when exiting the loop. |
| 74 final Map<Continuation, List<int>> loopHoistedBindings = | 70 final Map<Continuation, List<int>> loopHoistedBindings = |
| 75 <Continuation, List<int>>{}; | 71 <Continuation, List<int>>{}; |
| 76 | 72 |
| 77 /// Maps GVNs to a currently-in-scope binding for that value. | 73 /// Maps GVNs to a currently-in-scope binding for that value. |
| 78 final Map<int, Primitive> environment = <int, Primitive>{}; | 74 final Map<int, Primitive> environment = <int, Primitive>{}; |
| 79 | 75 |
| 80 /// Maps GVN'able primitives to their global value number. | 76 /// Maps GVN'able primitives to their global value number. |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 148 } | 144 } |
| 149 | 145 |
| 150 // Compute the GVN for this primitive. | 146 // Compute the GVN for this primitive. |
| 151 int gvn = gvnTable.insert(vector); | 147 int gvn = gvnTable.insert(vector); |
| 152 gvnFor[prim] = gvn; | 148 gvnFor[prim] = gvn; |
| 153 | 149 |
| 154 // Try to reuse a previously computed value with the same GVN. | 150 // Try to reuse a previously computed value with the same GVN. |
| 155 Primitive existing = environment[gvn]; | 151 Primitive existing = environment[gvn]; |
| 156 if (existing != null && | 152 if (existing != null && |
| 157 canReplaceWithExistingValue(prim) && | 153 canReplaceWithExistingValue(prim) && |
| 158 !isTrivialPrimitive(prim)) { | 154 !isFastConstant(prim)) { |
| 159 if (prim is Interceptor) { | 155 if (prim is Interceptor) { |
| 160 Interceptor interceptor = existing; | 156 Interceptor interceptor = existing; |
| 161 interceptor.interceptedClasses.addAll(prim.interceptedClasses); | 157 interceptor.interceptedClasses.addAll(prim.interceptedClasses); |
| 162 } | 158 } |
| 163 prim..replaceUsesWith(existing)..destroy(); | 159 prim..replaceUsesWith(existing)..destroy(); |
| 164 node.remove(); | 160 node.remove(); |
| 165 return next; | 161 return next; |
| 166 } | 162 } |
| 167 | 163 |
| 168 if (tryToHoistOutOfLoop(prim, gvn)) { | 164 if (tryToHoistOutOfLoop(prim, gvn)) { |
| (...skipping 25 matching lines...) Expand all Loading... |
| 194 // Find the depth of the outermost scope where we can bind the primitive | 190 // Find the depth of the outermost scope where we can bind the primitive |
| 195 // without bringing a reference out of scope. 0 is the depth of the | 191 // without bringing a reference out of scope. 0 is the depth of the |
| 196 // top-level scope. | 192 // top-level scope. |
| 197 int hoistDepth = 0; | 193 int hoistDepth = 0; |
| 198 List<Primitive> inputsHoistedOnDemand = <Primitive>[]; | 194 List<Primitive> inputsHoistedOnDemand = <Primitive>[]; |
| 199 InputVisitor.forEach(prim, (Reference ref) { | 195 InputVisitor.forEach(prim, (Reference ref) { |
| 200 Primitive input = ref.definition; | 196 Primitive input = ref.definition; |
| 201 if (canIgnoreRefinementGuards(prim)) { | 197 if (canIgnoreRefinementGuards(prim)) { |
| 202 input = input.effectiveDefinition; | 198 input = input.effectiveDefinition; |
| 203 } | 199 } |
| 204 Continuation loopHeader; | 200 if (isFastConstant(input)) { |
| 205 if (potentialLoopHeaderFor.containsKey(input)) { | 201 // Fast constants can be hoisted all the way out, but should only be |
| 206 // This is a reference to a value that can be hoisted further out than | 202 // hoisted if needed to hoist something else. |
| 207 // it currently is. If we decide to hoist [prim], we must also hoist | |
| 208 // such dependent values. | |
| 209 loopHeader = potentialLoopHeaderFor[input]; | |
| 210 inputsHoistedOnDemand.add(input); | 203 inputsHoistedOnDemand.add(input); |
| 211 } else { | 204 } else { |
| 212 loopHeader = loopHeaderFor[input]; | 205 Continuation loopHeader = loopHeaderFor[input]; |
| 213 } | 206 Continuation referencedLoop = |
| 214 Continuation referencedLoop = | 207 loopHierarchy.lowestCommonAncestor(loopHeader, currentLoopHeader); |
| 215 loopHierarchy.lowestCommonAncestor(loopHeader, currentLoopHeader); | 208 int depth = loopHierarchy.getDepth(referencedLoop); |
| 216 int depth = loopHierarchy.getDepth(referencedLoop); | 209 if (depth > hoistDepth) { |
| 217 if (depth > hoistDepth) { | 210 hoistDepth = depth; |
| 218 hoistDepth = depth; | 211 } |
| 219 } | 212 } |
| 220 }); | 213 }); |
| 221 | 214 |
| 222 // Bail out if it can not be hoisted further out than it is now. | 215 // Bail out if it can not be hoisted further out than it is now. |
| 223 if (hoistDepth == loopHierarchy.getDepth(currentLoopHeader)) return false; | 216 if (hoistDepth == loopHierarchy.getDepth(currentLoopHeader)) return false; |
| 224 | 217 |
| 225 // Walk up the loop hierarchy and check at every step that any heap | 218 // Walk up the loop hierarchy and check at every step that any heap |
| 226 // dependencies can safely be hoisted out of the loop. | 219 // dependencies can safely be hoisted out of the loop. |
| 227 Continuation enclosingLoop = currentLoopHeader; | 220 Continuation enclosingLoop = currentLoopHeader; |
| 228 Continuation hoistTarget = null; | 221 Continuation hoistTarget = null; |
| 229 while (loopHierarchy.getDepth(enclosingLoop) > hoistDepth && | 222 while (loopHierarchy.getDepth(enclosingLoop) > hoistDepth && |
| 230 canHoistHeapDependencyOutOfLoop(prim, enclosingLoop)) { | 223 canHoistHeapDependencyOutOfLoop(prim, enclosingLoop)) { |
| 231 hoistTarget = enclosingLoop; | 224 hoistTarget = enclosingLoop; |
| 232 enclosingLoop = loopHierarchy.getEnclosingLoop(enclosingLoop); | 225 enclosingLoop = loopHierarchy.getEnclosingLoop(enclosingLoop); |
| 233 } | 226 } |
| 234 | 227 |
| 235 // Bail out if heap dependencies prohibit any hoisting at all. | 228 // Bail out if heap dependencies prohibit any hoisting at all. |
| 236 if (hoistTarget == null) return false; | 229 if (hoistTarget == null) return false; |
| 237 | 230 |
| 238 if (isTrivialPrimitive(prim)) { | 231 if (isFastConstant(prim)) { |
| 239 // The overhead from introducting a temporary might be greater than | 232 // The overhead from introducting a temporary might be greater than |
| 240 // the overhead of evaluating this primitive at every iteration. | 233 // the overhead of evaluating this primitive at every iteration. |
| 241 // Only hoist if this enables hoisting of a non-trivial primitive. | 234 // Only hoist if this enables hoisting of a non-trivial primitive. |
| 242 potentialLoopHeaderFor[prim] = enclosingLoop; | |
| 243 return true; | 235 return true; |
| 244 } | 236 } |
| 245 | 237 |
| 246 LetCont loopBinding = hoistTarget.parent; | 238 LetCont loopBinding = hoistTarget.parent; |
| 247 | 239 |
| 248 // The primitive may depend on values that have not yet been | 240 // The primitive may depend on values that have not yet been |
| 249 // hoisted as far as they can. Hoist those now. | 241 // hoisted as far as they can. Hoist those now. |
| 250 for (Primitive input in inputsHoistedOnDemand) { | 242 for (Primitive input in inputsHoistedOnDemand) { |
| 251 hoistTrivialPrimitive(input, loopBinding, enclosingLoop); | 243 hoistTrivialPrimitive(input, loopBinding, enclosingLoop); |
| 252 } | 244 } |
| (...skipping 26 matching lines...) Expand all Loading... |
| 279 .putIfAbsent(hoistTarget, () => <int>[]) | 271 .putIfAbsent(hoistTarget, () => <int>[]) |
| 280 .add(gvn); | 272 .add(gvn); |
| 281 return true; | 273 return true; |
| 282 } | 274 } |
| 283 | 275 |
| 284 /// If the given primitive is a trivial primitive that should be hoisted | 276 /// If the given primitive is a trivial primitive that should be hoisted |
| 285 /// on-demand, hoist it and its dependent values above [loopBinding]. | 277 /// on-demand, hoist it and its dependent values above [loopBinding]. |
| 286 void hoistTrivialPrimitive(Primitive prim, | 278 void hoistTrivialPrimitive(Primitive prim, |
| 287 LetCont loopBinding, | 279 LetCont loopBinding, |
| 288 Continuation enclosingLoop) { | 280 Continuation enclosingLoop) { |
| 289 if (!potentialLoopHeaderFor.containsKey(prim)) return; | 281 assert(isFastConstant(prim)); |
| 290 assert(isTrivialPrimitive(prim)); | |
| 291 | 282 |
| 292 // The primitive might already be bound in an outer scope. Do not relocate | 283 // The primitive might already be bound in an outer scope. Do not relocate |
| 293 // the primitive unless we are lifting it. For example; | 284 // the primitive unless we are lifting it. For example; |
| 294 // t1 = a + b | 285 // t1 = a + b |
| 295 // t2 = t1 + c | 286 // t2 = t1 + c |
| 296 // t3 = t1 * t2 | 287 // t3 = t1 * t2 |
| 297 // If it was decided that `t3` should be hoisted, `t1` will be seen twice by | 288 // If it was decided that `t3` should be hoisted, `t1` will be seen twice by |
| 298 // this method: by the direct reference and by reference through `t2`. | 289 // this method: by the direct reference and by reference through `t2`. |
| 299 // The second time it is seen, it will already have been moved. | 290 // The second time it is seen, it will already have been moved. |
| 300 Continuation currentLoop = loopHeaderFor[prim]; | 291 Continuation currentLoop = loopHeaderFor[prim]; |
| 301 int currentDepth = loopHierarchy.getDepth(currentLoop); | 292 int currentDepth = loopHierarchy.getDepth(currentLoop); |
| 302 int targetDepth = loopHierarchy.getDepth(enclosingLoop); | 293 int targetDepth = loopHierarchy.getDepth(enclosingLoop); |
| 303 if (currentDepth <= targetDepth) return; | 294 if (currentDepth <= targetDepth) return; |
| 304 | 295 |
| 305 // Hoist the trivial primitives being depended on so they remain in scope. | 296 // Hoist the trivial primitives being depended on so they remain in scope. |
| 306 InputVisitor.forEach(prim, (Reference ref) { | 297 InputVisitor.forEach(prim, (Reference ref) { |
| 307 hoistTrivialPrimitive(ref.definition, loopBinding, enclosingLoop); | 298 hoistTrivialPrimitive(ref.definition, loopBinding, enclosingLoop); |
| 308 }); | 299 }); |
| 309 | 300 |
| 310 // Move the primitive. | 301 // Move the primitive. |
| 311 LetPrim binding = prim.parent; | 302 LetPrim binding = prim.parent; |
| 312 binding.remove(); | 303 binding.remove(); |
| 313 binding.insertAbove(loopBinding); | 304 binding.insertAbove(loopBinding); |
| 314 loopHeaderFor[prim] = enclosingLoop; | 305 loopHeaderFor[prim] = enclosingLoop; |
| 315 | |
| 316 if (potentialLoopHeaderFor[prim] == enclosingLoop) { | |
| 317 potentialLoopHeaderFor.remove(prim); | |
| 318 } | |
| 319 } | 306 } |
| 320 | 307 |
| 321 bool canIgnoreRefinementGuards(Primitive primitive) { | 308 bool canIgnoreRefinementGuards(Primitive primitive) { |
| 322 return primitive is Interceptor; | 309 return primitive is Interceptor; |
| 323 } | 310 } |
| 324 | 311 |
| 325 /// Returns true if the given primitive is so cheap at runtime that it is | 312 /// Returns true if [prim] is a constant that has no significant runtime cost. |
| 326 /// better to (redundantly) recompute it rather than introduce a temporary. | 313 bool isFastConstant(Primitive prim) { |
| 327 bool isTrivialPrimitive(Primitive primitive) { | 314 return prim is Constant && (prim.value.isPrimitive || prim.value.isDummy); |
| 328 return primitive is ApplyBuiltinOperator || | |
| 329 primitive is Constant && isTrivialConstant(primitive.value); | |
| 330 } | |
| 331 | |
| 332 /// Returns true if the given constant has almost no runtime cost. | |
| 333 bool isTrivialConstant(ConstantValue value) { | |
| 334 return value.isPrimitive || value.isDummy; | |
| 335 } | 315 } |
| 336 | 316 |
| 337 /// True if [element] is a final or constant field or a function. | 317 /// True if [element] is a final or constant field or a function. |
| 338 bool isImmutable(Element element) { | 318 bool isImmutable(Element element) { |
| 339 if (element.isField && backend.isNative(element)) return false; | 319 if (element.isField && backend.isNative(element)) return false; |
| 340 return element.isField && world.fieldNeverChanges(element) || | 320 return element.isField && world.fieldNeverChanges(element) || |
| 341 element.isFunction; | 321 element.isFunction; |
| 342 } | 322 } |
| 343 | 323 |
| 344 bool isImmutableLength(GetLength length) { | 324 bool isImmutableLength(GetLength length) { |
| (...skipping 397 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 742 | 722 |
| 743 @override | 723 @override |
| 744 processReference(Reference ref) { | 724 processReference(Reference ref) { |
| 745 callback(ref); | 725 callback(ref); |
| 746 } | 726 } |
| 747 | 727 |
| 748 static void forEach(Primitive node, ReferenceCallback callback) { | 728 static void forEach(Primitive node, ReferenceCallback callback) { |
| 749 new InputVisitor(callback).visit(node); | 729 new InputVisitor(callback).visit(node); |
| 750 } | 730 } |
| 751 } | 731 } |
| OLD | NEW |