Remove redundant ConstClass instructions in redundant load elimination
Fixes: b/501056932
Bug: b/490364465
Change-Id: Ica28ba97cd3b907ca9cd3da7fce4eeeae6323215
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/passes/CodeRewriterPassCollection.java b/src/main/java/com/android/tools/r8/ir/conversion/passes/CodeRewriterPassCollection.java
index 2227864..70ae00e 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/passes/CodeRewriterPassCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/passes/CodeRewriterPassCollection.java
@@ -12,7 +12,7 @@
import com.android.tools.r8.ir.conversion.MethodProcessor;
import com.android.tools.r8.ir.conversion.passes.result.CodeRewriterResult;
import com.android.tools.r8.ir.optimize.ListIterationRewriter;
-import com.android.tools.r8.ir.optimize.RedundantFieldLoadAndStoreElimination;
+import com.android.tools.r8.ir.optimize.RedundantLoadAndStoreElimination;
import com.android.tools.r8.ir.optimize.ServiceLoaderRewriter;
import com.android.tools.r8.ir.optimize.ShareInstanceGetInstructions;
import com.android.tools.r8.ir.optimize.enums.EnumValueOptimizer;
@@ -57,7 +57,7 @@
passes.add(new SplitBranch(appView));
passes.add(new RedundantConstNumberRemover(appView));
if (appView.options().isRelease()) {
- passes.add(new RedundantFieldLoadAndStoreElimination(appView));
+ passes.add(new RedundantLoadAndStoreElimination(appView));
}
passes.add(new BinopRewriter(appView));
passes.add(new ServiceLoaderRewriter(appView));
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java b/src/main/java/com/android/tools/r8/ir/optimize/RedundantLoadAndStoreElimination.java
similarity index 92%
rename from src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java
rename to src/main/java/com/android/tools/r8/ir/optimize/RedundantLoadAndStoreElimination.java
index d3ec828..0fba875 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/RedundantLoadAndStoreElimination.java
@@ -45,7 +45,7 @@
import com.android.tools.r8.ir.conversion.MethodProcessor;
import com.android.tools.r8.ir.conversion.passes.CodeRewriterPass;
import com.android.tools.r8.ir.conversion.passes.result.CodeRewriterResult;
-import com.android.tools.r8.ir.optimize.RedundantFieldLoadAndStoreElimination.RedundantFieldLoadAndStoreEliminationOnCode.ExistingValue;
+import com.android.tools.r8.ir.optimize.RedundantLoadAndStoreElimination.RedundantFieldLoadAndStoreEliminationOnCode.ExistingValue;
import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoCollection;
import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -65,29 +65,30 @@
import java.util.Set;
/**
- * Eliminate redundant field loads.
+ * Eliminate redundant loads and stores.
*
* <p>Simple algorithm that goes through all blocks in one pass in topological order and propagates
- * active field sets across control-flow edges where the target has only one predecessor.
+ * active load/store sets across control-flow edges where the target has only one predecessor.
*/
-public class RedundantFieldLoadAndStoreElimination extends CodeRewriterPass<AppInfo> {
+public class RedundantLoadAndStoreElimination extends CodeRewriterPass<AppInfo> {
private static final int MAX_CAPACITY = 10000;
private static final int MIN_CAPACITY_PER_BLOCK = 50;
- public RedundantFieldLoadAndStoreElimination(AppView<?> appView) {
+ public RedundantLoadAndStoreElimination(AppView<?> appView) {
super(appView);
}
@Override
protected String getRewriterId() {
- return "RedundantFieldLoadAndStoreElimination";
+ return "RedundantLoadAndStoreElimination";
}
@Override
protected boolean shouldRewriteCode(IRCode code, MethodProcessor methodProcessor) {
- return appView.options().enableRedundantFieldLoadElimination
+ return appView.options().enableRedundantLoadAndStoreElimination
&& (code.metadata().mayHaveArrayGet()
+ || code.metadata().mayHaveConstClass()
|| code.metadata().mayHaveFieldInstruction()
|| code.metadata().mayHaveInitClass());
}
@@ -97,7 +98,7 @@
return new RedundantFieldLoadAndStoreEliminationOnCode(code).run();
}
- private interface FieldValue {
+ private interface ExistingOrMaterializableValue {
default ExistingValue asExistingValue() {
return null;
@@ -259,7 +260,7 @@
assert !appView.options().debug;
}
- class ExistingValue implements FieldValue {
+ class ExistingValue implements ExistingOrMaterializableValue {
private final Value value;
@@ -294,7 +295,7 @@
}
}
- private class MaterializableValue implements FieldValue {
+ private class MaterializableValue implements ExistingOrMaterializableValue {
private final SingleValue value;
@@ -318,7 +319,7 @@
DexItemFactory dexItemFactory = appView.dexItemFactory();
if (value.isSingleStringValue() || value.isSingleDexItemBasedStringValue()) {
return dexItemFactory.stringType.toTypeElement(
- RedundantFieldLoadAndStoreElimination.this.appView, Nullability.definitelyNotNull());
+ RedundantLoadAndStoreElimination.this.appView, Nullability.definitelyNotNull());
}
if (value.isSingleFieldValue()) {
return value.asSingleFieldValue().getField().getTypeElement(appView);
@@ -394,6 +395,8 @@
}
} else if (instruction.isInitClass()) {
handleInitClass(it, instruction.asInitClass());
+ } else if (instruction.isConstClass()) {
+ handleConstClass(it, instruction.asConstClass());
} else if (instruction.isMonitor()) {
if (instruction.isMonitorEnter()) {
killAllNonFinalActiveFields();
@@ -428,7 +431,6 @@
|| instruction.isAssume()
|| instruction.isBinop()
|| instruction.isCheckCast()
- || instruction.isConstClass()
|| instruction.isConstMethodHandle()
|| instruction.isConstMethodType()
|| instruction.isConstNumber()
@@ -604,6 +606,23 @@
}
}
+ private void handleConstClass(
+ InstructionListIterator instructionIterator,
+ com.android.tools.r8.ir.code.ConstClass constClass) {
+ if (constClass.outValue().hasLocalInfo()) {
+ return;
+ }
+
+ DexType type = constClass.getType();
+ ExistingOrMaterializableValue replacement = activeState.getConstClassValue(type);
+ if (replacement != null) {
+ replacement.eliminateRedundantRead(instructionIterator, constClass);
+ return;
+ }
+
+ activeState.putConstClassValue(type, new ExistingValue(constClass.outValue()));
+ }
+
private boolean markClassAsInitialized(DexType type) {
return activeState.markClassAsInitialized(type);
}
@@ -637,7 +656,7 @@
Value array = arrayGet.array().getAliasedValue();
Value index = arrayGet.index().getAliasedValue();
ArraySlot arraySlot = ArraySlot.create(array, index, arrayGet.getMemberType());
- FieldValue replacement = activeState.getArraySlotValue(arraySlot);
+ ExistingOrMaterializableValue replacement = activeState.getArraySlotValue(arraySlot);
if (replacement != null) {
TypeElement outType = arrayGet.outValue().getType();
if (replacement.getType(appView, outType).lessThanOrEqual(outType, appView)) {
@@ -684,7 +703,7 @@
Value object = instanceGet.object().getAliasedValue();
FieldAndObject fieldAndObject = new FieldAndObject(field.getReference(), object);
- FieldValue replacement = activeState.getInstanceFieldValue(fieldAndObject);
+ ExistingOrMaterializableValue replacement = activeState.getInstanceFieldValue(fieldAndObject);
if (replacement != null) {
if (isRedundantFieldLoadEliminationAllowed(field)) {
replacement.eliminateRedundantRead(it, instanceGet);
@@ -775,7 +794,8 @@
return;
}
- FieldValue replacement = activeState.getStaticFieldValue(field.getReference());
+ ExistingOrMaterializableValue replacement =
+ activeState.getStaticFieldValue(field.getReference());
if (replacement != null) {
replacement.eliminateRedundantRead(instructionIterator, staticGet);
return;
@@ -785,7 +805,7 @@
killNonFinalActiveFields(staticGet);
clearMostRecentStaticFieldWrite(staticGet, field);
- FieldValue value = new ExistingValue(staticGet.value());
+ ExistingOrMaterializableValue value = new ExistingValue(staticGet.value());
if (field.isFinalOrEffectivelyFinal(appView)) {
activeState.putFinalStaticField(field.getReference(), value);
} else {
@@ -1032,17 +1052,20 @@
static class BlockState {
- private LinkedHashMap<ArraySlot, FieldValue> arraySlotValues;
+ private LinkedHashMap<ArraySlot, ExistingOrMaterializableValue> arraySlotValues;
- private LinkedHashMap<FieldAndObject, FieldValue> finalInstanceFieldValues;
+ private LinkedHashMap<DexType, ExistingOrMaterializableValue> constClassValues;
- private LinkedHashMap<DexField, FieldValue> finalStaticFieldValues;
+ private LinkedHashMap<FieldAndObject, ExistingOrMaterializableValue> finalInstanceFieldValues;
+
+ private LinkedHashMap<DexField, ExistingOrMaterializableValue> finalStaticFieldValues;
private LinkedHashSet<DexType> initializedClasses;
- private LinkedHashMap<FieldAndObject, FieldValue> nonFinalInstanceFieldValues;
+ private LinkedHashMap<FieldAndObject, ExistingOrMaterializableValue>
+ nonFinalInstanceFieldValues;
- private LinkedHashMap<DexField, FieldValue> nonFinalStaticFieldValues;
+ private LinkedHashMap<DexField, ExistingOrMaterializableValue> nonFinalStaticFieldValues;
private InitClass mostRecentInitClass;
@@ -1063,6 +1086,10 @@
arraySlotValues = new LinkedHashMap<>();
arraySlotValues.putAll(state.arraySlotValues);
}
+ if (state.constClassValues != null && !state.constClassValues.isEmpty()) {
+ constClassValues = new LinkedHashMap<>();
+ constClassValues.putAll(state.constClassValues);
+ }
if (state.finalInstanceFieldValues != null && !state.finalInstanceFieldValues.isEmpty()) {
finalInstanceFieldValues = new LinkedHashMap<>();
finalInstanceFieldValues.putAll(state.finalInstanceFieldValues);
@@ -1143,12 +1170,16 @@
}
}
- public FieldValue getArraySlotValue(ArraySlot arraySlot) {
+ public ExistingOrMaterializableValue getArraySlotValue(ArraySlot arraySlot) {
return arraySlotValues != null ? arraySlotValues.get(arraySlot) : null;
}
- public FieldValue getInstanceFieldValue(FieldAndObject field) {
- FieldValue value =
+ public ExistingOrMaterializableValue getConstClassValue(DexType type) {
+ return constClassValues != null ? constClassValues.get(type) : null;
+ }
+
+ public ExistingOrMaterializableValue getInstanceFieldValue(FieldAndObject field) {
+ ExistingOrMaterializableValue value =
nonFinalInstanceFieldValues != null ? nonFinalInstanceFieldValues.get(field) : null;
if (value != null) {
return value;
@@ -1156,8 +1187,8 @@
return finalInstanceFieldValues != null ? finalInstanceFieldValues.get(field) : null;
}
- public FieldValue getStaticFieldValue(DexField field) {
- FieldValue value =
+ public ExistingOrMaterializableValue getStaticFieldValue(DexField field) {
+ ExistingOrMaterializableValue value =
nonFinalStaticFieldValues != null ? nonFinalStaticFieldValues.get(field) : null;
if (value != null) {
return value;
@@ -1171,6 +1202,11 @@
} else {
arraySlotValues = null;
}
+ if (constClassValues != null && state.constClassValues != null) {
+ intersectFieldValues(constClassValues, state.constClassValues);
+ } else {
+ constClassValues = null;
+ }
if (finalInstanceFieldValues != null && state.finalInstanceFieldValues != null) {
intersectFieldValues(finalInstanceFieldValues, state.finalInstanceFieldValues);
} else {
@@ -1202,7 +1238,8 @@
}
private static <K> void intersectFieldValues(
- Map<K, FieldValue> fieldValues, Map<K, FieldValue> other) {
+ Map<K, ExistingOrMaterializableValue> fieldValues,
+ Map<K, ExistingOrMaterializableValue> other) {
fieldValues.entrySet().removeIf(entry -> other.get(entry.getKey()) != entry.getValue());
}
@@ -1213,6 +1250,7 @@
public boolean isEmpty() {
return isEmpty(arraySlotValues)
+ && isEmpty(constClassValues)
&& isEmpty(initializedClasses)
&& isEmpty(finalInstanceFieldValues)
&& isEmpty(finalStaticFieldValues)
@@ -1261,6 +1299,7 @@
assert numberOfItemsToRemove > 0;
assert numberOfItemsToRemove < size();
numberOfItemsToRemove = reduceSize(numberOfItemsToRemove, arraySlotValues);
+ numberOfItemsToRemove = reduceSize(numberOfItemsToRemove, constClassValues);
numberOfItemsToRemove = reduceSize(numberOfItemsToRemove, initializedClasses);
numberOfItemsToRemove = reduceSize(numberOfItemsToRemove, nonFinalInstanceFieldValues);
numberOfItemsToRemove = reduceSize(numberOfItemsToRemove, nonFinalStaticFieldValues);
@@ -1358,7 +1397,7 @@
}
}
- public void putArraySlotValue(ArraySlot arraySlot, FieldValue value) {
+ public void putArraySlotValue(ArraySlot arraySlot, ExistingOrMaterializableValue value) {
ensureCapacityForNewElement();
if (arraySlotValues == null) {
arraySlotValues = new LinkedHashMap<>();
@@ -1366,7 +1405,16 @@
arraySlotValues.put(arraySlot, value);
}
- public void putFinalOrEffectivelyFinalInstanceField(FieldAndObject field, FieldValue value) {
+ public void putConstClassValue(DexType type, ExistingOrMaterializableValue value) {
+ ensureCapacityForNewElement();
+ if (constClassValues == null) {
+ constClassValues = new LinkedHashMap<>();
+ }
+ constClassValues.put(type, value);
+ }
+
+ public void putFinalOrEffectivelyFinalInstanceField(
+ FieldAndObject field, ExistingOrMaterializableValue value) {
ensureCapacityForNewElement();
if (finalInstanceFieldValues == null) {
finalInstanceFieldValues = new LinkedHashMap<>();
@@ -1374,7 +1422,7 @@
finalInstanceFieldValues.put(field, value);
}
- public void putFinalStaticField(DexField field, FieldValue value) {
+ public void putFinalStaticField(DexField field, ExistingOrMaterializableValue value) {
ensureCapacityForNewElement();
if (finalStaticFieldValues == null) {
finalStaticFieldValues = new LinkedHashMap<>();
@@ -1399,7 +1447,8 @@
return mostRecentStaticFieldWrites.put(field, staticPut);
}
- public void putNonFinalInstanceField(FieldAndObject field, FieldValue value) {
+ public void putNonFinalInstanceField(
+ FieldAndObject field, ExistingOrMaterializableValue value) {
ensureCapacityForNewElement();
assert finalInstanceFieldValues == null || !finalInstanceFieldValues.containsKey(field);
if (nonFinalInstanceFieldValues == null) {
@@ -1408,7 +1457,7 @@
nonFinalInstanceFieldValues.put(field, value);
}
- public void putNonFinalStaticField(DexField field, FieldValue value) {
+ public void putNonFinalStaticField(DexField field, ExistingOrMaterializableValue value) {
ensureCapacityForNewElement();
assert nonFinalStaticFieldValues == null || !nonFinalStaticFieldValues.containsKey(field);
if (nonFinalStaticFieldValues == null) {
@@ -1433,6 +1482,7 @@
public int size() {
return size(arraySlotValues)
+ + size(constClassValues)
+ size(finalInstanceFieldValues)
+ size(finalStaticFieldValues)
+ size(initializedClasses)
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 63dd31a..b115033 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -502,7 +502,7 @@
public boolean enableEnumSwitchMapRemoval = true;
public final OutlineOptions outline = new OutlineOptions();
public boolean enableInitializedClassesInInstanceMethodsAnalysis = true;
- public boolean enableRedundantFieldLoadElimination = true;
+ public boolean enableRedundantLoadAndStoreElimination = true;
// TODO(b/138917494): Disable until we have numbers on potential performance penalties.
public boolean enableRedundantConstNumberOptimization = false;
public boolean enableLoopUnrolling = true;
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/ConstClassCanonicalizationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/ConstClassCanonicalizationTest.java
index 54dc249..5dd01c8 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/ConstClassCanonicalizationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/ConstClassCanonicalizationTest.java
@@ -97,6 +97,7 @@
private static final int CANONICALIZED_MAIN_COUNT = 1;
private static final int CANONICALIZED_OUTER_COUNT = 1;
private static final int CANONICALIZED_INNER_COUNT = 1;
+ private static final int PARTIALLY_OPTIMIZED_INNER_COUNT = 2;
@Parameterized.Parameters(name = "{0}, isCompat: {1}")
public static List<Object[]> data() {
@@ -148,7 +149,7 @@
MethodSubject mainMethod = main.mainMethod();
assertThat(mainMethod, isPresent());
assertEquals(
- 3,
+ 1,
mainMethod.streamInstructions().filter(InstructionSubject::isConstClass).count());
});
}
@@ -203,8 +204,8 @@
.run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutput(JAVA_OUTPUT),
CANONICALIZED_MAIN_COUNT,
- ORIGINAL_OUTER_COUNT,
- ORIGINAL_INNER_COUNT);
+ CANONICALIZED_OUTER_COUNT,
+ PARTIALLY_OPTIMIZED_INNER_COUNT);
}
@Test
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/IllegalStaticGetCanonicalizationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/IllegalStaticGetCanonicalizationTest.java
index 5f7f094..cb0c098 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/IllegalStaticGetCanonicalizationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/IllegalStaticGetCanonicalizationTest.java
@@ -33,7 +33,7 @@
testForR8(parameters.getBackend())
.addInnerClasses(IllegalStaticGetCanonicalizationTest.class)
.addKeepMainRule(TestClass.class)
- .addOptionsModification(options -> options.enableRedundantFieldLoadElimination = false)
+ .addOptionsModification(options -> options.enableRedundantLoadAndStoreElimination = false)
.enableInliningAnnotations()
.enableReprocessClassInitializerAnnotations()
.setMinApi(parameters)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/redundantconstclasselimination/RedundantConstClassEliminationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/redundantconstclasselimination/RedundantConstClassEliminationTest.java
new file mode 100644
index 0000000..46a3a0c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/redundantconstclasselimination/RedundantConstClassEliminationTest.java
@@ -0,0 +1,67 @@
+// Copyright (c) 2026, 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.redundantconstclasselimination;
+
+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 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 RedundantConstClassEliminationTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withDexRuntimesAndAllApiLevels().build();
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForD8(parameters)
+ .addInnerClasses(RedundantConstClassEliminationTest.class)
+ .release()
+ .setMinApi(parameters)
+ .compile()
+ .inspect(this::inspect);
+ }
+
+ private void inspect(CodeInspector inspector) {
+ MethodSubject mainMethodSubject = inspector.clazz(TestClass.class).mainMethod();
+ assertThat(mainMethodSubject, isPresent());
+ assertEquals(
+ 2, mainMethodSubject.streamInstructions().filter(InstructionSubject::isConstClass).count());
+ }
+
+ static class A {}
+
+ static class B {}
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ consume(A.class);
+ consume(A.class);
+ consume(B.class);
+ consume(B.class);
+ }
+
+ static void consume(Class<?> clazz) {
+ System.out.println(clazz.getName());
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetClassTest.java b/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetClassTest.java
index b23e434..bf63725 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetClassTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetClassTest.java
@@ -209,7 +209,7 @@
boolean isRelease = mode == CompilationMode.RELEASE;
boolean expectCallPresent = !isRelease;
int expectedGetClassCount = isRelease ? 0 : 5;
- int expectedConstClassCount = isRelease ? (parameters.isCfRuntime() ? 9 : 6) : 1;
+ int expectedConstClassCount = isRelease ? (parameters.isCfRuntime() ? 7 : 6) : 1;
testForR8(parameters)
.setMode(mode)
.addInnerClasses(GetClassTest.class)
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingTest.java b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingTest.java
index 15d91c2..902421d 100644
--- a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingTest.java
+++ b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingTest.java
@@ -254,7 +254,7 @@
.addKeepRules("-neverpropagatevalue class * { *; }")
.addOptionsModification(
options -> {
- options.enableRedundantFieldLoadElimination = false;
+ options.enableRedundantLoadAndStoreElimination = false;
options.inlinerOptions().enableInlining = false;
})
.enableProguardTestOptions()
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterHorizontalMergingFieldTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterHorizontalMergingFieldTest.java
index 1e11234..04f428d 100644
--- a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterHorizontalMergingFieldTest.java
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterHorizontalMergingFieldTest.java
@@ -90,7 +90,8 @@
R8TestCompileResult libraryResult =
testForR8(parameters.getBackend())
.addProgramClasses(LIBRARY_CLASSES)
- .addOptionsModification(options -> options.enableRedundantFieldLoadElimination = false)
+ .addOptionsModification(
+ options -> options.enableRedundantLoadAndStoreElimination = false)
.addKeepMainRule(LibraryMain.class)
.setMinApi(parameters)
.compile();
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithInitClassTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithInitClassTest.java
index d6cce99..1c2bbab 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageWithInitClassTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithInitClassTest.java
@@ -54,7 +54,7 @@
.apply(this::configureRepackaging)
.enableMemberValuePropagationAnnotations(enableMemberValuePropagationAnnotations)
.enableNoAccessModificationAnnotationsForMembers()
- .addOptionsModification(options -> options.enableRedundantFieldLoadElimination = false)
+ .addOptionsModification(options -> options.enableRedundantLoadAndStoreElimination = false)
.setMinApi(parameters)
.compile()
.inspect(this::inspect)
diff --git a/src/test/java/com/android/tools/r8/rewrite/arrays/ConstClassArrayWithNonUniqueValuesTest.java b/src/test/java/com/android/tools/r8/rewrite/arrays/ConstClassArrayWithNonUniqueValuesTest.java
index 7d8bba7..9459997 100644
--- a/src/test/java/com/android/tools/r8/rewrite/arrays/ConstClassArrayWithNonUniqueValuesTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/arrays/ConstClassArrayWithNonUniqueValuesTest.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.rewrite.arrays;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.NeverInline;
@@ -54,8 +55,11 @@
assertEquals(
expectingFilledNewArray ? 1 : 0,
method.streamInstructions().filter(InstructionSubject::isFilledNewArray).count());
+ // When using filled-new-array, we generate 1 const-class instruction + 99 moves.
assertEquals(
- expectingFilledNewArray || parameters.isCfRuntime() ? puts : constClasses,
+ (compilationMode.isDebug() && expectingFilledNewArray) || parameters.isCfRuntime()
+ ? puts
+ : constClasses,
method.streamInstructions().filter(InstructionSubject::isConstClass).count());
}
@@ -69,7 +73,9 @@
inspect(inspector.clazz(TestClass.class).uniqueMethodWithOriginalName("m1"), 1, 100);
inspect(
inspector.clazz(TestClass.class).uniqueMethodWithOriginalName("m2"),
- maxMaterializingConstants == 2 ? 98 : 26,
+ canUseFilledNewArrayOfNonStringObjects(parameters) || maxMaterializingConstants != 2
+ ? 26
+ : 98,
104);
}
@@ -79,6 +85,7 @@
testForD8(parameters.getBackend())
.addInnerClasses(getClass())
.setMinApi(parameters)
+ .setMode(compilationMode)
.apply(this::configure)
.run(parameters.getRuntime(), TestClass.class)
.inspect(this::inspectD8)
@@ -89,12 +96,17 @@
inspect(inspector.clazz(TestClass.class).uniqueMethodWithOriginalName("m1"), 1, 100);
inspect(
inspector.clazz(TestClass.class).uniqueMethodWithOriginalName("m2"),
- maxMaterializingConstants == 2 ? 32 : 26,
+ // The lowering from FilledNewArray to a sequence of ArrayPut instructions in the compiler
+ // uses rematerialization when possible, rather than moves.
+ canUseFilledNewArrayOfNonStringObjects(parameters) || maxMaterializingConstants != 2
+ ? 26
+ : 32,
104);
}
@Test
public void testR8() throws Exception {
+ assumeTrue(compilationMode.isRelease());
testForR8(parameters.getBackend())
.addInnerClasses(getClass())
.addKeepMainRule(TestClass.class)