Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(268)

Side by Side Diff: pkg/compiler/lib/src/ssa/optimize.dart

Issue 2569733002: Even less reliance on Compiler.closedWorld (Closed)
Patch Set: Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698