| 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 '../elements/elements.dart'; | 8 import '../elements/elements.dart'; |
| 9 import 'optimizers.dart' show Pass; | 9 import 'optimizers.dart' show Pass; |
| 10 import 'loop_hierarchy.dart'; | 10 import 'loop_hierarchy.dart'; |
| (...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 88 // ------------------ GLOBAL VALUE NUMBERING --------------------- | 88 // ------------------ GLOBAL VALUE NUMBERING --------------------- |
| 89 | 89 |
| 90 /// True if [prim] can be eliminated if its value is already in scope. | 90 /// True if [prim] can be eliminated if its value is already in scope. |
| 91 bool canReplaceWithExistingValue(Primitive prim) { | 91 bool canReplaceWithExistingValue(Primitive prim) { |
| 92 // Primitives that have no side effects other than potentially throwing are | 92 // Primitives that have no side effects other than potentially throwing are |
| 93 // known not the throw if the value is already in scope. Handling those | 93 // known not the throw if the value is already in scope. Handling those |
| 94 // specially is equivalent to updating refinements during GVN. | 94 // specially is equivalent to updating refinements during GVN. |
| 95 // GetLazyStatic cannot have side effects because the field has already | 95 // GetLazyStatic cannot have side effects because the field has already |
| 96 // been initialized. | 96 // been initialized. |
| 97 return prim.isSafeForElimination || | 97 return prim.isSafeForElimination || |
| 98 prim is GetField || | 98 prim is GetField || |
| 99 prim is GetLength || | 99 prim is GetLength || |
| 100 prim is GetIndex || | 100 prim is GetIndex || |
| 101 prim is GetLazyStatic; | 101 prim is GetLazyStatic; |
| 102 } | 102 } |
| 103 | 103 |
| 104 @override | 104 @override |
| 105 Expression traverseLetPrim(LetPrim node) { | 105 Expression traverseLetPrim(LetPrim node) { |
| 106 Expression next = node.body; | 106 Expression next = node.body; |
| 107 Primitive prim = node.primitive; | 107 Primitive prim = node.primitive; |
| 108 | 108 |
| 109 loopHeaderFor[prim] = currentLoopHeader; | 109 loopHeaderFor[prim] = currentLoopHeader; |
| 110 | 110 |
| 111 if (prim is Refinement) { | 111 if (prim is Refinement) { |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 143 | 143 |
| 144 // Try to reuse a previously computed value with the same GVN. | 144 // Try to reuse a previously computed value with the same GVN. |
| 145 Primitive existing = environment[gvn]; | 145 Primitive existing = environment[gvn]; |
| 146 if (existing != null && | 146 if (existing != null && |
| 147 canReplaceWithExistingValue(prim) && | 147 canReplaceWithExistingValue(prim) && |
| 148 !isFastConstant(prim)) { | 148 !isFastConstant(prim)) { |
| 149 if (prim is Interceptor) { | 149 if (prim is Interceptor) { |
| 150 Interceptor interceptor = existing; | 150 Interceptor interceptor = existing; |
| 151 interceptor.interceptedClasses.addAll(prim.interceptedClasses); | 151 interceptor.interceptedClasses.addAll(prim.interceptedClasses); |
| 152 } | 152 } |
| 153 prim..replaceUsesWith(existing)..destroy(); | 153 prim |
| 154 ..replaceUsesWith(existing) |
| 155 ..destroy(); |
| 154 node.remove(); | 156 node.remove(); |
| 155 return next; | 157 return next; |
| 156 } | 158 } |
| 157 | 159 |
| 158 if (tryToHoistOutOfLoop(prim, gvn)) { | 160 if (tryToHoistOutOfLoop(prim, gvn)) { |
| 159 return next; | 161 return next; |
| 160 } | 162 } |
| 161 | 163 |
| 162 // The primitive could not be hoisted. Put the primitive in the | 164 // The primitive could not be hoisted. Put the primitive in the |
| 163 // environment while processing the body of the LetPrim. | 165 // environment while processing the body of the LetPrim. |
| (...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 240 }); | 242 }); |
| 241 | 243 |
| 242 // Bail out if it can not be hoisted further out than it is now. | 244 // Bail out if it can not be hoisted further out than it is now. |
| 243 if (hoistDepth == loopHierarchy.getDepth(currentLoopHeader)) return false; | 245 if (hoistDepth == loopHierarchy.getDepth(currentLoopHeader)) return false; |
| 244 | 246 |
| 245 // Walk up the loop hierarchy and check at every step that any heap | 247 // Walk up the loop hierarchy and check at every step that any heap |
| 246 // dependencies can safely be hoisted out of the loop. | 248 // dependencies can safely be hoisted out of the loop. |
| 247 Continuation enclosingLoop = currentLoopHeader; | 249 Continuation enclosingLoop = currentLoopHeader; |
| 248 Continuation hoistTarget = null; | 250 Continuation hoistTarget = null; |
| 249 while (loopHierarchy.getDepth(enclosingLoop) > hoistDepth && | 251 while (loopHierarchy.getDepth(enclosingLoop) > hoistDepth && |
| 250 canHoistHeapDependencyOutOfLoop(prim, enclosingLoop)) { | 252 canHoistHeapDependencyOutOfLoop(prim, enclosingLoop)) { |
| 251 hoistTarget = enclosingLoop; | 253 hoistTarget = enclosingLoop; |
| 252 enclosingLoop = loopHierarchy.getEnclosingLoop(enclosingLoop); | 254 enclosingLoop = loopHierarchy.getEnclosingLoop(enclosingLoop); |
| 253 } | 255 } |
| 254 | 256 |
| 255 // Bail out if heap dependencies prohibit any hoisting at all. | 257 // Bail out if heap dependencies prohibit any hoisting at all. |
| 256 if (hoistTarget == null) return false; | 258 if (hoistTarget == null) return false; |
| 257 | 259 |
| 258 if (isFastConstant(prim)) { | 260 if (isFastConstant(prim)) { |
| 259 // The overhead from introducting a temporary might be greater than | 261 // The overhead from introducting a temporary might be greater than |
| 260 // the overhead of evaluating this primitive at every iteration. | 262 // the overhead of evaluating this primitive at every iteration. |
| (...skipping 26 matching lines...) Expand all Loading... |
| 287 if (loopHierarchy.getDepth(loop) <= target) break; | 289 if (loopHierarchy.getDepth(loop) <= target) break; |
| 288 Refinement refinement = input; | 290 Refinement refinement = input; |
| 289 input = refinement.value.definition; | 291 input = refinement.value.definition; |
| 290 } | 292 } |
| 291 ref.changeTo(input); | 293 ref.changeTo(input); |
| 292 }); | 294 }); |
| 293 } | 295 } |
| 294 | 296 |
| 295 // Put the primitive in the environment while processing the loop. | 297 // Put the primitive in the environment while processing the loop. |
| 296 environment[gvn] = prim; | 298 environment[gvn] = prim; |
| 297 loopHoistedBindings | 299 loopHoistedBindings.putIfAbsent(hoistTarget, () => <int>[]).add(gvn); |
| 298 .putIfAbsent(hoistTarget, () => <int>[]) | |
| 299 .add(gvn); | |
| 300 return true; | 300 return true; |
| 301 } | 301 } |
| 302 | 302 |
| 303 /// If the given primitive is a trivial primitive that should be hoisted | 303 /// If the given primitive is a trivial primitive that should be hoisted |
| 304 /// on-demand, hoist it and its dependent values above [loopBinding]. | 304 /// on-demand, hoist it and its dependent values above [loopBinding]. |
| 305 void hoistTrivialPrimitive(Primitive prim, | 305 void hoistTrivialPrimitive( |
| 306 LetCont loopBinding, | 306 Primitive prim, LetCont loopBinding, Continuation enclosingLoop) { |
| 307 Continuation enclosingLoop) { | |
| 308 assert(isFastConstant(prim)); | 307 assert(isFastConstant(prim)); |
| 309 | 308 |
| 310 // The primitive might already be bound in an outer scope. Do not relocate | 309 // The primitive might already be bound in an outer scope. Do not relocate |
| 311 // the primitive unless we are lifting it. For example; | 310 // the primitive unless we are lifting it. For example; |
| 312 // t1 = a + b | 311 // t1 = a + b |
| 313 // t2 = t1 + c | 312 // t2 = t1 + c |
| 314 // t3 = t1 * t2 | 313 // t3 = t1 * t2 |
| 315 // If it was decided that `t3` should be hoisted, `t1` will be seen twice by | 314 // If it was decided that `t3` should be hoisted, `t1` will be seen twice by |
| 316 // this method: by the direct reference and by reference through `t2`. | 315 // this method: by the direct reference and by reference through `t2`. |
| 317 // The second time it is seen, it will already have been moved. | 316 // The second time it is seen, it will already have been moved. |
| (...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 432 return _table.putIfAbsent(new GvnEntry(vector), _makeNewGvn); | 431 return _table.putIfAbsent(new GvnEntry(vector), _makeNewGvn); |
| 433 } | 432 } |
| 434 } | 433 } |
| 435 | 434 |
| 436 /// Wrapper around a [List] that compares for equality based on contents | 435 /// Wrapper around a [List] that compares for equality based on contents |
| 437 /// instead of object identity. | 436 /// instead of object identity. |
| 438 class GvnEntry { | 437 class GvnEntry { |
| 439 final List vector; | 438 final List vector; |
| 440 final int hashCode; | 439 final int hashCode; |
| 441 | 440 |
| 442 GvnEntry(List vector) : vector = vector, hashCode = computeHashCode(vector); | 441 GvnEntry(List vector) |
| 442 : vector = vector, |
| 443 hashCode = computeHashCode(vector); |
| 443 | 444 |
| 444 bool operator==(other) { | 445 bool operator ==(other) { |
| 445 if (other is! GvnEntry) return false; | 446 if (other is! GvnEntry) return false; |
| 446 GvnEntry entry = other; | 447 GvnEntry entry = other; |
| 447 List otherVector = entry.vector; | 448 List otherVector = entry.vector; |
| 448 if (vector.length != otherVector.length) return false; | 449 if (vector.length != otherVector.length) return false; |
| 449 for (int i = 0; i < vector.length; ++i) { | 450 for (int i = 0; i < vector.length; ++i) { |
| 450 if (vector[i] != otherVector[i]) return false; | 451 if (vector[i] != otherVector[i]) return false; |
| 451 } | 452 } |
| 452 return true; | 453 return true; |
| 453 } | 454 } |
| 454 | 455 |
| 455 /// Combines the hash codes of [vector] using Jenkin's hash function, with | 456 /// Combines the hash codes of [vector] using Jenkin's hash function, with |
| 456 /// intermediate results truncated to SMI range. | 457 /// intermediate results truncated to SMI range. |
| 457 static int computeHashCode(List vector) { | 458 static int computeHashCode(List vector) { |
| 458 int hash = 0; | 459 int hash = 0; |
| 459 for (int i = 0; i < vector.length; ++i) { | 460 for (int i = 0; i < vector.length; ++i) { |
| 460 hash = 0x1fffffff & (hash + vector[i].hashCode); | 461 hash = 0x1fffffff & (hash + vector[i].hashCode); |
| 461 hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); | 462 hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); |
| 462 hash = hash ^ (hash >> 6); | 463 hash = hash ^ (hash >> 6); |
| 463 } | 464 } |
| 464 hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); | 465 hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); |
| 465 hash = hash ^ (hash >> 11); | 466 hash = hash ^ (hash >> 11); |
| 466 return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); | 467 return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); |
| 467 } | 468 } |
| 468 } | 469 } |
| 469 | 470 |
| 470 /// Converts GVN'able primitives to a vector containing all the values | 471 /// Converts GVN'able primitives to a vector containing all the values |
| 471 /// to be considered when computing a GVN for it. | 472 /// to be considered when computing a GVN for it. |
| 472 /// | 473 /// |
| 473 /// This includes the instruction type, inputs, effect numbers for any part | 474 /// This includes the instruction type, inputs, effect numbers for any part |
| 474 /// of the heap being depended on, as well as any instruction-specific payload | 475 /// of the heap being depended on, as well as any instruction-specific payload |
| (...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 593 static const int GET_INDEX = 6; | 594 static const int GET_INDEX = 6; |
| 594 static const int GET_STATIC = 7; | 595 static const int GET_STATIC = 7; |
| 595 static const int CONSTANT = 8; | 596 static const int CONSTANT = 8; |
| 596 static const int REIFY_RUNTIME_TYPE = 9; | 597 static const int REIFY_RUNTIME_TYPE = 9; |
| 597 static const int READ_TYPE_VARIABLE = 10; | 598 static const int READ_TYPE_VARIABLE = 10; |
| 598 static const int TYPE_EXPRESSION = 11; | 599 static const int TYPE_EXPRESSION = 11; |
| 599 static const int INTERCEPTOR = 12; | 600 static const int INTERCEPTOR = 12; |
| 600 } | 601 } |
| 601 | 602 |
| 602 typedef ReferenceCallback(Reference ref); | 603 typedef ReferenceCallback(Reference ref); |
| 604 |
| 603 class InputVisitor extends DeepRecursiveVisitor { | 605 class InputVisitor extends DeepRecursiveVisitor { |
| 604 ReferenceCallback callback; | 606 ReferenceCallback callback; |
| 605 | 607 |
| 606 InputVisitor(this.callback); | 608 InputVisitor(this.callback); |
| 607 | 609 |
| 608 @override | 610 @override |
| 609 processReference(Reference ref) { | 611 processReference(Reference ref) { |
| 610 callback(ref); | 612 callback(ref); |
| 611 } | 613 } |
| 612 | 614 |
| 613 static void forEach(Primitive node, ReferenceCallback callback) { | 615 static void forEach(Primitive node, ReferenceCallback callback) { |
| 614 new InputVisitor(callback).visit(node); | 616 new InputVisitor(callback).visit(node); |
| 615 } | 617 } |
| 616 } | 618 } |
| OLD | NEW |