| 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; |
| 11 import 'loop_hierarchy.dart'; | 11 import 'loop_hierarchy.dart'; |
| 12 import 'loop_effects.dart'; | 12 import 'loop_effects.dart'; |
| 13 import '../world.dart'; | 13 import '../world.dart'; |
| 14 import '../compiler.dart' show Compiler; | 14 import '../compiler.dart' show Compiler; |
| 15 import '../js_backend/js_backend.dart' show JavaScriptBackend; | 15 import '../js_backend/js_backend.dart' show JavaScriptBackend; |
| 16 import '../constants/values.dart'; | 16 import '../constants/values.dart'; |
| 17 import 'type_mask_system.dart'; |
| 17 | 18 |
| 18 /// Eliminates redundant primitives by reusing the value of another primitive | 19 /// Eliminates redundant primitives by reusing the value of another primitive |
| 19 /// that is known to have the same result. Primitives are also hoisted out of | 20 /// that is known to have the same result. Primitives are also hoisted out of |
| 20 /// loops when possible. | 21 /// loops when possible. |
| 21 /// | 22 /// |
| 22 /// Reusing values can introduce new temporaries, which in some cases is more | 23 /// Reusing values can introduce new temporaries, which in some cases is more |
| 23 /// expensive than recomputing the value on-demand. For example, pulling an | 24 /// expensive than recomputing the value on-demand. For example, pulling an |
| 24 /// expression such as "n+1" out of a loop is generally not worth it. | 25 /// expression such as "n+1" out of a loop is generally not worth it. |
| 25 /// Such primitives are said to be "trivial". | 26 /// Such primitives are said to be "trivial". |
| 26 /// | 27 /// |
| (...skipping 10 matching lines...) Expand all Loading... |
| 37 // | 38 // |
| 38 // TODO(asgerf): Put this pass at a better place in the pipeline. We currently | 39 // TODO(asgerf): Put this pass at a better place in the pipeline. We currently |
| 39 // cannot put it anywhere we want, because this pass relies on refinement | 40 // cannot put it anywhere we want, because this pass relies on refinement |
| 40 // nodes being present (for safety), whereas other passes rely on refinement | 41 // nodes being present (for safety), whereas other passes rely on refinement |
| 41 // nodes being absent (for simplicity & precision). | 42 // nodes being absent (for simplicity & precision). |
| 42 // | 43 // |
| 43 class GVN extends TrampolineRecursiveVisitor implements Pass { | 44 class GVN extends TrampolineRecursiveVisitor implements Pass { |
| 44 String get passName => 'GVN'; | 45 String get passName => 'GVN'; |
| 45 | 46 |
| 46 final Compiler compiler; | 47 final Compiler compiler; |
| 48 final TypeMaskSystem types; |
| 47 JavaScriptBackend get backend => compiler.backend; | 49 JavaScriptBackend get backend => compiler.backend; |
| 48 World get world => compiler.world; | 50 World get world => compiler.world; |
| 49 | 51 |
| 50 final GvnTable gvnTable = new GvnTable(); | 52 final GvnTable gvnTable = new GvnTable(); |
| 51 GvnVectorBuilder gvnVectorBuilder; | 53 GvnVectorBuilder gvnVectorBuilder; |
| 52 LoopHierarchy loopHierarchy; | 54 LoopHierarchy loopHierarchy; |
| 53 LoopSideEffects loopEffects; | 55 LoopSideEffects loopEffects; |
| 54 | 56 |
| 55 /// Effect numbers at the given join point. | 57 /// Effect numbers at the given join point. |
| 56 Map<Continuation, EffectNumbers> effectsAt = <Continuation, EffectNumbers>{}; | 58 Map<Continuation, EffectNumbers> effectsAt = <Continuation, EffectNumbers>{}; |
| (...skipping 16 matching lines...) Expand all Loading... |
| 73 <Continuation, List<int>>{}; | 75 <Continuation, List<int>>{}; |
| 74 | 76 |
| 75 /// Maps GVNs to a currently-in-scope binding for that value. | 77 /// Maps GVNs to a currently-in-scope binding for that value. |
| 76 final Map<int, Primitive> environment = <int, Primitive>{}; | 78 final Map<int, Primitive> environment = <int, Primitive>{}; |
| 77 | 79 |
| 78 /// Maps GVN'able primitives to their global value number. | 80 /// Maps GVN'able primitives to their global value number. |
| 79 final Map<Primitive, int> gvnFor = <Primitive, int>{}; | 81 final Map<Primitive, int> gvnFor = <Primitive, int>{}; |
| 80 | 82 |
| 81 Continuation currentLoopHeader; | 83 Continuation currentLoopHeader; |
| 82 | 84 |
| 83 GVN(this.compiler); | 85 GVN(this.compiler, this.types); |
| 84 | 86 |
| 85 int _usedEffectNumbers = 0; | 87 int _usedEffectNumbers = 0; |
| 86 int makeNewEffect() => ++_usedEffectNumbers; | 88 int makeNewEffect() => ++_usedEffectNumbers; |
| 87 | 89 |
| 88 void rewrite(FunctionDefinition node) { | 90 void rewrite(FunctionDefinition node) { |
| 89 gvnVectorBuilder = new GvnVectorBuilder(gvnFor, backend); | 91 gvnVectorBuilder = new GvnVectorBuilder(gvnFor, compiler, types); |
| 90 loopHierarchy = new LoopHierarchy(node); | 92 loopHierarchy = new LoopHierarchy(node); |
| 91 loopEffects = | 93 loopEffects = |
| 92 new LoopSideEffects(node, world, loopHierarchy: loopHierarchy); | 94 new LoopSideEffects(node, world, loopHierarchy: loopHierarchy); |
| 93 visit(node); | 95 visit(node); |
| 94 } | 96 } |
| 95 | 97 |
| 96 // ------------------ GLOBAL VALUE NUMBERING --------------------- | 98 // ------------------ GLOBAL VALUE NUMBERING --------------------- |
| 97 | 99 |
| 98 @override | 100 @override |
| 99 Expression traverseLetPrim(LetPrim node) { | 101 Expression traverseLetPrim(LetPrim node) { |
| (...skipping 206 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 306 } | 308 } |
| 307 | 309 |
| 308 /// Returns true if the given constant has almost no runtime cost. | 310 /// Returns true if the given constant has almost no runtime cost. |
| 309 bool isTrivialConstant(ConstantValue value) { | 311 bool isTrivialConstant(ConstantValue value) { |
| 310 return value.isPrimitive || value.isDummy; | 312 return value.isPrimitive || value.isDummy; |
| 311 } | 313 } |
| 312 | 314 |
| 313 /// True if [element] is a final or constant field or a function. | 315 /// True if [element] is a final or constant field or a function. |
| 314 bool isImmutable(Element element) { | 316 bool isImmutable(Element element) { |
| 315 if (element.isField && backend.isNative(element)) return false; | 317 if (element.isField && backend.isNative(element)) return false; |
| 316 return element.isField && (element.isFinal || element.isConst) || | 318 return element.isField && world.fieldNeverChanges(element) || |
| 317 element.isFunction; | 319 element.isFunction; |
| 318 } | 320 } |
| 319 | 321 |
| 322 bool isImmutableLength(GetLength length) { |
| 323 return types.isDefinitelyFixedLengthIndexable(length.object.definition.type, |
| 324 allowNull: true); |
| 325 } |
| 326 |
| 320 /// Assuming [prim] has no side effects, returns true if it can safely | 327 /// Assuming [prim] has no side effects, returns true if it can safely |
| 321 /// be hoisted out of [loop] without changing its value. | 328 /// be hoisted out of [loop] without changing its value. |
| 322 bool canHoistHeapDependencyOutOfLoop(Primitive prim, Continuation loop) { | 329 bool canHoistHeapDependencyOutOfLoop(Primitive prim, Continuation loop) { |
| 323 assert(prim.isSafeForElimination); | 330 assert(prim.isSafeForElimination); |
| 324 if (prim is GetLength) { | 331 if (prim is GetLength && !isImmutableLength(prim)) { |
| 325 return !loopEffects.loopChangesLength(loop); | 332 return !loopEffects.loopChangesLength(loop); |
| 326 } else if (prim is GetField && !isImmutable(prim.field)) { | 333 } else if (prim is GetField && !isImmutable(prim.field)) { |
| 327 return !loopEffects.getSideEffectsInLoop(loop).changesInstanceProperty(); | 334 return !loopEffects.getSideEffectsInLoop(loop).changesInstanceProperty(); |
| 328 } else if (prim is GetStatic && !isImmutable(prim.element)) { | 335 } else if (prim is GetStatic && !isImmutable(prim.element)) { |
| 329 return !loopEffects.getSideEffectsInLoop(loop).changesStaticProperty(); | 336 return !loopEffects.getSideEffectsInLoop(loop).changesStaticProperty(); |
| 330 } else if (prim is GetIndex) { | 337 } else if (prim is GetIndex) { |
| 331 return !loopEffects.getSideEffectsInLoop(loop).changesIndex(); | 338 return !loopEffects.getSideEffectsInLoop(loop).changesIndex(); |
| 332 } else { | 339 } else { |
| 333 return true; | 340 return true; |
| 334 } | 341 } |
| (...skipping 234 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 569 /// This includes the instruction type, inputs, effect numbers for any part | 576 /// This includes the instruction type, inputs, effect numbers for any part |
| 570 /// of the heap being depended on, as well as any instruction-specific payload | 577 /// of the heap being depended on, as well as any instruction-specific payload |
| 571 /// such as any DartTypes, Elements, and operator kinds. | 578 /// such as any DartTypes, Elements, and operator kinds. |
| 572 /// | 579 /// |
| 573 /// Each `visit` or `process` method for a primitive must initialize [vector] | 580 /// Each `visit` or `process` method for a primitive must initialize [vector] |
| 574 /// if the primitive is GVN'able and fill in any components except the inputs. | 581 /// if the primitive is GVN'able and fill in any components except the inputs. |
| 575 /// The inputs will be filled in by [processReference]. | 582 /// The inputs will be filled in by [processReference]. |
| 576 class GvnVectorBuilder extends DeepRecursiveVisitor { | 583 class GvnVectorBuilder extends DeepRecursiveVisitor { |
| 577 List vector; | 584 List vector; |
| 578 final Map<Primitive, int> gvnFor; | 585 final Map<Primitive, int> gvnFor; |
| 579 final JavaScriptBackend backend; | 586 final Compiler compiler; |
| 587 World get world => compiler.world; |
| 588 JavaScriptBackend get backend => compiler.backend; |
| 589 final TypeMaskSystem types; |
| 580 EffectNumbers effectNumbers; | 590 EffectNumbers effectNumbers; |
| 581 | 591 |
| 582 GvnVectorBuilder(this.gvnFor, this.backend); | 592 GvnVectorBuilder(this.gvnFor, this.compiler, this.types); |
| 583 | 593 |
| 584 List make(Primitive prim, EffectNumbers effectNumbers) { | 594 List make(Primitive prim, EffectNumbers effectNumbers) { |
| 585 this.effectNumbers = effectNumbers; | 595 this.effectNumbers = effectNumbers; |
| 586 vector = null; | 596 vector = null; |
| 587 visit(prim); | 597 visit(prim); |
| 588 return vector; | 598 return vector; |
| 589 } | 599 } |
| 590 | 600 |
| 591 /// The `process` methods below do not insert the referenced arguments into | 601 /// The `process` methods below do not insert the referenced arguments into |
| 592 /// the vector, but instead rely on them being inserted here. | 602 /// the vector, but instead rely on them being inserted here. |
| 593 processReference(Reference ref) { | 603 processReference(Reference ref) { |
| 594 if (vector == null) return; | 604 if (vector == null) return; |
| 595 Primitive prim = ref.definition.effectiveDefinition; | 605 Primitive prim = ref.definition.effectiveDefinition; |
| 596 vector.add(gvnFor[prim] ?? prim); | 606 vector.add(gvnFor[prim] ?? prim); |
| 597 } | 607 } |
| 598 | 608 |
| 599 processTypeTest(TypeTest node) { | 609 processTypeTest(TypeTest node) { |
| 600 vector = [GvnCode.TYPE_TEST, node.dartType]; | 610 vector = [GvnCode.TYPE_TEST, node.dartType]; |
| 601 } | 611 } |
| 602 | 612 |
| 603 processTypeTestViaFlag(TypeTestViaFlag node) { | 613 processTypeTestViaFlag(TypeTestViaFlag node) { |
| 604 vector = [GvnCode.TYPE_TEST_VIA_FLAG, node.dartType]; | 614 vector = [GvnCode.TYPE_TEST_VIA_FLAG, node.dartType]; |
| 605 } | 615 } |
| 606 | 616 |
| 607 processApplyBuiltinOperator(ApplyBuiltinOperator node) { | 617 processApplyBuiltinOperator(ApplyBuiltinOperator node) { |
| 608 vector = [GvnCode.BUILTIN_OPERATOR, node.operator.index]; | 618 vector = [GvnCode.BUILTIN_OPERATOR, node.operator.index]; |
| 609 } | 619 } |
| 610 | 620 |
| 611 processGetLength(GetLength node) { | 621 processGetLength(GetLength node) { |
| 612 // TODO(asgerf): Take fixed lengths into account? | 622 if (isImmutableLength(node)) { |
| 613 vector = [GvnCode.GET_LENGTH, effectNumbers.indexableLength]; | 623 // Omit the effect number for fixed-length lists. Note that if a the list |
| 624 // gets refined to a fixed-length type, we still won't be able to GVN a |
| 625 // GetLength across the refinement, because the first GetLength uses an |
| 626 // effect number in its vector while the second one does not. |
| 627 vector = [GvnCode.GET_LENGTH]; |
| 628 } else { |
| 629 vector = [GvnCode.GET_LENGTH, effectNumbers.indexableLength]; |
| 630 } |
| 614 } | 631 } |
| 615 | 632 |
| 616 bool isImmutable(Element element) { | 633 bool isImmutable(Element element) { |
| 617 return element.isFunction || | 634 return element.isFunction || |
| 618 element.isField && (element.isFinal || element.isConst); | 635 element.isField && world.fieldNeverChanges(element); |
| 636 } |
| 637 |
| 638 bool isImmutableLength(GetLength length) { |
| 639 return types.isDefinitelyFixedLengthIndexable(length.object.definition.type, |
| 640 allowNull: true); |
| 619 } | 641 } |
| 620 | 642 |
| 621 bool isNativeField(FieldElement field) { | 643 bool isNativeField(FieldElement field) { |
| 622 // TODO(asgerf): We should add a GetNativeField instruction. | 644 // TODO(asgerf): We should add a GetNativeField instruction. |
| 623 return backend.isNative(field); | 645 return backend.isNative(field); |
| 624 } | 646 } |
| 625 | 647 |
| 626 processGetField(GetField node) { | 648 processGetField(GetField node) { |
| 627 if (isNativeField(node.field)) { | 649 if (isNativeField(node.field)) { |
| 628 vector = null; // Native field access cannot be GVN'ed. | 650 vector = null; // Native field access cannot be GVN'ed. |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 697 | 719 |
| 698 @override | 720 @override |
| 699 processReference(Reference ref) { | 721 processReference(Reference ref) { |
| 700 callback(ref); | 722 callback(ref); |
| 701 } | 723 } |
| 702 | 724 |
| 703 static void forEach(Primitive node, ReferenceCallback callback) { | 725 static void forEach(Primitive node, ReferenceCallback callback) { |
| 704 new InputVisitor(callback).visit(node); | 726 new InputVisitor(callback).visit(node); |
| 705 } | 727 } |
| 706 } | 728 } |
| OLD | NEW |