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 |