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 |