Add tests about StringBuilder used by potentially dead code.
Bug: 114002137, 137038659
Change-Id: Ib44b0ff0e11af1ac3ebcdd51cb8730b93d8e777f
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java
index e4446c6..8d3942f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java
@@ -509,7 +509,8 @@
assert invoke.inValues().size() == 1;
Value builder = invoke.getReceiver().getAliasedValue();
assert invoke.hasOutValue();
- if (invoke.outValue().isDead(appView, code)) {
+ // TODO(b/114002137): Can be extended to potentially dead code.
+ if (!invoke.outValue().isUsed()) {
it.removeOrReplaceByDebugLocalRead();
// Although this builder is not simplified by this analysis, add that to the set so that
// it can be removed at the final clean-up phase.
@@ -544,29 +545,31 @@
return false;
}
DexMethod invokedMethod = invoke.getInvokedMethod();
- if (optimizationConfiguration.isToStringMethod(invokedMethod)) {
- assert invoke.inValues().size() == 1;
- Value builder = invoke.getReceiver().getAliasedValue();
- if (!candidateBuilders.contains(builder)) {
- return false;
- }
- // If the result of toString() is no longer used, computing the compile-time constant is
- // even not necessary.
- if (invoke.outValue().isDead(appView, code)) {
- return true;
- }
- Map<Instruction, BuilderState> perInstrState = builderStates.get(builder);
- if (perInstrState == null) {
- return false;
- }
- BuilderState builderState = perInstrState.get(instr);
- if (builderState == null) {
- return false;
- }
- String element = toCompileTimeString(builderState);
- if (element == null) {
- return false;
- }
+ if (!optimizationConfiguration.isToStringMethod(invokedMethod)) {
+ return false;
+ }
+ assert invoke.inValues().size() == 1;
+ Value builder = invoke.getReceiver().getAliasedValue();
+ if (!candidateBuilders.contains(builder)) {
+ return false;
+ }
+ // If the result of toString() is no longer used, computing the compile-time constant is
+ // even not necessary.
+ // TODO(b/114002137): Can be extended to potentially dead code.
+ if (!invoke.outValue().isUsed()) {
+ return true;
+ }
+ Map<Instruction, BuilderState> perInstrState = builderStates.get(builder);
+ if (perInstrState == null) {
+ return false;
+ }
+ BuilderState builderState = perInstrState.get(instr);
+ if (builderState == null) {
+ return false;
+ }
+ String element = toCompileTimeString(builderState);
+ if (element == null) {
+ return false;
}
return true;
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderStoredToDeadFieldTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderStoredToDeadFieldTest.java
new file mode 100644
index 0000000..13e58e1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderStoredToDeadFieldTest.java
@@ -0,0 +1,83 @@
+// 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.ir.optimize.string;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class StringBuilderStoredToDeadFieldTest extends TestBase {
+ private static final Class<?> MAIN = TestClass.class;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().build();
+ }
+
+ private final TestParameters parameters;
+
+ public StringBuilderStoredToDeadFieldTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ assumeTrue("CF does not rewrite move results.", parameters.isDexRuntime());
+
+ testForR8(parameters.getBackend())
+ .addInnerClasses(StringBuilderStoredToDeadFieldTest.class)
+ .addKeepMainRule(MAIN)
+ .addOptionsModification(this::configure)
+ .noMinification()
+ .setMinApi(parameters.getRuntime())
+ .run(parameters.getRuntime(), MAIN)
+ .assertSuccess()
+ .inspect(this::inspect);
+ }
+
+ private void inspect(CodeInspector inspector) {
+ ClassSubject main = inspector.clazz(MAIN);
+ assertThat(main, isPresent());
+
+ MethodSubject mainMethod = main.mainMethod();
+ assertThat(main, isPresent());
+
+ // TODO(b/114002137): can remove builder usage, which is linked to potentially dead code.
+ assertFalse(
+ mainMethod.streamInstructions().noneMatch(
+ i -> i.isInvoke()
+ && i.getMethod().holder.toDescriptorString().contains("StringBuilder")));
+ }
+
+ // TODO(b/114002137): Once enabled, remove this test-specific setting.
+ private void configure(InternalOptions options) {
+ assert !options.enableStringConcatenationOptimization;
+ options.enableStringConcatenationOptimization = true;
+ }
+
+ static class TestClass {
+ static String neverRead;
+
+ public static void main(String... args) {
+ StringBuilder b = new StringBuilder();
+ b.append("CurrentTimeMillis: ");
+ // TODO(b/114002137): switch to use Long value as-is.
+ b.append(String.valueOf(System.currentTimeMillis()));
+ neverRead = b.toString();
+ }
+ }
+}