Remove dead array initializations
When we can guarantee that a NewArray and NewArrayFilledData will not throw and is not used, remove them.
Bug: 122683370
Change-Id: I64e47d7adecb889c8181b3c03f66fc1ed9877472
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java b/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java
index c010973..e235682 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java
@@ -64,6 +64,24 @@
}
@Override
+ public boolean instructionInstanceCanThrow() {
+ return !(size().definition != null
+ && size().definition.isConstNumber()
+ && size().definition.asConstNumber().getRawValue() >= 0
+ && size().definition.asConstNumber().getRawValue() < Integer.MAX_VALUE);
+ }
+
+ @Override
+ public boolean canBeDeadCode(AppInfo appInfo, IRCode code) {
+ if (instructionInstanceCanThrow()) {
+ return false;
+ }
+ // This would belong better in instructionInstanceCanThrow, but that is not passed an appInfo.
+ DexType baseType = type.toBaseType(appInfo.dexItemFactory);
+ return baseType.isPrimitiveType() || appInfo.definitionFor(baseType) != null;
+ }
+
+ @Override
public boolean identicalNonValueNonPositionParts(Instruction other) {
return other.isNewArrayEmpty() && other.asNewArrayEmpty().type == type;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java b/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java
index ed51913..f534fb9 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.code.FillArrayDataPayload;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
@@ -73,6 +74,19 @@
}
@Override
+ public boolean canBeDeadCode(AppInfo appInfo, IRCode code) {
+ if (!src().getTypeLattice().isNullable() && src().numberOfAllUsers() == 1) {
+ // The NewArrayFilledData instruction is only inserted by an R8 optimization following
+ // a NewArrayEmpty when there are more than one entry.
+ assert src().uniqueUsers().iterator().next() == this;
+ assert src().definition != null;
+ assert src().definition.isNewArrayEmpty();
+ return true;
+ }
+ return false;
+ }
+
+ @Override
public boolean instructionTypeCanThrow() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java b/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java
index be171c9..fa259c2 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java
@@ -115,16 +115,15 @@
continue;
}
Value outValue = current.outValue();
- // Instructions with no out value cannot be dead code by the current definition
- // (unused out value). They typically side-effect input values or deals with control-flow.
- assert outValue != null;
- if (!outValue.isDead(appInfo)) {
+ if (outValue != null && !outValue.isDead(appInfo)) {
continue;
}
updateWorklist(worklist, current);
// All users will be removed for this instruction. Eagerly clear them so further inspection
// of this instruction during dead code elimination will terminate here.
- outValue.clearUsers();
+ if (outValue != null) {
+ outValue.clearUsers();
+ }
iterator.removeOrReplaceByDebugLocalRead();
}
}
diff --git a/src/test/examples/classmerging/ArrayTypeCollisionTest.java b/src/test/examples/classmerging/ArrayTypeCollisionTest.java
index 9e4c9fd..c422946 100644
--- a/src/test/examples/classmerging/ArrayTypeCollisionTest.java
+++ b/src/test/examples/classmerging/ArrayTypeCollisionTest.java
@@ -12,11 +12,11 @@
}
private static void method(A[] obj) {
- System.out.println("In method(A[])");
+ System.out.println("In method(A[]), length: " + obj.length);
}
private static void method(B[] obj) {
- System.out.println("In method(B[])");
+ System.out.println("In method(B[]), length: " + obj.length);
}
// A cannot be merged into B because that would lead to a collision.
diff --git a/src/test/java/com/android/tools/r8/classmerging/VerticalClassMergerTest.java b/src/test/java/com/android/tools/r8/classmerging/VerticalClassMergerTest.java
index 2284fa5..4e01188 100644
--- a/src/test/java/com/android/tools/r8/classmerging/VerticalClassMergerTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/VerticalClassMergerTest.java
@@ -209,7 +209,8 @@
" public static void main(...);",
"}",
"-neverinline class " + main + " {",
- " static void method(...);",
+ " static classmerging.A[] method(...);",
+ " static classmerging.B[] method(...);",
"}"));
}
diff --git a/src/test/java/com/android/tools/r8/deadcode/RemoveDeadArray.java b/src/test/java/com/android/tools/r8/deadcode/RemoveDeadArray.java
new file mode 100644
index 0000000..5de7de0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/deadcode/RemoveDeadArray.java
@@ -0,0 +1,92 @@
+// 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.deadcode;
+
+import static junit.framework.TestCase.assertTrue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.code.Aput;
+import com.android.tools.r8.code.Instruction;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.util.Arrays;
+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 RemoveDeadArray extends TestBase {
+
+ public static class TestClassWithCatch {
+ static {
+ try {
+ int[] foobar = new int[]{42, 42, 42, 42};
+ } catch (Exception ex) {
+ System.out.println("foobar");
+ }
+ }
+ }
+
+ public static class TestClass {
+ private static int[] foobar = new int[]{42, 42, 42, 42};
+ public static void main(java.lang.String[] args) {
+ ShouldGoAway[] willBeRemoved = new ShouldGoAway[4];
+ ShouldGoAway[] willAlsoBeRemoved = new ShouldGoAway[0];
+ System.out.println("foobar");
+ }
+
+ public static class ShouldGoAway { }
+ }
+
+ private Backend backend;
+
+ public RemoveDeadArray(Backend backend) {
+ this.backend = backend;
+ }
+
+ @Parameters(name = "Backend: {0}")
+ public static Backend[] data() {
+ // Todo(ricow): enable unused array removal for cf backend.
+ return new Backend[]{Backend.DEX};
+ }
+
+
+ @Test
+ public void testDeadArraysRemoved() throws Exception {
+ R8TestCompileResult result =
+ testForR8(backend)
+ .addProgramClasses(TestClass.class, TestClass.ShouldGoAway.class)
+ .addKeepMainRule(TestClass.class)
+ .compile();
+ CodeInspector inspector = result.inspector();
+ assertFalse(inspector.clazz(TestClass.class).clinit().isPresent());
+
+ MethodSubject main = inspector.clazz(TestClass.class).mainMethod();
+ main.streamInstructions().noneMatch(instructionSubject -> instructionSubject.isNewArray());
+ assertFalse(main.getMethod().getCode().asDexCode().toString().contains("NewArray"));
+ runOnArt(result.app, TestClass.class.getName());
+ }
+
+ @Test
+ public void testNotRemoveStaticCatch() throws Exception {
+ R8TestCompileResult result =
+ testForR8(backend)
+ .addProgramClasses(TestClassWithCatch.class)
+ .addKeepAllClassesRule()
+ .compile();
+ CodeInspector inspector = result.inspector();
+ MethodSubject clinit = inspector.clazz(TestClassWithCatch.class).clinit();
+ assertTrue(clinit.isPresent());
+ // Ensure that our optimization does not hit, we should still have 4 Aput instructions.
+ long aPutCount = Arrays.stream(clinit.getMethod().getCode().asDexCode().instructions)
+ .filter(instruction -> instruction instanceof Aput)
+ .count();
+ assertEquals(4, aPutCount);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java
index 924ec2d..7316d04 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java
@@ -24,6 +24,7 @@
import com.android.tools.r8.cf.code.CfLoad;
import com.android.tools.r8.cf.code.CfMonitor;
import com.android.tools.r8.cf.code.CfNew;
+import com.android.tools.r8.cf.code.CfNewArray;
import com.android.tools.r8.cf.code.CfNop;
import com.android.tools.r8.cf.code.CfPosition;
import com.android.tools.r8.cf.code.CfReturn;
@@ -277,6 +278,11 @@
}
@Override
+ public boolean isNewArray() {
+ return instruction instanceof CfNewArray;
+ }
+
+ @Override
public int size() {
// TODO(b/122302789): CfInstruction#getSize()
throw new UnsupportedOperationException("CfInstruction doesn't have size yet.");
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java
index 6d463da..d48e81a 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java
@@ -67,6 +67,7 @@
import com.android.tools.r8.code.MulIntLit8;
import com.android.tools.r8.code.MulLong;
import com.android.tools.r8.code.MulLong2Addr;
+import com.android.tools.r8.code.NewArray;
import com.android.tools.r8.code.NewInstance;
import com.android.tools.r8.code.Nop;
import com.android.tools.r8.code.PackedSwitch;
@@ -354,6 +355,11 @@
}
@Override
+ public boolean isNewArray() {
+ return instruction instanceof NewArray;
+ }
+
+ @Override
public boolean isMonitorEnter() {
return instruction instanceof MonitorEnter;
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
index 6abeacf..b417c68 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
@@ -80,6 +80,8 @@
boolean isMultiplication();
+ boolean isNewArray();
+
boolean isMonitorEnter();
boolean isMonitorExit();