Merge "Run string optimizations after class inlining."
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 3d1f080..18e67bb 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -924,7 +924,6 @@
}
if (!isDebugMode) {
- // TODO(jsjeon): Consider merging these into one single optimize().
stringOptimizer.computeTrivialOperationsOnConstString(code, appInfo.dexItemFactory);
// Reflection optimization 2. get*Name() with const-class -> const-string
if (options.enableNameReflectionOptimization) {
@@ -1008,7 +1007,12 @@
// lambda, it does not get collected by merger.
assert options.enableInlining && inliner != null;
classInliner.processMethodCode(
- appInfo.withLiveness(), codeRewriter, method, code, isProcessedConcurrently,
+ appInfo.withLiveness(),
+ codeRewriter,
+ stringOptimizer,
+ method,
+ code,
+ isProcessedConcurrently,
methodsToInline -> inliner.performForcedInlining(method, code, methodsToInline),
Suppliers.memoize(() -> inliner.createDefaultOracle(
method, code,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
index 30cd8b7..b495507 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.ir.optimize.CodeRewriter;
import com.android.tools.r8.ir.optimize.Inliner.InliningInfo;
import com.android.tools.r8.ir.optimize.InliningOracle;
+import com.android.tools.r8.ir.optimize.string.StringOptimizer;
import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
import com.google.common.collect.Streams;
import java.util.Iterator;
@@ -121,6 +122,7 @@
public final void processMethodCode(
AppInfoWithLiveness appInfo,
CodeRewriter codeRewriter,
+ StringOptimizer stringOptimizer,
DexEncodedMethod method,
IRCode code,
Predicate<DexEncodedMethod> isProcessedConcurrently,
@@ -187,6 +189,13 @@
codeRewriter.removeTrivialCheckCastAndInstanceOfInstructions(code, true);
// If a method was inlined we may be able to prune additional branches.
codeRewriter.simplifyIf(code);
+ // If a method was inlined we may see more trivial computation/conversion of String.
+ boolean isDebugMode =
+ code.options.debug || method.getOptimizationInfo().isReachabilitySensitive();
+ if (!isDebugMode) {
+ stringOptimizer.computeTrivialOperationsOnConstString(code, appInfo.dexItemFactory);
+ stringOptimizer.removeTrivialConversions(code, appInfo);
+ }
}
}
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 09d4cc4..ed1fa75 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
@@ -30,6 +30,17 @@
class StringValueOfTestMain {
+ static class Notification {
+ String id;
+ Notification(String id) {
+ this.id = id;
+ }
+
+ String getId() {
+ return id;
+ }
+ }
+
interface Itf {
String getter();
}
@@ -53,6 +64,11 @@
}
}
+ @NeverInline
+ static String eventuallyReturnsNull(String s) {
+ return System.currentTimeMillis() > 0 ? null : s;
+ }
+
public static void main(String[] args) {
Foo foo = new Foo();
System.out.println(foo.getter());
@@ -73,6 +89,15 @@
} catch (NullPointerException npe) {
fail("Not expected: " + npe);
}
+
+ // No matter what we pass, that function will return null.
+ // But, we're not sure about it, hence not optimizing String#valueOf.
+ System.out.println(String.valueOf(eventuallyReturnsNull(null)));
+ System.out.println(String.valueOf(eventuallyReturnsNull("non-null")));
+
+ // Eligible for class inlining. Make sure we're optimizing valueOf after class inlining.
+ Notification n = new Notification(null);
+ System.out.println(String.valueOf(n.getId()));
}
}
@@ -83,6 +108,7 @@
ForceInline.class,
NeverInline.class,
StringValueOfTestMain.class,
+ StringValueOfTestMain.Notification.class,
StringValueOfTestMain.Itf.class,
StringValueOfTestMain.Foo.class
);
@@ -92,6 +118,9 @@
"com.android.tools.r8.ir.optimize.string.StringValueOfTestMain$Foo",
"com.android.tools.r8.ir.optimize.string.StringValueOfTestMain$Foo",
"null",
+ "null",
+ "null",
+ "null",
"null"
);
private static final Class<?> MAIN = StringValueOfTestMain.class;
@@ -175,14 +204,14 @@
.addProgramClasses(CLASSES)
.run(MAIN)
.assertSuccessWithOutput(JAVA_OUTPUT);
- test(result, 4, 1, 0);
+ test(result, 7, 1, 0);
result = testForD8()
.release()
.addProgramClasses(CLASSES)
.run(MAIN)
.assertSuccessWithOutput(JAVA_OUTPUT);
- test(result, 3, 1, 1);
+ test(result, 6, 1, 1);
}
@Test
@@ -196,6 +225,9 @@
.addOptionsModification(this::configure)
.run(MAIN)
.assertSuccessWithOutput(JAVA_OUTPUT);
- test(result, 1, 1, 1);
+ // Due to the different behavior regarding constant canonicalization.
+ int expectedNullCount = backend == Backend.CF ? 2 : 1;
+ int expectedNullStringCount = backend == Backend.CF ? 2 : 1;
+ test(result, 3, expectedNullCount, expectedNullStringCount);
}
}