Remove String#valueOf for uninstantiated types.
Bug: 118912005
Change-Id: I58a0bb0e25426058af6c5c23dbcc4854e66a735f
diff --git a/src/main/java/com/android/tools/r8/ir/code/Value.java b/src/main/java/com/android/tools/r8/ir/code/Value.java
index 23d3621..7c9fda8 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Value.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Value.java
@@ -13,7 +13,6 @@
import com.android.tools.r8.ir.regalloc.LiveIntervals;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.position.MethodPosition;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.LongInterval;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.StringDiagnostic;
@@ -446,7 +445,7 @@
return !users.isEmpty() || !phiUsers.isEmpty() || numberOfDebugUsers() > 0;
}
- public boolean isAlwaysNull(AppView<AppInfoWithLiveness> appView) {
+ public boolean isAlwaysNull(AppView<? extends AppInfo> appView) {
if (hasLocalInfo()) {
// Not always null as the value can be changed via the debugger.
return false;
@@ -454,8 +453,11 @@
if (typeLattice.isDefinitelyNull()) {
return true;
}
- if (typeLattice.isClassType()) {
- return typeLattice.asClassTypeLatticeElement().getClassType().isAlwaysNull(appView);
+ if (typeLattice.isClassType() && appView.appInfo().hasLiveness()) {
+ return typeLattice
+ .asClassTypeLatticeElement()
+ .getClassType()
+ .isAlwaysNull(appView.withLiveness());
}
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java
index 4be6602..aca25ac 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java
@@ -400,7 +400,7 @@
continue;
}
TypeLatticeElement inType = in.getTypeLattice();
- if (inType.isNullType()) {
+ if (in.isAlwaysNull(appView)) {
Value nullStringValue =
code.createValue(
TypeLatticeElement.stringClassType(appView, definitelyNotNull()),
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringValueOfTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringValueOfTest.java
index 1e9b3f4..512c54d 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringValueOfTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringValueOfTest.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.ForceInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NeverPropagateValue;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -23,7 +24,6 @@
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import com.android.tools.r8.utils.codeinspector.InstructionSubject.JumboStringMode;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
-import com.google.common.collect.ImmutableList;
import com.google.common.collect.Streams;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -46,6 +46,15 @@
String getter();
}
+ static class Uninitialized {
+ }
+
+ @NeverInline
+ @NeverPropagateValue
+ static String consumeUninitialized(Uninitialized arg) {
+ return String.valueOf(arg);
+ }
+
@NeverInline
static String hideNPE(String s) {
return String.valueOf(s);
@@ -90,6 +99,7 @@
} catch (NullPointerException npe) {
fail("Not expected: " + npe);
}
+ System.out.println(consumeUninitialized(null));
// No matter what we pass, that function will return null.
// But, we're not sure about it, hence not optimizing String#valueOf.
@@ -113,6 +123,7 @@
"null",
"null",
"null",
+ "null",
"null"
);
private static final Class<?> MAIN = StringValueOfTestMain.class;
@@ -173,25 +184,29 @@
private void test(
TestRunResult result,
- int expectedStringValueOfCount,
+ int expectedStringValueOfCountInMain,
int expectedNullCount,
- int expectedNullStringCount)
+ int expectedNullStringCountInMain,
+ int expectedStringValueOfCountInConsumer,
+ int expectedNullStringCountInConsumer)
throws Exception {
CodeInspector codeInspector = result.inspector();
ClassSubject mainClass = codeInspector.clazz(MAIN);
MethodSubject mainMethod = mainClass.mainMethod();
assertThat(mainMethod, isPresent());
- long count = countStringValueOf(mainMethod);
- assertEquals(expectedStringValueOfCount, count);
- count = countConstNullNumber(mainMethod);
- assertEquals(expectedNullCount, count);
- count = countNullStringNumber(mainMethod);
- assertEquals(expectedNullStringCount, count);
+ assertEquals(expectedStringValueOfCountInMain, countStringValueOf(mainMethod));
+ assertEquals(expectedNullCount, countConstNullNumber(mainMethod));
+ assertEquals(expectedNullStringCountInMain, countNullStringNumber(mainMethod));
- MethodSubject hideNPE = mainClass.method(STRING_TYPE, "hideNPE", ImmutableList.of(STRING_TYPE));
+ MethodSubject hideNPE = mainClass.uniqueMethodWithName("hideNPE");
assertThat(hideNPE, isPresent());
// Due to the nullable argument, valueOf should remain.
assertEquals(1, countStringValueOf(hideNPE));
+
+ MethodSubject uninit = mainClass.uniqueMethodWithName("consumeUninitialized");
+ assertThat(uninit, isPresent());
+ assertEquals(expectedStringValueOfCountInConsumer, countStringValueOf(uninit));
+ assertEquals(expectedNullStringCountInConsumer, countNullStringNumber(uninit));
}
@Test
@@ -205,7 +220,7 @@
.setMinApi(parameters.getRuntime())
.run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutput(JAVA_OUTPUT);
- test(result, 7, 1, 0);
+ test(result, 7, 1, 0, 1, 0);
result =
testForD8()
@@ -214,7 +229,7 @@
.setMinApi(parameters.getRuntime())
.run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutput(JAVA_OUTPUT);
- test(result, 5, 1, 1);
+ test(result, 5, 1, 1, 1, 0);
}
@Test
@@ -223,6 +238,7 @@
testForR8(parameters.getBackend())
.addProgramClassesAndInnerClasses(MAIN)
.enableInliningAnnotations()
+ .enableMemberValuePropagationAnnotations()
.addKeepMainRule(MAIN)
.setMinApi(parameters.getRuntime())
.noMinification()
@@ -232,6 +248,6 @@
// Due to the different behavior regarding constant canonicalization.
int expectedNullCount = parameters.getBackend() == Backend.CF ? 2 : 1;
int expectedNullStringCount = parameters.getBackend() == Backend.CF ? 2 : 1;
- test(result, 3, expectedNullCount, expectedNullStringCount);
+ test(result, 3, expectedNullCount, expectedNullStringCount, 0, 1);
}
}