| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, 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 import '../common/codegen.dart' show CodegenRegistry, CodegenWorkItem; | 5 import '../common/codegen.dart' show CodegenRegistry, CodegenWorkItem; |
| 6 import '../common/names.dart' show Selectors; | 6 import '../common/names.dart' show Selectors; |
| 7 import '../common/tasks.dart' show CompilerTask; | 7 import '../common/tasks.dart' show CompilerTask; |
| 8 import '../compiler.dart' show Compiler; | 8 import '../compiler.dart' show Compiler; |
| 9 import '../constants/constant_system.dart'; | 9 import '../constants/constant_system.dart'; |
| 10 import '../constants/values.dart'; | 10 import '../constants/values.dart'; |
| 11 import '../core_types.dart' show CoreClasses; | 11 import '../core_types.dart' show CommonElements, CoreClasses; |
| 12 import '../dart_types.dart'; | 12 import '../dart_types.dart'; |
| 13 import '../elements/elements.dart'; | 13 import '../elements/elements.dart'; |
| 14 import '../js/js.dart' as js; | 14 import '../js/js.dart' as js; |
| 15 import '../js_backend/backend_helpers.dart' show BackendHelpers; | 15 import '../js_backend/backend_helpers.dart' show BackendHelpers; |
| 16 import '../js_backend/js_backend.dart'; | 16 import '../js_backend/js_backend.dart'; |
| 17 import '../native/native.dart' as native; | 17 import '../native/native.dart' as native; |
| 18 import '../tree/dartstring.dart' as ast; | 18 import '../tree/dartstring.dart' as ast; |
| 19 import '../types/types.dart'; | 19 import '../types/types.dart'; |
| 20 import '../universe/selector.dart' show Selector; | 20 import '../universe/selector.dart' show Selector; |
| 21 import '../universe/side_effects.dart' show SideEffects; | 21 import '../universe/side_effects.dart' show SideEffects; |
| (...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 132 /// of identifying gvn-able lengths and mis-identifies some unions of fixed | 132 /// of identifying gvn-able lengths and mis-identifies some unions of fixed |
| 133 /// length indexables (see TODO) as not fixed length. | 133 /// length indexables (see TODO) as not fixed length. |
| 134 bool isFixedLength(mask, Compiler compiler) { | 134 bool isFixedLength(mask, Compiler compiler) { |
| 135 ClosedWorld closedWorld = compiler.closedWorld; | 135 ClosedWorld closedWorld = compiler.closedWorld; |
| 136 JavaScriptBackend backend = compiler.backend; | 136 JavaScriptBackend backend = compiler.backend; |
| 137 if (mask.isContainer && mask.length != null) { | 137 if (mask.isContainer && mask.length != null) { |
| 138 // A container on which we have inferred the length. | 138 // A container on which we have inferred the length. |
| 139 return true; | 139 return true; |
| 140 } | 140 } |
| 141 // TODO(sra): Recognize any combination of fixed length indexables. | 141 // TODO(sra): Recognize any combination of fixed length indexables. |
| 142 if (mask.containsOnly(backend.helpers.jsFixedArrayClass) || | 142 if (mask.containsOnly(closedWorld.backendClasses.fixedListImplementation) || |
| 143 mask.containsOnly(backend.helpers.jsUnmodifiableArrayClass) || | 143 mask.containsOnly(closedWorld.backendClasses.constListImplementation) || |
| 144 mask.containsOnlyString(closedWorld) || | 144 mask.containsOnlyString(closedWorld) || |
| 145 backend.isTypedArray(mask)) { | 145 closedWorld.commonMasks.isTypedArray(mask)) { |
| 146 return true; | 146 return true; |
| 147 } | 147 } |
| 148 return false; | 148 return false; |
| 149 } | 149 } |
| 150 | 150 |
| 151 /** | 151 /** |
| 152 * If both inputs to known operations are available execute the operation at | 152 * If both inputs to known operations are available execute the operation at |
| 153 * compile-time. | 153 * compile-time. |
| 154 */ | 154 */ |
| 155 class SsaInstructionSimplifier extends HBaseVisitor | 155 class SsaInstructionSimplifier extends HBaseVisitor |
| 156 implements OptimizationPhase { | 156 implements OptimizationPhase { |
| 157 // We don't produce constant-folded strings longer than this unless they have | 157 // We don't produce constant-folded strings longer than this unless they have |
| 158 // a single use. This protects against exponentially large constant folded | 158 // a single use. This protects against exponentially large constant folded |
| 159 // strings. | 159 // strings. |
| 160 static const MAX_SHARED_CONSTANT_FOLDED_STRING_LENGTH = 512; | 160 static const MAX_SHARED_CONSTANT_FOLDED_STRING_LENGTH = 512; |
| 161 | 161 |
| 162 final String name = "SsaInstructionSimplifier"; | 162 final String name = "SsaInstructionSimplifier"; |
| 163 final JavaScriptBackend backend; | 163 final JavaScriptBackend backend; |
| 164 final ConstantSystem constantSystem; | 164 final ConstantSystem constantSystem; |
| 165 final CodegenRegistry registry; | 165 final CodegenRegistry registry; |
| 166 HGraph graph; | 166 HGraph graph; |
| 167 Compiler get compiler => backend.compiler; | 167 Compiler get compiler => backend.compiler; |
| 168 final SsaOptimizerTask optimizer; | 168 final SsaOptimizerTask optimizer; |
| 169 | 169 |
| 170 SsaInstructionSimplifier( | 170 SsaInstructionSimplifier( |
| 171 this.constantSystem, this.backend, this.optimizer, this.registry); | 171 this.constantSystem, this.backend, this.optimizer, this.registry); |
| 172 | 172 |
| 173 CoreClasses get coreClasses => compiler.coreClasses; | |
| 174 | |
| 175 ClosedWorld get closedWorld => compiler.closedWorld; | 173 ClosedWorld get closedWorld => compiler.closedWorld; |
| 176 | 174 |
| 175 CommonElements get commonElements => closedWorld.commonElements; |
| 176 |
| 177 BackendHelpers get helpers => backend.helpers; | 177 BackendHelpers get helpers => backend.helpers; |
| 178 | 178 |
| 179 GlobalTypeInferenceResults get globalInferenceResults => |
| 180 compiler.globalInference.results; |
| 181 |
| 179 void visitGraph(HGraph visitee) { | 182 void visitGraph(HGraph visitee) { |
| 180 graph = visitee; | 183 graph = visitee; |
| 181 visitDominatorTree(visitee); | 184 visitDominatorTree(visitee); |
| 182 } | 185 } |
| 183 | 186 |
| 184 visitBasicBlock(HBasicBlock block) { | 187 visitBasicBlock(HBasicBlock block) { |
| 185 HInstruction instruction = block.first; | 188 HInstruction instruction = block.first; |
| 186 while (instruction != null) { | 189 while (instruction != null) { |
| 187 HInstruction next = instruction.next; | 190 HInstruction next = instruction.next; |
| 188 HInstruction replacement = instruction.accept(this); | 191 HInstruction replacement = instruction.accept(this); |
| (...skipping 251 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 440 return node; | 443 return node; |
| 441 } | 444 } |
| 442 | 445 |
| 443 HInstruction visitInvokeDynamicMethod(HInvokeDynamicMethod node) { | 446 HInstruction visitInvokeDynamicMethod(HInvokeDynamicMethod node) { |
| 444 propagateConstantValueToUses(node); | 447 propagateConstantValueToUses(node); |
| 445 if (node.isInterceptedCall) { | 448 if (node.isInterceptedCall) { |
| 446 HInstruction folded = handleInterceptedCall(node); | 449 HInstruction folded = handleInterceptedCall(node); |
| 447 if (folded != node) return folded; | 450 if (folded != node) return folded; |
| 448 } | 451 } |
| 449 | 452 |
| 450 TypeMask receiverType = node.getDartReceiver(compiler).instructionType; | 453 TypeMask receiverType = node.getDartReceiver(closedWorld).instructionType; |
| 451 Element element = | 454 Element element = |
| 452 compiler.closedWorld.locateSingleElement(node.selector, receiverType); | 455 closedWorld.locateSingleElement(node.selector, receiverType); |
| 453 // TODO(ngeoffray): Also fold if it's a getter or variable. | 456 // TODO(ngeoffray): Also fold if it's a getter or variable. |
| 454 if (element != null && | 457 if (element != null && |
| 455 element.isFunction | 458 element.isFunction |
| 456 // If we found out that the only target is an implicitly called | 459 // If we found out that the only target is an implicitly called |
| 457 // [:noSuchMethod:] we just ignore it. | 460 // [:noSuchMethod:] we just ignore it. |
| 458 && | 461 && |
| 459 node.selector.applies(element)) { | 462 node.selector.applies(element)) { |
| 460 MethodElement method = element; | 463 MethodElement method = element; |
| 461 | 464 |
| 462 if (backend.isNative(method)) { | 465 if (backend.isNative(method)) { |
| (...skipping 12 matching lines...) Expand all Loading... |
| 475 } | 478 } |
| 476 | 479 |
| 477 // Replace method calls through fields with a closure call on the value of | 480 // Replace method calls through fields with a closure call on the value of |
| 478 // the field. This usually removes the demand for the call-through stub and | 481 // the field. This usually removes the demand for the call-through stub and |
| 479 // makes the field load available to further optimization, e.g. LICM. | 482 // makes the field load available to further optimization, e.g. LICM. |
| 480 | 483 |
| 481 if (element != null && | 484 if (element != null && |
| 482 element.isField && | 485 element.isField && |
| 483 element.name == node.selector.name) { | 486 element.name == node.selector.name) { |
| 484 FieldElement field = element; | 487 FieldElement field = element; |
| 485 if (!backend.isNative(field) && !node.isCallOnInterceptor(compiler)) { | 488 if (!backend.isNative(field) && !node.isCallOnInterceptor(closedWorld)) { |
| 486 HInstruction receiver = node.getDartReceiver(compiler); | 489 HInstruction receiver = node.getDartReceiver(closedWorld); |
| 487 TypeMask type = TypeMaskFactory.inferredTypeForElement(field, compiler); | 490 TypeMask type = TypeMaskFactory.inferredTypeForElement( |
| 491 field, globalInferenceResults); |
| 488 HInstruction load = new HFieldGet(field, receiver, type); | 492 HInstruction load = new HFieldGet(field, receiver, type); |
| 489 node.block.addBefore(node, load); | 493 node.block.addBefore(node, load); |
| 490 Selector callSelector = new Selector.callClosureFrom(node.selector); | 494 Selector callSelector = new Selector.callClosureFrom(node.selector); |
| 491 List<HInstruction> inputs = <HInstruction>[load] | 495 List<HInstruction> inputs = <HInstruction>[load] |
| 492 ..addAll(node.inputs.skip(node.isInterceptedCall ? 2 : 1)); | 496 ..addAll(node.inputs.skip(node.isInterceptedCall ? 2 : 1)); |
| 493 HInstruction closureCall = | 497 HInstruction closureCall = |
| 494 new HInvokeClosure(callSelector, inputs, node.instructionType) | 498 new HInvokeClosure(callSelector, inputs, node.instructionType) |
| 495 ..sourceInformation = node.sourceInformation; | 499 ..sourceInformation = node.sourceInformation; |
| 496 node.block.addAfter(load, closureCall); | 500 node.block.addAfter(load, closureCall); |
| 497 return closureCall; | 501 return closureCall; |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 547 } | 551 } |
| 548 }); | 552 }); |
| 549 | 553 |
| 550 if (!canInline) return null; | 554 if (!canInline) return null; |
| 551 | 555 |
| 552 // Strengthen instruction type from annotations to help optimize | 556 // Strengthen instruction type from annotations to help optimize |
| 553 // dependent instructions. | 557 // dependent instructions. |
| 554 native.NativeBehavior nativeBehavior = | 558 native.NativeBehavior nativeBehavior = |
| 555 backend.getNativeMethodBehavior(method); | 559 backend.getNativeMethodBehavior(method); |
| 556 TypeMask returnType = | 560 TypeMask returnType = |
| 557 TypeMaskFactory.fromNativeBehavior(nativeBehavior, compiler); | 561 TypeMaskFactory.fromNativeBehavior(nativeBehavior, closedWorld); |
| 558 HInvokeDynamicMethod result = | 562 HInvokeDynamicMethod result = |
| 559 new HInvokeDynamicMethod(node.selector, node.mask, inputs, returnType); | 563 new HInvokeDynamicMethod(node.selector, node.mask, inputs, returnType); |
| 560 result.element = method; | 564 result.element = method; |
| 561 return result; | 565 return result; |
| 562 } | 566 } |
| 563 | 567 |
| 564 HInstruction visitBoundsCheck(HBoundsCheck node) { | 568 HInstruction visitBoundsCheck(HBoundsCheck node) { |
| 565 HInstruction index = node.index; | 569 HInstruction index = node.index; |
| 566 if (index.isInteger(closedWorld)) return node; | 570 if (index.isInteger(closedWorld)) return node; |
| 567 if (index.isConstant()) { | 571 if (index.isConstant()) { |
| (...skipping 157 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 725 } | 729 } |
| 726 | 730 |
| 727 HInstruction visitIs(HIs node) { | 731 HInstruction visitIs(HIs node) { |
| 728 DartType type = node.typeExpression; | 732 DartType type = node.typeExpression; |
| 729 Element element = type.element; | 733 Element element = type.element; |
| 730 | 734 |
| 731 if (!node.isRawCheck) { | 735 if (!node.isRawCheck) { |
| 732 return node; | 736 return node; |
| 733 } else if (type.isTypedef) { | 737 } else if (type.isTypedef) { |
| 734 return node; | 738 return node; |
| 735 } else if (element == coreClasses.functionClass) { | 739 } else if (element == commonElements.functionClass) { |
| 736 return node; | 740 return node; |
| 737 } | 741 } |
| 738 | 742 |
| 739 if (type.isObject || type.treatAsDynamic) { | 743 if (type.isObject || type.treatAsDynamic) { |
| 740 return graph.addConstantBool(true, compiler); | 744 return graph.addConstantBool(true, compiler); |
| 741 } | 745 } |
| 742 | 746 |
| 743 HInstruction expression = node.expression; | 747 HInstruction expression = node.expression; |
| 744 if (expression.isInteger(closedWorld)) { | 748 if (expression.isInteger(closedWorld)) { |
| 745 if (element == coreClasses.intClass || | 749 if (element == commonElements.intClass || |
| 746 element == coreClasses.numClass || | 750 element == commonElements.numClass || |
| 747 Elements.isNumberOrStringSupertype(element, compiler)) { | 751 Elements.isNumberOrStringSupertype(element, commonElements)) { |
| 748 return graph.addConstantBool(true, compiler); | 752 return graph.addConstantBool(true, compiler); |
| 749 } else if (element == coreClasses.doubleClass) { | 753 } else if (element == commonElements.doubleClass) { |
| 750 // We let the JS semantics decide for that check. Currently | 754 // We let the JS semantics decide for that check. Currently |
| 751 // the code we emit will always return true. | 755 // the code we emit will always return true. |
| 752 return node; | 756 return node; |
| 753 } else { | 757 } else { |
| 754 return graph.addConstantBool(false, compiler); | 758 return graph.addConstantBool(false, compiler); |
| 755 } | 759 } |
| 756 } else if (expression.isDouble(closedWorld)) { | 760 } else if (expression.isDouble(closedWorld)) { |
| 757 if (element == coreClasses.doubleClass || | 761 if (element == commonElements.doubleClass || |
| 758 element == coreClasses.numClass || | 762 element == commonElements.numClass || |
| 759 Elements.isNumberOrStringSupertype(element, compiler)) { | 763 Elements.isNumberOrStringSupertype(element, commonElements)) { |
| 760 return graph.addConstantBool(true, compiler); | 764 return graph.addConstantBool(true, compiler); |
| 761 } else if (element == coreClasses.intClass) { | 765 } else if (element == commonElements.intClass) { |
| 762 // We let the JS semantics decide for that check. Currently | 766 // We let the JS semantics decide for that check. Currently |
| 763 // the code we emit will return true for a double that can be | 767 // the code we emit will return true for a double that can be |
| 764 // represented as a 31-bit integer and for -0.0. | 768 // represented as a 31-bit integer and for -0.0. |
| 765 return node; | 769 return node; |
| 766 } else { | 770 } else { |
| 767 return graph.addConstantBool(false, compiler); | 771 return graph.addConstantBool(false, compiler); |
| 768 } | 772 } |
| 769 } else if (expression.isNumber(closedWorld)) { | 773 } else if (expression.isNumber(closedWorld)) { |
| 770 if (element == coreClasses.numClass) { | 774 if (element == commonElements.numClass) { |
| 771 return graph.addConstantBool(true, compiler); | 775 return graph.addConstantBool(true, compiler); |
| 772 } else { | 776 } else { |
| 773 // We cannot just return false, because the expression may be of | 777 // We cannot just return false, because the expression may be of |
| 774 // type int or double. | 778 // type int or double. |
| 775 } | 779 } |
| 776 } else if (expression.canBePrimitiveNumber(closedWorld) && | 780 } else if (expression.canBePrimitiveNumber(closedWorld) && |
| 777 element == coreClasses.intClass) { | 781 element == commonElements.intClass) { |
| 778 // We let the JS semantics decide for that check. | 782 // We let the JS semantics decide for that check. |
| 779 return node; | 783 return node; |
| 780 // We need the [:hasTypeArguments:] check because we don't have | 784 // We need the [:hasTypeArguments:] check because we don't have |
| 781 // the notion of generics in the backend. For example, [:this:] in | 785 // the notion of generics in the backend. For example, [:this:] in |
| 782 // a class [:A<T>:], is currently always considered to have the | 786 // a class [:A<T>:], is currently always considered to have the |
| 783 // raw type. | 787 // raw type. |
| 784 } else if (!RuntimeTypes.hasTypeArguments(type)) { | 788 } else if (!RuntimeTypes.hasTypeArguments(type)) { |
| 785 TypeMask expressionMask = expression.instructionType; | 789 TypeMask expressionMask = expression.instructionType; |
| 786 assert(TypeMask.assertIsNormalized(expressionMask, closedWorld)); | 790 assert(TypeMask.assertIsNormalized(expressionMask, closedWorld)); |
| 787 TypeMask typeMask = (element == coreClasses.nullClass) | 791 TypeMask typeMask = (element == commonElements.nullClass) |
| 788 ? new TypeMask.subtype(element, closedWorld) | 792 ? new TypeMask.subtype(element, closedWorld) |
| 789 : new TypeMask.nonNullSubtype(element, closedWorld); | 793 : new TypeMask.nonNullSubtype(element, closedWorld); |
| 790 if (expressionMask.union(typeMask, closedWorld) == typeMask) { | 794 if (expressionMask.union(typeMask, closedWorld) == typeMask) { |
| 791 return graph.addConstantBool(true, compiler); | 795 return graph.addConstantBool(true, compiler); |
| 792 } else if (expressionMask.isDisjoint(typeMask, closedWorld)) { | 796 } else if (expressionMask.isDisjoint(typeMask, closedWorld)) { |
| 793 return graph.addConstantBool(false, compiler); | 797 return graph.addConstantBool(false, compiler); |
| 794 } | 798 } |
| 795 } | 799 } |
| 796 return node; | 800 return node; |
| 797 } | 801 } |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 838 HInstruction input = node.checkedInput; | 842 HInstruction input = node.checkedInput; |
| 839 TypeMask inputType = input.instructionType; | 843 TypeMask inputType = input.instructionType; |
| 840 return inputType.isInMask(checkedType, closedWorld) ? input : node; | 844 return inputType.isInMask(checkedType, closedWorld) ? input : node; |
| 841 } | 845 } |
| 842 | 846 |
| 843 HInstruction removeCheck(HCheck node) => node.checkedInput; | 847 HInstruction removeCheck(HCheck node) => node.checkedInput; |
| 844 | 848 |
| 845 FieldElement findConcreteFieldForDynamicAccess( | 849 FieldElement findConcreteFieldForDynamicAccess( |
| 846 HInstruction receiver, Selector selector) { | 850 HInstruction receiver, Selector selector) { |
| 847 TypeMask receiverType = receiver.instructionType; | 851 TypeMask receiverType = receiver.instructionType; |
| 848 return compiler.closedWorld.locateSingleField(selector, receiverType); | 852 return closedWorld.locateSingleField(selector, receiverType); |
| 849 } | 853 } |
| 850 | 854 |
| 851 HInstruction visitFieldGet(HFieldGet node) { | 855 HInstruction visitFieldGet(HFieldGet node) { |
| 852 if (node.isNullCheck) return node; | 856 if (node.isNullCheck) return node; |
| 853 var receiver = node.receiver; | 857 var receiver = node.receiver; |
| 854 if (node.element == helpers.jsIndexableLength) { | 858 if (node.element == helpers.jsIndexableLength) { |
| 855 if (graph.allocatedFixedLists.contains(receiver)) { | 859 if (graph.allocatedFixedLists.contains(receiver)) { |
| 856 // TODO(ngeoffray): checking if the second input is an integer | 860 // TODO(ngeoffray): checking if the second input is an integer |
| 857 // should not be necessary but it currently makes it easier for | 861 // should not be necessary but it currently makes it easier for |
| 858 // other optimizations to reason about a fixed length constructor | 862 // other optimizations to reason about a fixed length constructor |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 909 } | 913 } |
| 910 return node; | 914 return node; |
| 911 } | 915 } |
| 912 | 916 |
| 913 HInstruction visitInvokeDynamicGetter(HInvokeDynamicGetter node) { | 917 HInstruction visitInvokeDynamicGetter(HInvokeDynamicGetter node) { |
| 914 propagateConstantValueToUses(node); | 918 propagateConstantValueToUses(node); |
| 915 if (node.isInterceptedCall) { | 919 if (node.isInterceptedCall) { |
| 916 HInstruction folded = handleInterceptedCall(node); | 920 HInstruction folded = handleInterceptedCall(node); |
| 917 if (folded != node) return folded; | 921 if (folded != node) return folded; |
| 918 } | 922 } |
| 919 HInstruction receiver = node.getDartReceiver(compiler); | 923 HInstruction receiver = node.getDartReceiver(closedWorld); |
| 920 FieldElement field = | 924 FieldElement field = |
| 921 findConcreteFieldForDynamicAccess(receiver, node.selector); | 925 findConcreteFieldForDynamicAccess(receiver, node.selector); |
| 922 if (field != null) return directFieldGet(receiver, field); | 926 if (field != null) return directFieldGet(receiver, field); |
| 923 | 927 |
| 924 if (node.element == null) { | 928 if (node.element == null) { |
| 925 MemberElement element = closedWorld.locateSingleElement( | 929 MemberElement element = closedWorld.locateSingleElement( |
| 926 node.selector, receiver.instructionType); | 930 node.selector, receiver.instructionType); |
| 927 if (element != null && element.name == node.selector.name) { | 931 if (element != null && element.name == node.selector.name) { |
| 928 node.element = element; | 932 node.element = element; |
| 929 if (element.isFunction) { | 933 if (element.isFunction) { |
| 930 // A property extraction getter, aka a tear-off. | 934 // A property extraction getter, aka a tear-off. |
| 931 node.sideEffects.clearAllDependencies(); | 935 node.sideEffects.clearAllDependencies(); |
| 932 node.sideEffects.clearAllSideEffects(); | 936 node.sideEffects.clearAllSideEffects(); |
| 933 node.setUseGvn(); // We don't care about identity of tear-offs. | 937 node.setUseGvn(); // We don't care about identity of tear-offs. |
| 934 } | 938 } |
| 935 } | 939 } |
| 936 } | 940 } |
| 937 return node; | 941 return node; |
| 938 } | 942 } |
| 939 | 943 |
| 940 HInstruction directFieldGet(HInstruction receiver, FieldElement field) { | 944 HInstruction directFieldGet(HInstruction receiver, FieldElement field) { |
| 941 bool isAssignable = !closedWorld.fieldNeverChanges(field); | 945 bool isAssignable = !closedWorld.fieldNeverChanges(field); |
| 942 | 946 |
| 943 TypeMask type; | 947 TypeMask type; |
| 944 if (backend.isNative(field.enclosingClass)) { | 948 if (backend.isNative(field.enclosingClass)) { |
| 945 type = TypeMaskFactory.fromNativeBehavior( | 949 type = TypeMaskFactory.fromNativeBehavior( |
| 946 backend.getNativeFieldLoadBehavior(field), compiler); | 950 backend.getNativeFieldLoadBehavior(field), closedWorld); |
| 947 } else { | 951 } else { |
| 948 type = TypeMaskFactory.inferredTypeForElement(field, compiler); | 952 type = |
| 953 TypeMaskFactory.inferredTypeForElement(field, globalInferenceResults); |
| 949 } | 954 } |
| 950 | 955 |
| 951 return new HFieldGet(field, receiver, type, isAssignable: isAssignable); | 956 return new HFieldGet(field, receiver, type, isAssignable: isAssignable); |
| 952 } | 957 } |
| 953 | 958 |
| 954 HInstruction visitInvokeDynamicSetter(HInvokeDynamicSetter node) { | 959 HInstruction visitInvokeDynamicSetter(HInvokeDynamicSetter node) { |
| 955 if (node.isInterceptedCall) { | 960 if (node.isInterceptedCall) { |
| 956 HInstruction folded = handleInterceptedCall(node); | 961 HInstruction folded = handleInterceptedCall(node); |
| 957 if (folded != node) return folded; | 962 if (folded != node) return folded; |
| 958 } | 963 } |
| 959 | 964 |
| 960 HInstruction receiver = node.getDartReceiver(compiler); | 965 HInstruction receiver = node.getDartReceiver(closedWorld); |
| 961 FieldElement field = | 966 FieldElement field = |
| 962 findConcreteFieldForDynamicAccess(receiver, node.selector); | 967 findConcreteFieldForDynamicAccess(receiver, node.selector); |
| 963 if (field == null || !field.isAssignable) return node; | 968 if (field == null || !field.isAssignable) return node; |
| 964 // Use `node.inputs.last` in case the call follows the interceptor calling | 969 // Use `node.inputs.last` in case the call follows the interceptor calling |
| 965 // convention, but is not a call on an interceptor. | 970 // convention, but is not a call on an interceptor. |
| 966 HInstruction value = node.inputs.last; | 971 HInstruction value = node.inputs.last; |
| 967 if (compiler.options.enableTypeAssertions) { | 972 if (compiler.options.enableTypeAssertions) { |
| 968 DartType type = field.type; | 973 DartType type = field.type; |
| 969 if (!type.treatAsRaw || | 974 if (!type.treatAsRaw || |
| 970 type.isTypeVariable || | 975 type.isTypeVariable || |
| (...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1087 | 1092 |
| 1088 HInstruction tryToString() { | 1093 HInstruction tryToString() { |
| 1089 // If the `toString` method is guaranteed to return a string we can call | 1094 // If the `toString` method is guaranteed to return a string we can call |
| 1090 // it directly. Keep the stringifier for primitives (since they have fast | 1095 // it directly. Keep the stringifier for primitives (since they have fast |
| 1091 // path code in the stringifier) and for classes requiring interceptors | 1096 // path code in the stringifier) and for classes requiring interceptors |
| 1092 // (since SsaInstructionSimplifier runs after SsaSimplifyInterceptors). | 1097 // (since SsaInstructionSimplifier runs after SsaSimplifyInterceptors). |
| 1093 if (input.canBePrimitive(closedWorld)) return null; | 1098 if (input.canBePrimitive(closedWorld)) return null; |
| 1094 if (input.canBeNull()) return null; | 1099 if (input.canBeNull()) return null; |
| 1095 Selector selector = Selectors.toString_; | 1100 Selector selector = Selectors.toString_; |
| 1096 TypeMask toStringType = TypeMaskFactory.inferredTypeForSelector( | 1101 TypeMask toStringType = TypeMaskFactory.inferredTypeForSelector( |
| 1097 selector, input.instructionType, compiler); | 1102 selector, input.instructionType, globalInferenceResults); |
| 1098 if (!toStringType.containsOnlyString(closedWorld)) return null; | 1103 if (!toStringType.containsOnlyString(closedWorld)) return null; |
| 1099 // All intercepted classes extend `Interceptor`, so if the receiver can't | 1104 // All intercepted classes extend `Interceptor`, so if the receiver can't |
| 1100 // be a class extending `Interceptor` then it can be called directly. | 1105 // be a class extending `Interceptor` then it can be called directly. |
| 1101 if (new TypeMask.nonNullSubclass(helpers.jsInterceptorClass, closedWorld) | 1106 if (new TypeMask.nonNullSubclass(helpers.jsInterceptorClass, closedWorld) |
| 1102 .isDisjoint(input.instructionType, closedWorld)) { | 1107 .isDisjoint(input.instructionType, closedWorld)) { |
| 1103 var inputs = <HInstruction>[input, input]; // [interceptor, receiver]. | 1108 var inputs = <HInstruction>[input, input]; // [interceptor, receiver]. |
| 1104 HInstruction result = new HInvokeDynamicMethod( | 1109 HInstruction result = new HInvokeDynamicMethod( |
| 1105 selector, | 1110 selector, |
| 1106 input.instructionType, // receiver mask. | 1111 input.instructionType, // receiver mask. |
| 1107 inputs, | 1112 inputs, |
| (...skipping 322 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1430 return hole.isPositional && hole.nameOrPosition == 0; | 1435 return hole.isPositional && hole.nameOrPosition == 0; |
| 1431 } | 1436 } |
| 1432 } | 1437 } |
| 1433 return false; | 1438 return false; |
| 1434 } | 1439 } |
| 1435 | 1440 |
| 1436 /// Returns whether the next throwing instruction that may have side | 1441 /// Returns whether the next throwing instruction that may have side |
| 1437 /// effects after [instruction], throws [NoSuchMethodError] on the | 1442 /// effects after [instruction], throws [NoSuchMethodError] on the |
| 1438 /// same receiver of [instruction]. | 1443 /// same receiver of [instruction]. |
| 1439 bool hasFollowingThrowingNSM(HInstruction instruction) { | 1444 bool hasFollowingThrowingNSM(HInstruction instruction) { |
| 1440 HInstruction receiver = instruction.getDartReceiver(compiler); | 1445 HInstruction receiver = instruction.getDartReceiver(closedWorld); |
| 1441 HInstruction current = instruction.next; | 1446 HInstruction current = instruction.next; |
| 1442 do { | 1447 do { |
| 1443 if ((current.getDartReceiver(compiler) == receiver) && | 1448 if ((current.getDartReceiver(closedWorld) == receiver) && |
| 1444 current.canThrow()) { | 1449 current.canThrow()) { |
| 1445 return true; | 1450 return true; |
| 1446 } | 1451 } |
| 1447 if (current is HForeignCode && | 1452 if (current is HForeignCode && |
| 1448 templateThrowsNSMonNull(current, receiver)) { | 1453 templateThrowsNSMonNull(current, receiver)) { |
| 1449 return true; | 1454 return true; |
| 1450 } | 1455 } |
| 1451 if (current.canThrow() || current.sideEffects.hasSideEffects()) { | 1456 if (current.canThrow() || current.sideEffects.hasSideEffects()) { |
| 1452 return false; | 1457 return false; |
| 1453 } | 1458 } |
| (...skipping 23 matching lines...) Expand all Loading... |
| 1477 } | 1482 } |
| 1478 | 1483 |
| 1479 bool isTrivialDeadStoreReceiver(HInstruction instruction) { | 1484 bool isTrivialDeadStoreReceiver(HInstruction instruction) { |
| 1480 // For an allocation, if all the loads are dead (awaiting removal after | 1485 // For an allocation, if all the loads are dead (awaiting removal after |
| 1481 // SsaLoadElimination) and the only other uses are stores, then the | 1486 // SsaLoadElimination) and the only other uses are stores, then the |
| 1482 // allocation does not escape which makes all the stores dead too. | 1487 // allocation does not escape which makes all the stores dead too. |
| 1483 bool isDeadUse(HInstruction use) { | 1488 bool isDeadUse(HInstruction use) { |
| 1484 if (use is HFieldSet) { | 1489 if (use is HFieldSet) { |
| 1485 // The use must be the receiver. Even if the use is also the argument, | 1490 // The use must be the receiver. Even if the use is also the argument, |
| 1486 // i.e. a.x = a, the store is still dead if all other uses are dead. | 1491 // i.e. a.x = a, the store is still dead if all other uses are dead. |
| 1487 if (use.getDartReceiver(compiler) == instruction) return true; | 1492 if (use.getDartReceiver(closedWorld) == instruction) return true; |
| 1488 } else if (use is HFieldGet) { | 1493 } else if (use is HFieldGet) { |
| 1489 assert(use.getDartReceiver(compiler) == instruction); | 1494 assert(use.getDartReceiver(closedWorld) == instruction); |
| 1490 if (isDeadCode(use)) return true; | 1495 if (isDeadCode(use)) return true; |
| 1491 } | 1496 } |
| 1492 return false; | 1497 return false; |
| 1493 } | 1498 } |
| 1494 | 1499 |
| 1495 return instruction.isAllocation && | 1500 return instruction.isAllocation && |
| 1496 instruction.isPure() && | 1501 instruction.isPure() && |
| 1497 trivialDeadStoreReceivers.putIfAbsent( | 1502 trivialDeadStoreReceivers.putIfAbsent( |
| 1498 instruction, () => instruction.usedBy.every(isDeadUse)); | 1503 instruction, () => instruction.usedBy.every(isDeadUse)); |
| 1499 } | 1504 } |
| 1500 | 1505 |
| 1501 bool isTrivialDeadStore(HInstruction instruction) { | 1506 bool isTrivialDeadStore(HInstruction instruction) { |
| 1502 return instruction is HFieldSet && | 1507 return instruction is HFieldSet && |
| 1503 isTrivialDeadStoreReceiver(instruction.getDartReceiver(compiler)); | 1508 isTrivialDeadStoreReceiver(instruction.getDartReceiver(closedWorld)); |
| 1504 } | 1509 } |
| 1505 | 1510 |
| 1506 bool isDeadCode(HInstruction instruction) { | 1511 bool isDeadCode(HInstruction instruction) { |
| 1507 if (!instruction.usedBy.isEmpty) return false; | 1512 if (!instruction.usedBy.isEmpty) return false; |
| 1508 if (isTrivialDeadStore(instruction)) return true; | 1513 if (isTrivialDeadStore(instruction)) return true; |
| 1509 if (instruction.sideEffects.hasSideEffects()) return false; | 1514 if (instruction.sideEffects.hasSideEffects()) return false; |
| 1510 if (instruction.canThrow() && | 1515 if (instruction.canThrow() && |
| 1511 instruction.onlyThrowsNSM() && | 1516 instruction.onlyThrowsNSM() && |
| 1512 hasFollowingThrowingNSM(instruction)) { | 1517 hasFollowingThrowingNSM(instruction)) { |
| 1513 return true; | 1518 return true; |
| (...skipping 708 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2222 * location. | 2227 * location. |
| 2223 */ | 2228 */ |
| 2224 class SsaLoadElimination extends HBaseVisitor implements OptimizationPhase { | 2229 class SsaLoadElimination extends HBaseVisitor implements OptimizationPhase { |
| 2225 final Compiler compiler; | 2230 final Compiler compiler; |
| 2226 final String name = "SsaLoadElimination"; | 2231 final String name = "SsaLoadElimination"; |
| 2227 MemorySet memorySet; | 2232 MemorySet memorySet; |
| 2228 List<MemorySet> memories; | 2233 List<MemorySet> memories; |
| 2229 | 2234 |
| 2230 SsaLoadElimination(this.compiler); | 2235 SsaLoadElimination(this.compiler); |
| 2231 | 2236 |
| 2237 ClosedWorld get closedWorld => compiler.closedWorld; |
| 2238 |
| 2232 void visitGraph(HGraph graph) { | 2239 void visitGraph(HGraph graph) { |
| 2233 memories = new List<MemorySet>(graph.blocks.length); | 2240 memories = new List<MemorySet>(graph.blocks.length); |
| 2234 List<HBasicBlock> blocks = graph.blocks; | 2241 List<HBasicBlock> blocks = graph.blocks; |
| 2235 for (int i = 0; i < blocks.length; i++) { | 2242 for (int i = 0; i < blocks.length; i++) { |
| 2236 HBasicBlock block = blocks[i]; | 2243 HBasicBlock block = blocks[i]; |
| 2237 visitBasicBlock(block); | 2244 visitBasicBlock(block); |
| 2238 if (block.successors.isNotEmpty && block.successors[0].isLoopHeader()) { | 2245 if (block.successors.isNotEmpty && block.successors[0].isLoopHeader()) { |
| 2239 // We've reached the ending block of a loop. Iterate over the | 2246 // We've reached the ending block of a loop. Iterate over the |
| 2240 // blocks of the loop again to take values that flow from that | 2247 // blocks of the loop again to take values that flow from that |
| 2241 // ending block into account. | 2248 // ending block into account. |
| 2242 for (int j = block.successors[0].id; j <= block.id; j++) { | 2249 for (int j = block.successors[0].id; j <= block.id; j++) { |
| 2243 visitBasicBlock(blocks[j]); | 2250 visitBasicBlock(blocks[j]); |
| 2244 } | 2251 } |
| 2245 } | 2252 } |
| 2246 } | 2253 } |
| 2247 } | 2254 } |
| 2248 | 2255 |
| 2249 void visitBasicBlock(HBasicBlock block) { | 2256 void visitBasicBlock(HBasicBlock block) { |
| 2250 if (block.predecessors.length == 0) { | 2257 if (block.predecessors.length == 0) { |
| 2251 // Entry block. | 2258 // Entry block. |
| 2252 memorySet = new MemorySet(compiler); | 2259 memorySet = new MemorySet(closedWorld); |
| 2253 } else if (block.predecessors.length == 1 && | 2260 } else if (block.predecessors.length == 1 && |
| 2254 block.predecessors[0].successors.length == 1) { | 2261 block.predecessors[0].successors.length == 1) { |
| 2255 // No need to clone, there is no other successor for | 2262 // No need to clone, there is no other successor for |
| 2256 // `block.predecessors[0]`, and this block has only one | 2263 // `block.predecessors[0]`, and this block has only one |
| 2257 // predecessor. Since we are not going to visit | 2264 // predecessor. Since we are not going to visit |
| 2258 // `block.predecessors[0]` again, we can just re-use its | 2265 // `block.predecessors[0]` again, we can just re-use its |
| 2259 // [memorySet]. | 2266 // [memorySet]. |
| 2260 memorySet = memories[block.predecessors[0].id]; | 2267 memorySet = memories[block.predecessors[0].id]; |
| 2261 } else if (block.predecessors.length == 1) { | 2268 } else if (block.predecessors.length == 1) { |
| 2262 // Clone the memorySet of the predecessor, because it is also used | 2269 // Clone the memorySet of the predecessor, because it is also used |
| (...skipping 13 matching lines...) Expand all Loading... |
| 2276 while (instruction != null) { | 2283 while (instruction != null) { |
| 2277 HInstruction next = instruction.next; | 2284 HInstruction next = instruction.next; |
| 2278 instruction.accept(this); | 2285 instruction.accept(this); |
| 2279 instruction = next; | 2286 instruction = next; |
| 2280 } | 2287 } |
| 2281 } | 2288 } |
| 2282 | 2289 |
| 2283 void visitFieldGet(HFieldGet instruction) { | 2290 void visitFieldGet(HFieldGet instruction) { |
| 2284 if (instruction.isNullCheck) return; | 2291 if (instruction.isNullCheck) return; |
| 2285 MemberElement element = instruction.element; | 2292 MemberElement element = instruction.element; |
| 2286 HInstruction receiver = instruction.getDartReceiver(compiler).nonCheck(); | 2293 HInstruction receiver = instruction.getDartReceiver(closedWorld).nonCheck(); |
| 2287 HInstruction existing = memorySet.lookupFieldValue(element, receiver); | 2294 HInstruction existing = memorySet.lookupFieldValue(element, receiver); |
| 2288 if (existing != null) { | 2295 if (existing != null) { |
| 2289 instruction.block.rewriteWithBetterUser(instruction, existing); | 2296 instruction.block.rewriteWithBetterUser(instruction, existing); |
| 2290 instruction.block.remove(instruction); | 2297 instruction.block.remove(instruction); |
| 2291 } else { | 2298 } else { |
| 2292 memorySet.registerFieldValue(element, receiver, instruction); | 2299 memorySet.registerFieldValue(element, receiver, instruction); |
| 2293 } | 2300 } |
| 2294 } | 2301 } |
| 2295 | 2302 |
| 2296 void visitFieldSet(HFieldSet instruction) { | 2303 void visitFieldSet(HFieldSet instruction) { |
| 2297 HInstruction receiver = instruction.getDartReceiver(compiler).nonCheck(); | 2304 HInstruction receiver = instruction.getDartReceiver(closedWorld).nonCheck(); |
| 2298 memorySet.registerFieldValueUpdate( | 2305 memorySet.registerFieldValueUpdate( |
| 2299 instruction.element, receiver, instruction.inputs.last); | 2306 instruction.element, receiver, instruction.inputs.last); |
| 2300 } | 2307 } |
| 2301 | 2308 |
| 2302 void visitCreate(HCreate instruction) { | 2309 void visitCreate(HCreate instruction) { |
| 2303 memorySet.registerAllocation(instruction); | 2310 memorySet.registerAllocation(instruction); |
| 2304 if (shouldTrackInitialValues(instruction)) { | 2311 if (shouldTrackInitialValues(instruction)) { |
| 2305 int argumentIndex = 0; | 2312 int argumentIndex = 0; |
| 2306 instruction.element.forEachInstanceField((_, FieldElement member) { | 2313 instruction.element.forEachInstanceField((_, FieldElement member) { |
| 2307 if (compiler.elementHasCompileTimeError(member)) return; | 2314 if (compiler.elementHasCompileTimeError(member)) return; |
| (...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2427 | 2434 |
| 2428 /** | 2435 /** |
| 2429 * Holds values of memory places. | 2436 * Holds values of memory places. |
| 2430 * | 2437 * |
| 2431 * Generally, values that name a place (a receiver) have type refinements and | 2438 * Generally, values that name a place (a receiver) have type refinements and |
| 2432 * other checks removed to ensure that checks and type refinements do not | 2439 * other checks removed to ensure that checks and type refinements do not |
| 2433 * confuse aliasing. Values stored into a memory place keep the type | 2440 * confuse aliasing. Values stored into a memory place keep the type |
| 2434 * refinements to help further optimizations. | 2441 * refinements to help further optimizations. |
| 2435 */ | 2442 */ |
| 2436 class MemorySet { | 2443 class MemorySet { |
| 2437 final Compiler compiler; | 2444 final ClosedWorld closedWorld; |
| 2438 | 2445 |
| 2439 /** | 2446 /** |
| 2440 * Maps a field to a map of receiver to value. | 2447 * Maps a field to a map of receiver to value. |
| 2441 */ | 2448 */ |
| 2442 final Map<Element, Map<HInstruction, HInstruction>> fieldValues = | 2449 final Map<Element, Map<HInstruction, HInstruction>> fieldValues = |
| 2443 <Element, Map<HInstruction, HInstruction>>{}; | 2450 <Element, Map<HInstruction, HInstruction>>{}; |
| 2444 | 2451 |
| 2445 /** | 2452 /** |
| 2446 * Maps a receiver to a map of keys to value. | 2453 * Maps a receiver to a map of keys to value. |
| 2447 */ | 2454 */ |
| 2448 final Map<HInstruction, Map<HInstruction, HInstruction>> keyedValues = | 2455 final Map<HInstruction, Map<HInstruction, HInstruction>> keyedValues = |
| 2449 <HInstruction, Map<HInstruction, HInstruction>>{}; | 2456 <HInstruction, Map<HInstruction, HInstruction>>{}; |
| 2450 | 2457 |
| 2451 /** | 2458 /** |
| 2452 * Set of objects that we know don't escape the current function. | 2459 * Set of objects that we know don't escape the current function. |
| 2453 */ | 2460 */ |
| 2454 final Setlet<HInstruction> nonEscapingReceivers = new Setlet<HInstruction>(); | 2461 final Setlet<HInstruction> nonEscapingReceivers = new Setlet<HInstruction>(); |
| 2455 | 2462 |
| 2456 MemorySet(this.compiler); | 2463 MemorySet(this.closedWorld); |
| 2457 | |
| 2458 JavaScriptBackend get backend => compiler.backend; | |
| 2459 | 2464 |
| 2460 /** | 2465 /** |
| 2461 * Returns whether [first] and [second] always alias to the same object. | 2466 * Returns whether [first] and [second] always alias to the same object. |
| 2462 */ | 2467 */ |
| 2463 bool mustAlias(HInstruction first, HInstruction second) { | 2468 bool mustAlias(HInstruction first, HInstruction second) { |
| 2464 return first == second; | 2469 return first == second; |
| 2465 } | 2470 } |
| 2466 | 2471 |
| 2467 /** | 2472 /** |
| 2468 * Returns whether [first] and [second] may alias to the same object. | 2473 * Returns whether [first] and [second] may alias to the same object. |
| 2469 */ | 2474 */ |
| 2470 bool mayAlias(HInstruction first, HInstruction second) { | 2475 bool mayAlias(HInstruction first, HInstruction second) { |
| 2471 if (mustAlias(first, second)) return true; | 2476 if (mustAlias(first, second)) return true; |
| 2472 if (isConcrete(first) && isConcrete(second)) return false; | 2477 if (isConcrete(first) && isConcrete(second)) return false; |
| 2473 if (nonEscapingReceivers.contains(first)) return false; | 2478 if (nonEscapingReceivers.contains(first)) return false; |
| 2474 if (nonEscapingReceivers.contains(second)) return false; | 2479 if (nonEscapingReceivers.contains(second)) return false; |
| 2475 // Typed arrays of different types might have a shared buffer. | 2480 // Typed arrays of different types might have a shared buffer. |
| 2476 if (couldBeTypedArray(first) && couldBeTypedArray(second)) return true; | 2481 if (couldBeTypedArray(first) && couldBeTypedArray(second)) return true; |
| 2477 return !first.instructionType | 2482 return !first.instructionType |
| 2478 .isDisjoint(second.instructionType, compiler.closedWorld); | 2483 .isDisjoint(second.instructionType, closedWorld); |
| 2479 } | 2484 } |
| 2480 | 2485 |
| 2481 bool isFinal(Element element) { | 2486 bool isFinal(Element element) { |
| 2482 return compiler.closedWorld.fieldNeverChanges(element); | 2487 return closedWorld.fieldNeverChanges(element); |
| 2483 } | 2488 } |
| 2484 | 2489 |
| 2485 bool isConcrete(HInstruction instruction) { | 2490 bool isConcrete(HInstruction instruction) { |
| 2486 return instruction is HCreate || | 2491 return instruction is HCreate || |
| 2487 instruction is HConstant || | 2492 instruction is HConstant || |
| 2488 instruction is HLiteralList; | 2493 instruction is HLiteralList; |
| 2489 } | 2494 } |
| 2490 | 2495 |
| 2491 bool couldBeTypedArray(HInstruction receiver) { | 2496 bool couldBeTypedArray(HInstruction receiver) { |
| 2492 JavaScriptBackend backend = compiler.backend; | 2497 return closedWorld.commonMasks.couldBeTypedArray(receiver.instructionType); |
| 2493 return backend.couldBeTypedArray(receiver.instructionType); | |
| 2494 } | 2498 } |
| 2495 | 2499 |
| 2496 /** | 2500 /** |
| 2497 * Returns whether [receiver] escapes the current function. | 2501 * Returns whether [receiver] escapes the current function. |
| 2498 */ | 2502 */ |
| 2499 bool escapes(HInstruction receiver) { | 2503 bool escapes(HInstruction receiver) { |
| 2500 assert(receiver == null || receiver == receiver.nonCheck()); | 2504 assert(receiver == null || receiver == receiver.nonCheck()); |
| 2501 return !nonEscapingReceivers.contains(receiver); | 2505 return !nonEscapingReceivers.contains(receiver); |
| 2502 } | 2506 } |
| 2503 | 2507 |
| 2504 void registerAllocation(HInstruction instruction) { | 2508 void registerAllocation(HInstruction instruction) { |
| 2505 assert(instruction == instruction.nonCheck()); | 2509 assert(instruction == instruction.nonCheck()); |
| 2506 nonEscapingReceivers.add(instruction); | 2510 nonEscapingReceivers.add(instruction); |
| 2507 } | 2511 } |
| 2508 | 2512 |
| 2509 /** | 2513 /** |
| 2510 * Sets `receiver.element` to contain [value]. Kills all potential places that | 2514 * Sets `receiver.element` to contain [value]. Kills all potential places that |
| 2511 * may be affected by this update. | 2515 * may be affected by this update. |
| 2512 */ | 2516 */ |
| 2513 void registerFieldValueUpdate( | 2517 void registerFieldValueUpdate( |
| 2514 MemberElement element, HInstruction receiver, HInstruction value) { | 2518 MemberElement element, HInstruction receiver, HInstruction value) { |
| 2515 assert(receiver == null || receiver == receiver.nonCheck()); | 2519 assert(receiver == null || receiver == receiver.nonCheck()); |
| 2516 if (backend.isNative(element)) { | 2520 if (closedWorld.backendClasses.isNative(element)) { |
| 2517 return; // TODO(14955): Remove this restriction? | 2521 return; // TODO(14955): Remove this restriction? |
| 2518 } | 2522 } |
| 2519 // [value] is being set in some place in memory, we remove it from | 2523 // [value] is being set in some place in memory, we remove it from |
| 2520 // the non-escaping set. | 2524 // the non-escaping set. |
| 2521 nonEscapingReceivers.remove(value.nonCheck()); | 2525 nonEscapingReceivers.remove(value.nonCheck()); |
| 2522 Map<HInstruction, HInstruction> map = | 2526 Map<HInstruction, HInstruction> map = |
| 2523 fieldValues.putIfAbsent(element, () => <HInstruction, HInstruction>{}); | 2527 fieldValues.putIfAbsent(element, () => <HInstruction, HInstruction>{}); |
| 2524 map.forEach((key, value) { | 2528 map.forEach((key, value) { |
| 2525 if (mayAlias(receiver, key)) map[key] = null; | 2529 if (mayAlias(receiver, key)) map[key] = null; |
| 2526 }); | 2530 }); |
| 2527 map[receiver] = value; | 2531 map[receiver] = value; |
| 2528 } | 2532 } |
| 2529 | 2533 |
| 2530 /** | 2534 /** |
| 2531 * Registers that `receiver.element` is now [value]. | 2535 * Registers that `receiver.element` is now [value]. |
| 2532 */ | 2536 */ |
| 2533 void registerFieldValue( | 2537 void registerFieldValue( |
| 2534 MemberElement element, HInstruction receiver, HInstruction value) { | 2538 MemberElement element, HInstruction receiver, HInstruction value) { |
| 2535 assert(receiver == null || receiver == receiver.nonCheck()); | 2539 assert(receiver == null || receiver == receiver.nonCheck()); |
| 2536 if (backend.isNative(element)) { | 2540 if (closedWorld.backendClasses.isNative(element)) { |
| 2537 return; // TODO(14955): Remove this restriction? | 2541 return; // TODO(14955): Remove this restriction? |
| 2538 } | 2542 } |
| 2539 Map<HInstruction, HInstruction> map = | 2543 Map<HInstruction, HInstruction> map = |
| 2540 fieldValues.putIfAbsent(element, () => <HInstruction, HInstruction>{}); | 2544 fieldValues.putIfAbsent(element, () => <HInstruction, HInstruction>{}); |
| 2541 map[receiver] = value; | 2545 map[receiver] = value; |
| 2542 } | 2546 } |
| 2543 | 2547 |
| 2544 /** | 2548 /** |
| 2545 * Returns the value stored in `receiver.element`. Returns `null` if we don't | 2549 * Returns the value stored in `receiver.element`. Returns `null` if we don't |
| 2546 * know. | 2550 * know. |
| (...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2636 | 2640 |
| 2637 /** | 2641 /** |
| 2638 * Returns null if either [first] or [second] is null. Otherwise | 2642 * Returns null if either [first] or [second] is null. Otherwise |
| 2639 * returns [first] if [first] and [second] are equal. Otherwise | 2643 * returns [first] if [first] and [second] are equal. Otherwise |
| 2640 * creates or re-uses a phi in [block] that holds [first] and [second]. | 2644 * creates or re-uses a phi in [block] that holds [first] and [second]. |
| 2641 */ | 2645 */ |
| 2642 HInstruction findCommonInstruction(HInstruction first, HInstruction second, | 2646 HInstruction findCommonInstruction(HInstruction first, HInstruction second, |
| 2643 HBasicBlock block, int predecessorIndex) { | 2647 HBasicBlock block, int predecessorIndex) { |
| 2644 if (first == null || second == null) return null; | 2648 if (first == null || second == null) return null; |
| 2645 if (first == second) return first; | 2649 if (first == second) return first; |
| 2646 TypeMask phiType = second.instructionType | 2650 TypeMask phiType = |
| 2647 .union(first.instructionType, compiler.closedWorld); | 2651 second.instructionType.union(first.instructionType, closedWorld); |
| 2648 if (first is HPhi && first.block == block) { | 2652 if (first is HPhi && first.block == block) { |
| 2649 HPhi phi = first; | 2653 HPhi phi = first; |
| 2650 phi.addInput(second); | 2654 phi.addInput(second); |
| 2651 phi.instructionType = phiType; | 2655 phi.instructionType = phiType; |
| 2652 return phi; | 2656 return phi; |
| 2653 } else { | 2657 } else { |
| 2654 HPhi phi = new HPhi.noInputs(null, phiType); | 2658 HPhi phi = new HPhi.noInputs(null, phiType); |
| 2655 block.addPhi(phi); | 2659 block.addPhi(phi); |
| 2656 // Previous predecessors had the same input. A phi must have | 2660 // Previous predecessors had the same input. A phi must have |
| 2657 // the same number of inputs as its block has predecessors. | 2661 // the same number of inputs as its block has predecessors. |
| 2658 for (int i = 0; i < predecessorIndex; i++) { | 2662 for (int i = 0; i < predecessorIndex; i++) { |
| 2659 phi.addInput(first); | 2663 phi.addInput(first); |
| 2660 } | 2664 } |
| 2661 phi.addInput(second); | 2665 phi.addInput(second); |
| 2662 return phi; | 2666 return phi; |
| 2663 } | 2667 } |
| 2664 } | 2668 } |
| 2665 | 2669 |
| 2666 /** | 2670 /** |
| 2667 * Returns the intersection between [this] and [other]. | 2671 * Returns the intersection between [this] and [other]. |
| 2668 */ | 2672 */ |
| 2669 MemorySet intersectionFor( | 2673 MemorySet intersectionFor( |
| 2670 MemorySet other, HBasicBlock block, int predecessorIndex) { | 2674 MemorySet other, HBasicBlock block, int predecessorIndex) { |
| 2671 MemorySet result = new MemorySet(compiler); | 2675 MemorySet result = new MemorySet(closedWorld); |
| 2672 if (other == null) { | 2676 if (other == null) { |
| 2673 // This is the first visit to a loop header ([other] is `null` because we | 2677 // This is the first visit to a loop header ([other] is `null` because we |
| 2674 // have not visited the back edge). Copy the nonEscapingReceivers that are | 2678 // have not visited the back edge). Copy the nonEscapingReceivers that are |
| 2675 // guaranteed to survive the loop because they are not escaped before | 2679 // guaranteed to survive the loop because they are not escaped before |
| 2676 // method exit. | 2680 // method exit. |
| 2677 // TODO(sra): We should do a proper dataflow to find the maximal | 2681 // TODO(sra): We should do a proper dataflow to find the maximal |
| 2678 // nonEscapingReceivers (a variant of Available-Expressions), which must | 2682 // nonEscapingReceivers (a variant of Available-Expressions), which must |
| 2679 // converge before we edit the program in [findCommonInstruction]. | 2683 // converge before we edit the program in [findCommonInstruction]. |
| 2680 for (HInstruction instruction in nonEscapingReceivers) { | 2684 for (HInstruction instruction in nonEscapingReceivers) { |
| 2681 bool isNonEscapingUse(HInstruction use) { | 2685 bool isNonEscapingUse(HInstruction use) { |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2726 result.nonEscapingReceivers.add(receiver); | 2730 result.nonEscapingReceivers.add(receiver); |
| 2727 } | 2731 } |
| 2728 }); | 2732 }); |
| 2729 return result; | 2733 return result; |
| 2730 } | 2734 } |
| 2731 | 2735 |
| 2732 /** | 2736 /** |
| 2733 * Returns a copy of [this]. | 2737 * Returns a copy of [this]. |
| 2734 */ | 2738 */ |
| 2735 MemorySet clone() { | 2739 MemorySet clone() { |
| 2736 MemorySet result = new MemorySet(compiler); | 2740 MemorySet result = new MemorySet(closedWorld); |
| 2737 | 2741 |
| 2738 fieldValues.forEach((element, values) { | 2742 fieldValues.forEach((element, values) { |
| 2739 result.fieldValues[element] = | 2743 result.fieldValues[element] = |
| 2740 new Map<HInstruction, HInstruction>.from(values); | 2744 new Map<HInstruction, HInstruction>.from(values); |
| 2741 }); | 2745 }); |
| 2742 | 2746 |
| 2743 keyedValues.forEach((receiver, values) { | 2747 keyedValues.forEach((receiver, values) { |
| 2744 result.keyedValues[receiver] = | 2748 result.keyedValues[receiver] = |
| 2745 new Map<HInstruction, HInstruction>.from(values); | 2749 new Map<HInstruction, HInstruction>.from(values); |
| 2746 }); | 2750 }); |
| 2747 | 2751 |
| 2748 result.nonEscapingReceivers.addAll(nonEscapingReceivers); | 2752 result.nonEscapingReceivers.addAll(nonEscapingReceivers); |
| 2749 return result; | 2753 return result; |
| 2750 } | 2754 } |
| 2751 } | 2755 } |
| OLD | NEW |