Fix load elimination for System.out
Fixes: b/379347949
Change-Id: I635dbbccc0a6d4e6793694a74932c011820ec4c2
diff --git a/src/main/java/com/android/tools/r8/graph/DexClassAndField.java b/src/main/java/com/android/tools/r8/graph/DexClassAndField.java
index 1dc176f..1f1eff4 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClassAndField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClassAndField.java
@@ -61,7 +61,7 @@
return this;
}
- public final boolean isFinalOrEffectivelyFinal(AppView<?> appView) {
+ public boolean isFinalOrEffectivelyFinal(AppView<?> appView) {
return getAccessFlags().isFinal()
|| (appView.hasLiveness() && isEffectivelyFinal(appView.withLiveness()));
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index a79ae60..2c0c8e4 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -773,6 +773,7 @@
androidSystemOsConstantsMembers,
androidViewViewMembers,
// java.**
+ enumMembers,
javaIoFileMembers,
javaMathBigIntegerMembers,
javaNioByteOrderMembers,
@@ -2114,7 +2115,7 @@
private JavaIoPrintStreamMembers() {}
}
- public class EnumMembers {
+ public class EnumMembers extends LibraryMembers {
public final DexField nameField = createField(enumType, stringType, "name");
public final DexField ordinalField = createField(enumType, intType, "ordinal");
@@ -2169,6 +2170,12 @@
fn.accept(ordinalField);
}
+ @Override
+ public void forEachFinalField(Consumer<DexField> fn) {
+ fn.accept(nameField);
+ fn.accept(ordinalField);
+ }
+
@SuppressWarnings("ReferenceEquality")
public boolean isNameOrOrdinalField(DexField field) {
return field == nameField || field == ordinalField;
diff --git a/src/main/java/com/android/tools/r8/graph/LibraryField.java b/src/main/java/com/android/tools/r8/graph/LibraryField.java
index 49ed565..8221f8d 100644
--- a/src/main/java/com/android/tools/r8/graph/LibraryField.java
+++ b/src/main/java/com/android/tools/r8/graph/LibraryField.java
@@ -32,4 +32,9 @@
public boolean isLibraryMember() {
return true;
}
+
+ @Override
+ public boolean isFinalOrEffectivelyFinal(AppView<?> appView) {
+ return appView.libraryMethodOptimizer().isFinalLibraryField(getDefinition());
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java
index 39b5c5c..7768041 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java
@@ -157,12 +157,11 @@
}
@Override
- @SuppressWarnings("EqualsGetClass")
public boolean equals(Object other) {
if (this == other) {
return true;
}
- if (other == null || getClass() != other.getClass()) {
+ if (!(other instanceof ArraySlotWithConstantIndex)) {
return false;
}
ArraySlotWithConstantIndex arraySlot = (ArraySlotWithConstantIndex) other;
@@ -190,12 +189,11 @@
}
@Override
- @SuppressWarnings("EqualsGetClass")
public boolean equals(Object other) {
if (this == other) {
return true;
}
- if (other == null || getClass() != other.getClass()) {
+ if (!(other instanceof ArraySlotWithValueIndex)) {
return false;
}
ArraySlotWithValueIndex arraySlot = (ArraySlotWithValueIndex) other;
@@ -220,13 +218,15 @@
}
@Override
- @SuppressWarnings("ReferenceEquality")
public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
if (!(other instanceof FieldAndObject)) {
return false;
}
FieldAndObject o = (FieldAndObject) other;
- return o.object == object && o.field == field;
+ return o.object == object && o.field.isIdenticalTo(field);
}
}
@@ -345,14 +345,13 @@
return appView.libraryMethodOptimizer().isFinalLibraryField(field.getDefinition());
}
- @SuppressWarnings("ReferenceEquality")
private DexClassAndField resolveField(DexField field) {
if (appView.enableWholeProgramOptimizations()) {
SingleFieldResolutionResult resolutionResult =
appView.appInfo().withLiveness().resolveField(field).asSingleFieldResolutionResult();
return resolutionResult != null ? resolutionResult.getResolutionPair() : null;
}
- if (field.getHolderType() == method.getHolderType()) {
+ if (field.getHolderType().isIdenticalTo(method.getHolderType())) {
return method.getHolder().lookupProgramField(field);
}
return null;
@@ -614,10 +613,10 @@
return activeState.markClassAsInitialized(type);
}
- @SuppressWarnings("ReferenceEquality")
private void markMostRecentInitClassForRemoval(DexType initializedType) {
InitClass mostRecentInitClass = activeState.getMostRecentInitClass();
- if (mostRecentInitClass != null && mostRecentInitClass.getClassValue() == initializedType) {
+ if (mostRecentInitClass != null
+ && mostRecentInitClass.getClassValue().isIdenticalTo(initializedType)) {
instructionsToRemove
.computeIfAbsent(mostRecentInitClass.getBlock(), ignoreKey(Sets::newIdentityHashSet))
.add(mostRecentInitClass);
@@ -1113,10 +1112,9 @@
clearMostRecentStaticFieldWrites();
}
- @SuppressWarnings("ReferenceEquality")
public void clearMostRecentInstanceFieldWrite(DexField field) {
if (mostRecentInstanceFieldWrites != null) {
- mostRecentInstanceFieldWrites.keySet().removeIf(key -> key.field == field);
+ mostRecentInstanceFieldWrites.keySet().removeIf(key -> key.field.isIdenticalTo(field));
}
}
@@ -1329,10 +1327,9 @@
}
}
- @SuppressWarnings("ReferenceEquality")
public void removeNonFinalInstanceFields(DexField field) {
if (nonFinalInstanceFieldValues != null) {
- nonFinalInstanceFieldValues.keySet().removeIf(key -> key.field == field);
+ nonFinalInstanceFieldValues.keySet().removeIf(key -> key.field.isIdenticalTo(field));
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/RedundantLibraryFieldLoadEliminationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/RedundantLibraryFieldLoadEliminationTest.java
new file mode 100644
index 0000000..01593db
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/RedundantLibraryFieldLoadEliminationTest.java
@@ -0,0 +1,76 @@
+// Copyright (c) 2024, 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.redundantfieldloadelimination;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+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 com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.io.PrintStream;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class RedundantLibraryFieldLoadEliminationTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ parameters.assumeDexRuntime();
+ testForD8()
+ .addInnerClasses(getClass())
+ .release()
+ .setMinApi(parameters)
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello, world!");
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .setMinApi(parameters)
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello, world!");
+ }
+
+ private void inspect(CodeInspector inspector) {
+ MethodSubject mainMethodSubject = inspector.clazz(Main.class).mainMethod();
+ assertThat(mainMethodSubject, isPresent());
+ assertEquals(
+ 2, mainMethodSubject.streamInstructions().filter(InstructionSubject::isStaticGet).count());
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ PrintStream out = System.out;
+ System.currentTimeMillis();
+ PrintStream out2 = System.out;
+ out.print("Hello");
+ out2.println(", world!");
+ }
+ }
+}