Account for method-return-value propagation in -if rule evaluation
Change-Id: I039d58ccce261cd3688f113573704375fe7b4adb
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index bfb248a..0687442 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -1131,6 +1131,11 @@
}
@Override
+ public boolean returnValueHasBeenPropagated() {
+ return false;
+ }
+
+ @Override
public UpdatableMethodOptimizationInfo mutableCopy() {
return new MethodOptimizationInfoImpl();
}
@@ -1189,6 +1194,7 @@
// Note that this bit set takes into account the receiver for instance methods.
private BitSet nonNullParamOnNormalExits = null;
private boolean reachabilitySensitive = false;
+ private boolean returnValueHasBeenPropagated = false;
private MethodOptimizationInfoImpl() {
// Intentionally left empty, just use the default values.
@@ -1461,6 +1467,16 @@
}
@Override
+ public void markAsPropagated() {
+ returnValueHasBeenPropagated = true;
+ }
+
+ @Override
+ public boolean returnValueHasBeenPropagated() {
+ return returnValueHasBeenPropagated;
+ }
+
+ @Override
public UpdatableMethodOptimizationInfo mutableCopy() {
assert this != DefaultMethodOptimizationInfoImpl.DEFAULT_INSTANCE;
return new MethodOptimizationInfoImpl(this);
diff --git a/src/main/java/com/android/tools/r8/graph/MethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/graph/MethodOptimizationInfo.java
index 977160c..96bbf6d 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodOptimizationInfo.java
@@ -67,5 +67,7 @@
boolean mayHaveSideEffects();
+ boolean returnValueHasBeenPropagated();
+
UpdatableMethodOptimizationInfo mutableCopy();
}
diff --git a/src/main/java/com/android/tools/r8/graph/UpdatableMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/graph/UpdatableMethodOptimizationInfo.java
index 78b10cd..1aa2129 100644
--- a/src/main/java/com/android/tools/r8/graph/UpdatableMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/UpdatableMethodOptimizationInfo.java
@@ -22,6 +22,8 @@
void markReturnsObjectOfType(TypeLatticeElement type);
+ void markAsPropagated();
+
void markMayNotHaveSideEffects();
void markNeverReturnsNull();
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedback.java b/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedback.java
index 6af7bef..488c06b 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedback.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedback.java
@@ -36,6 +36,8 @@
void markProcessed(DexEncodedMethod method, ConstraintWithTarget state);
+ void markAsPropagated(DexEncodedMethod method);
+
void markUseIdentifierNameString(DexEncodedMethod method);
void markCheckNullReceiverBeforeAnySideEffect(DexEncodedMethod method, boolean mark);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedbackDelayed.java b/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedbackDelayed.java
index ac143e3..874977b 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedbackDelayed.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedbackDelayed.java
@@ -80,6 +80,11 @@
}
@Override
+ public synchronized void markAsPropagated(DexEncodedMethod method) {
+ getOptimizationInfoForUpdating(method).markAsPropagated();
+ }
+
+ @Override
public synchronized void markProcessed(DexEncodedMethod method, ConstraintWithTarget state) {
processed.put(method, state);
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedbackIgnore.java b/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedbackIgnore.java
index 9254711..0946ca3 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedbackIgnore.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedbackIgnore.java
@@ -43,6 +43,9 @@
public void methodNeverReturnsNormally(DexEncodedMethod method) {}
@Override
+ public void markAsPropagated(DexEncodedMethod method) {}
+
+ @Override
public void markProcessed(DexEncodedMethod method, ConstraintWithTarget state) {}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedbackSimple.java b/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedbackSimple.java
index ab00d6e..313148f 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedbackSimple.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedbackSimple.java
@@ -59,6 +59,11 @@
}
@Override
+ public void markAsPropagated(DexEncodedMethod method) {
+ // Ignored.
+ }
+
+ @Override
public void markProcessed(DexEncodedMethod method, ConstraintWithTarget state) {
// Just as processed, don't provide any inlining constraints.
method.markProcessed(ConstraintWithTarget.NEVER);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
index 00a1acc..d229fa4 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
@@ -281,6 +281,7 @@
} else {
iterator.add(replacement);
}
+ target.getMutableOptimizationInfo().markAsPropagated();
return;
}
if (target.getOptimizationInfo().neverReturnsNull()
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
index 648d8d1..819c4ff 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -377,8 +377,9 @@
}
private boolean isEffectivelyLive(DexProgramClass clazz) {
- // A type is effectively live if it is truly live, or if the value of one of its fields has
- // been inlined by the member value propagation.
+ // A type is effectively live if (1) it is truly live, (2) the value of one of its fields has
+ // been inlined by the member value propagation, or (3) the return value of one of its methods
+ // has been forwarded by the member value propagation.
if (liveTypes.contains(clazz.type)) {
return true;
}
@@ -387,6 +388,11 @@
return true;
}
}
+ for (DexEncodedMethod method : clazz.methods()) {
+ if (method.getOptimizationInfo().returnValueHasBeenPropagated()) {
+ return true;
+ }
+ }
return false;
}
@@ -434,7 +440,9 @@
filteredMembers,
targetClass.methods(
m ->
- (liveMethods.contains(m) || targetedMethods.contains(m))
+ (liveMethods.contains(m)
+ || targetedMethods.contains(m)
+ || m.getOptimizationInfo().returnValueHasBeenPropagated())
&& appView.graphLense().getOriginalMethodSignature(m.method).holder
== sourceClass.type));
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/membervaluepropagation/IfWithMethodValuePropagationTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/membervaluepropagation/IfWithMethodValuePropagationTest.java
new file mode 100644
index 0000000..f795067
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/membervaluepropagation/IfWithMethodValuePropagationTest.java
@@ -0,0 +1,95 @@
+// Copyright (c) 2019, 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.shaking.ifrule.membervaluepropagation;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class IfWithMethodValuePropagationTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().build();
+ }
+
+ public IfWithMethodValuePropagationTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(TestClass.class, R.class, Layout.class)
+ .addKeepMainRule(TestClass.class)
+ .addKeepRules(
+ "-if class " + R.class.getTypeName() + " {",
+ " static int id();",
+ "}",
+ "-keep class " + Layout.class.getTypeName())
+ .addLibraryClasses(Library.class)
+ .addLibraryFiles(runtimeJar(parameters))
+ .setMinApi(parameters.getRuntime())
+ .compile()
+ .inspect(this::verifyOutput)
+ .addRunClasspathFiles(
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Library.class)
+ .addKeepAllClassesRule()
+ .setMinApi(parameters.getRuntime())
+ .compile()
+ .writeToZip())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Layout.toString()");
+ }
+
+ private void verifyOutput(CodeInspector inspector) {
+ // R.ID has been inlined.
+ assertThat(inspector.clazz(R.class), not(isPresent()));
+
+ // Layout is kept by the conditional rule.
+ assertThat(inspector.clazz(Layout.class), isPresent());
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ System.out.println(Library.getViewById(R.id()));
+ }
+ }
+
+ static class Library {
+
+ public static Object getViewById(int viewId) {
+ return new Layout();
+ }
+ }
+
+ static class R {
+
+ public static int id() {
+ return 42;
+ }
+ }
+
+ static class Layout {
+
+ @Override
+ public String toString() {
+ return "Layout.toString()";
+ }
+ }
+}