Fix inadequate call site propagation in presence of lambda targets
Bug: 153147271, 150269949
Change-Id: Ief75107ee14a90d4da32c4c5d568bfdf0997f1e4
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
index dc319a8..951cb63 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
@@ -3,19 +3,20 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.code;
+import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
+
import com.android.tools.r8.cf.LoadStoreHelper;
import com.android.tools.r8.cf.TypeVerificationHelper;
-import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.LookupResult.LookupResultSuccess;
-import com.android.tools.r8.graph.ResolutionResult;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.AbstractFieldSet;
import com.android.tools.r8.ir.analysis.modeling.LibraryMethodReadSetModeling;
+import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.analysis.value.UnknownValue;
@@ -24,7 +25,8 @@
import com.android.tools.r8.ir.optimize.Inliner.Reason;
import com.android.tools.r8.ir.optimize.inliner.WhyAreYouNotInliningReporter;
import com.android.tools.r8.ir.regalloc.RegisterAllocator;
-import com.google.common.collect.ImmutableList;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import java.util.BitSet;
import java.util.Collection;
@@ -77,14 +79,21 @@
// TODO(b/140204899): Refactor lookup methods to be defined in a single place.
public Collection<DexEncodedMethod> lookupTargets(
- AppView<? extends AppInfoWithSubtyping> appView, DexType invocationContext) {
- // Leverage exact receiver type if available.
- DexEncodedMethod singleTarget = lookupSingleTarget(appView, invocationContext);
- if (singleTarget != null) {
- return ImmutableList.of(singleTarget);
+ AppView<AppInfoWithLiveness> appView, DexType invocationContext) {
+ if (!isInvokeMethodWithDynamicDispatch()) {
+ DexEncodedMethod singleTarget = lookupSingleTarget(appView, invocationContext);
+ return singleTarget != null ? ImmutableSet.of(singleTarget) : null;
}
- if (!isInvokeMethodWithDynamicDispatch() || !appView.appInfo().hasLiveness()) {
- return null;
+ DexProgramClass refinedReceiverUpperBound =
+ asProgramClassOrNull(
+ appView.definitionFor(
+ TypeAnalysis.getRefinedReceiverType(appView, asInvokeMethodWithReceiver())));
+ DexProgramClass refinedReceiverLowerBound = null;
+ ClassTypeElement refinedReceiverLowerBoundType =
+ asInvokeMethodWithReceiver().getReceiver().getDynamicLowerBoundType(appView);
+ if (refinedReceiverLowerBoundType != null) {
+ refinedReceiverLowerBound =
+ asProgramClassOrNull(appView.definitionFor(refinedReceiverLowerBoundType.getClassType()));
}
LookupResultSuccess lookupResult =
appView
@@ -92,48 +101,18 @@
.resolveMethod(method.holder, method)
.lookupVirtualDispatchTargets(
appView.definitionForProgramType(invocationContext),
- appView.withLiveness().appInfo())
+ appView.withLiveness().appInfo(),
+ refinedReceiverUpperBound,
+ refinedReceiverLowerBound)
.asLookupResultSuccess();
- if (lookupResult == null || lookupResult.isEmpty()) {
+ if (lookupResult == null) {
return null;
}
- if (lookupResult.hasLambdaTargets()) {
- // TODO(b/150277553): Support lambda targets.
- return null;
- }
- assert lookupResult.hasMethodTargets();
- DexType staticReceiverType = getInvokedMethod().holder;
- DexType refinedReceiverType =
- TypeAnalysis.getRefinedReceiverType(
- appView.withLiveness(), this.asInvokeMethodWithReceiver());
- // TODO(b/140204899): Instead of reprocessing here, pass refined receiver to lookup.
- // Leverage refined receiver type if available.
- if (refinedReceiverType != staticReceiverType) {
- ResolutionResult refinedResolution =
- appView.appInfo().resolveMethod(refinedReceiverType, method);
- if (refinedResolution.isSingleResolution()) {
- DexEncodedMethod refinedTarget = refinedResolution.getSingleTarget();
- Set<DexEncodedMethod> result = Sets.newIdentityHashSet();
- lookupResult.forEach(
- methodTarget -> {
- DexEncodedMethod target = methodTarget.getMethod();
- if (target == refinedTarget
- || appView.isSubtype(target.holder(), refinedReceiverType).isPossiblyTrue()) {
- result.add(target);
- }
- },
- lambdaTarget -> {
- throw new Unreachable();
- });
- return result;
- }
- // If resolution at the refined type fails, conservatively return the full set of targets.
- }
Set<DexEncodedMethod> result = Sets.newIdentityHashSet();
lookupResult.forEach(
methodTarget -> result.add(methodTarget.getMethod()),
lambda -> {
- assert false;
+ // TODO(b/150277553): Support lambda targets.
});
return result;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java b/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
index f7b8688..41b1124 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
@@ -6,7 +6,6 @@
import com.android.tools.r8.cf.code.CfInvoke;
import com.android.tools.r8.code.InvokePolymorphicRange;
import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
@@ -22,6 +21,7 @@
import com.android.tools.r8.ir.optimize.Inliner.Reason;
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.ir.optimize.inliner.WhyAreYouNotInliningReporter;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
import java.util.Collection;
import java.util.List;
@@ -126,7 +126,7 @@
@Override
public Collection<DexEncodedMethod> lookupTargets(
- AppView<? extends AppInfoWithSubtyping> appView, DexType invocationContext) {
+ AppView<AppInfoWithLiveness> appView, DexType invocationContext) {
// TODO(herhut): Implement lookup target for invokePolymorphic.
return null;
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/CallSiteOptimizationWithLambdaTargetTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/CallSiteOptimizationWithLambdaTargetTest.java
new file mode 100644
index 0000000..c04f9b6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/CallSiteOptimizationWithLambdaTargetTest.java
@@ -0,0 +1,69 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.callsites;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class CallSiteOptimizationWithLambdaTargetTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public CallSiteOptimizationWithLambdaTargetTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(CallSiteOptimizationWithLambdaTargetTest.class)
+ .addKeepMainRule(TestClass.class)
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("true", "false");
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ new A().m(new Object());
+ get().m(null);
+ }
+
+ static I get() {
+ return System.currentTimeMillis() >= 0 ? new A() : System.out::println;
+ }
+ }
+
+ interface I {
+
+ void m(Object o);
+ }
+
+ @NeverClassInline
+ static class A implements I {
+
+ @NeverInline
+ @Override
+ public void m(Object o) {
+ System.out.println(o != null);
+ }
+ }
+}