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

Unified Diff: pkg/compiler/lib/src/cps_ir/type_propagation.dart

Issue 1571433002: dart2js cps: Compute intercepted classes in optimize_interceptors. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Address comments Created 4 years, 11 months 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « pkg/compiler/lib/src/cps_ir/optimizers.dart ('k') | pkg/compiler/lib/src/js_backend/codegen/codegen.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/compiler/lib/src/cps_ir/type_propagation.dart
diff --git a/pkg/compiler/lib/src/cps_ir/type_propagation.dart b/pkg/compiler/lib/src/cps_ir/type_propagation.dart
index 0b6e3f3c2766c9e81b7a59d67522f4215010d6fb..294112fa9289524d56a0c2cdafb438ad96b14e8a 100644
--- a/pkg/compiler/lib/src/cps_ir/type_propagation.dart
+++ b/pkg/compiler/lib/src/cps_ir/type_propagation.dart
@@ -1835,6 +1835,30 @@ class TransformingVisitor extends DeepRecursiveVisitor {
return cps;
}
+ visitInterceptor(Interceptor node) {
+ // Replace the interceptor with its input if the value is not intercepted.
+ // If the input might be null, we cannot do this since the interceptor
+ // might have to return JSNull. That case is handled by visitInvokeMethod
+ // and visitInvokeMethodDirectly which can sometimes tolerate that null
+ // is used instead of JSNull.
+ Primitive input = node.input.definition;
+ if (!input.type.isNullable &&
+ typeSystem.areDisjoint(input.type, typeSystem.interceptorType)) {
+ node.replaceUsesWith(input);
+ }
+ }
+
+ visitInvokeMethodDirectly(InvokeMethodDirectly node) {
+ TypeMask receiverType = node.dartReceiver.type;
+ if (node.callingConvention == CallingConvention.Intercepted &&
+ typeSystem.areDisjoint(receiverType, typeSystem.interceptorType)) {
+ // Some direct calls take an interceptor because the target class is
+ // mixed into a native class. If it is known at the call site that the
+ // receiver is non-intercepted, get rid of the interceptor.
+ node.receiver.changeTo(node.dartReceiver);
+ }
+ }
+
visitInvokeMethod(InvokeMethod node) {
var specialized =
specializeOperatorCall(node) ??
@@ -1845,15 +1869,19 @@ class TransformingVisitor extends DeepRecursiveVisitor {
specializeClosureCall(node);
if (specialized != null) return specialized;
- node.mask =
- typeSystem.intersection(node.mask, getValue(node.dartReceiver).type);
+ TypeMask receiverType = node.dartReceiver.type;
+ node.mask = typeSystem.intersection(node.mask, receiverType);
- AbstractConstantValue receiver = getValue(node.receiver.definition);
+ bool canBeNonThrowingCallOnNull =
+ selectorsOnNull.contains(node.selector) &&
+ receiverType.isNullable;
if (node.callingConvention == CallingConvention.Intercepted &&
- node.receiver.definition.sameValue(node.arguments[0].definition)) {
- // The receiver and first argument are the same; that means we already
- // determined in visitInterceptor that we are targeting a non-interceptor.
+ !canBeNonThrowingCallOnNull &&
+ typeSystem.areDisjoint(receiverType, typeSystem.interceptorType)) {
+ // Use the Dart receiver as the JS receiver. This changes the wording of
+ // the error message when the receiver is null, but we accept this.
+ node.receiver.changeTo(node.dartReceiver);
// Check if any of the possible targets depend on the extra receiver
// argument. Mixins do this, and tear-offs always needs the extra receiver
@@ -1866,7 +1894,7 @@ class TransformingVisitor extends DeepRecursiveVisitor {
return typeSystem.methodUsesReceiverArgument(function) ||
node.selector.isGetter && !function.isGetter;
}
- if (!getAllTargets(receiver.type, node.selector).any(needsReceiver)) {
+ if (!getAllTargets(receiverType, node.selector).any(needsReceiver)) {
// Replace the extra receiver argument with a dummy value if the
// target definitely does not use it.
Constant dummy = makeConstantPrimitive(new IntConstantValue(0));
@@ -2220,7 +2248,6 @@ class TransformingVisitor extends DeepRecursiveVisitor {
CpsFragment cps = new CpsFragment(node.sourceInformation);
Interceptor interceptor =
cps.letPrim(new Interceptor(prim, node.sourceInformation));
- interceptor.interceptedClasses.addAll(backend.interceptedClasses);
Primitive testViaFlag =
cps.letPrim(new TypeTestViaFlag(interceptor, dartType));
node.replaceUsesWith(testViaFlag);
@@ -2231,163 +2258,6 @@ class TransformingVisitor extends DeepRecursiveVisitor {
return null;
}
- Primitive visitInterceptor(Interceptor node) {
- AbstractConstantValue value = getValue(node.input.definition);
- TypeMask interceptedInputs = value.type.intersection(
- typeSystem.interceptorType.nullable(), classWorld);
- bool interceptNull =
- node.interceptedClasses.contains(helpers.jsNullClass) ||
- node.interceptedClasses.contains(helpers.jsInterceptorClass);
-
- void filterInterceptedClasses() {
- if (lattice.isDefinitelyInt(value, allowNull: !interceptNull)) {
- node.interceptedClasses..clear()..add(helpers.jsIntClass);
- return;
- }
- if (lattice.isDefinitelyNum(value, allowNull: !interceptNull) &&
- jsNumberClassSuffices(node)) {
- node.interceptedClasses..clear()..add(helpers.jsNumberClass);
- return;
- }
-
- // TODO(asgerf): Avoid adding non-instantiated intercepted classes
- // in the first place. (This should also happen in later pass).
- node.interceptedClasses.retainWhere(classWorld.isInstantiated);
-
- TypeMask interceptedClassesType = new TypeMask.unionOf(
- node.interceptedClasses.map(typeSystem.getInterceptorSubtypes),
- classWorld);
- TypeMask interceptedAndCaught =
- interceptedInputs.intersection(interceptedClassesType, classWorld);
- ClassElement single = interceptedAndCaught.singleClass(classWorld);
- if (single != null) {
- node.interceptedClasses..clear()..add(backend.getRuntimeClass(single));
- return;
- }
-
- // Filter out intercepted classes that do not match the input type.
- node.interceptedClasses.retainWhere((ClassElement clazz) {
- return !typeSystem.areDisjoint(
- value.type,
- typeSystem.getInterceptorSubtypes(clazz));
- });
-
- // The interceptor root class will usually not be filtered out because all
- // intercepted values are subtypes of it. But it can be "shadowed" by a
- // more specific interceptor classes if all possible intercepted values
- // will hit one of the more specific classes.
- //
- // For example, if all classes can respond to a given call, but we know
- // the input is a string or an unknown JavaScript object, we want to
- // use a specialized interceptor:
- //
- // getInterceptor(x).get$hashCode(x);
- // ==>
- // getInterceptor$su(x).get$hashCode(x)
- //
- // In this case, the intercepted classes that were not filtered out above
- // are {UnknownJavaScriptObject, JSString, Interceptor}, but there is no
- // need to include the Interceptor class.
- //
- // We only do this optimization if the resulting interceptor call is
- // sufficiently specialized to be worth it (#classes <= 2).
- // TODO(asgerf): Reconsider when TypeTestViaFlag interceptors don't
- // intercept ALL interceptor classes.
- if (node.interceptedClasses.length > 1 &&
- node.interceptedClasses.length < 4 &&
- node.interceptedClasses.contains(helpers.jsInterceptorClass)) {
- TypeMask specificInterceptors = new TypeMask.unionOf(
- node.interceptedClasses
- .where((cl) => cl != helpers.jsInterceptorClass)
- .map(typeSystem.getInterceptorSubtypes),
- classWorld);
- if (specificInterceptors.containsMask(interceptedInputs, classWorld)) {
- // All possible inputs are caught by an Interceptor subclass (or are
- // self-interceptors), so there is no need to have the check for
- // the Interceptor root class (which is expensive).
- node.interceptedClasses.remove(helpers.jsInterceptorClass);
- }
- }
- }
-
- filterInterceptedClasses();
-
- // Remove the interceptor call if it can only return its input.
- if (node.interceptedClasses.isEmpty) {
- node.replaceUsesWith(node.input.definition);
- return null;
- }
-
- node.flags = Interceptor.ALL_FLAGS;
-
- // If there is only one caught interceptor class, determine more precisely
- // how this might resolve at runtime. Later optimizations depend on this,
- // but do not have refined type information available.
- if (node.interceptedClasses.length == 1) {
- ClassElement class_ = node.interceptedClasses.single;
- if (value.isDefinitelyNotNull) {
- node.clearFlag(Interceptor.NULL);
- } else if (interceptNull) {
- node.clearFlag(Interceptor.NULL_BYPASS);
- if (class_ == helpers.jsNullClass) {
- node.clearFlag(Interceptor.NULL_INTERCEPT_SUBCLASS);
- } else {
- node.clearFlag(Interceptor.NULL_INTERCEPT_EXACT);
- }
- } else {
- node.clearFlag(Interceptor.NULL_INTERCEPT);
- }
- if (typeSystem.isDefinitelyIntercepted(value.type, allowNull: true)) {
- node.clearFlag(Interceptor.SELF_INTERCEPT);
- }
- TypeMask interceptedInputsNonNullable = interceptedInputs.nonNullable();
- TypeMask interceptedType = typeSystem.getInterceptorSubtypes(class_);
- if (interceptedType.containsMask(interceptedInputsNonNullable,
- classWorld)) {
- node.clearFlag(Interceptor.NON_NULL_BYPASS);
- }
- if (class_ == helpers.jsNumberClass) {
- // See [jsNumberClassSuffices]. We know that JSInt and JSDouble are
- // not intercepted classes so JSNumber is sufficient.
- node.clearFlag(Interceptor.NON_NULL_INTERCEPT_SUBCLASS);
- } else if (class_ == helpers.jsArrayClass) {
- // JSArray has compile-time subclasses like JSFixedArray, but should
- // still be considered "exact" if the input is any subclass of JSArray.
- if (typeSystem.isDefinitelyArray(interceptedInputsNonNullable)) {
- node.clearFlag(Interceptor.NON_NULL_INTERCEPT_SUBCLASS);
- }
- } else {
- TypeMask exactInterceptedType = typeSystem.nonNullExact(class_);
- if (exactInterceptedType.containsMask(interceptedInputsNonNullable,
- classWorld)) {
- node.clearFlag(Interceptor.NON_NULL_INTERCEPT_SUBCLASS);
- }
- }
- }
- return null;
- }
-
- bool jsNumberClassSuffices(Interceptor node) {
- // No methods on JSNumber call 'down' to methods on JSInt or JSDouble. If
- // all uses of the interceptor are for methods is defined only on JSNumber
- // then JSNumber will suffice in place of choosing between JSInt or
- // JSDouble.
- for (Reference ref = node.firstRef; ref != null; ref = ref.next) {
- if (ref.parent is InvokeMethod) {
- InvokeMethod invoke = ref.parent;
- if (invoke.receiver != ref) return false;
- var interceptedClasses =
- functionCompiler.glue.getInterceptedClassesOn(invoke.selector);
- if (interceptedClasses.contains(helpers.jsDoubleClass)) return false;
- if (interceptedClasses.contains(helpers.jsIntClass)) return false;
- continue;
- }
- // Other uses need full distinction.
- return false;
- }
- return true;
- }
-
visitBoundsCheck(BoundsCheck node) {
// Eliminate bounds checks using constant folding.
// The [BoundsChecker] pass does not try to eliminate checks that could be
« no previous file with comments | « pkg/compiler/lib/src/cps_ir/optimizers.dart ('k') | pkg/compiler/lib/src/js_backend/codegen/codegen.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698