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