Redundant field load elimination for final fields that are guaranteed to be initialized
Bug: 152196923
Change-Id: Ia22e105127d3ad73cdfff8198b73474ed9fde64c
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
index 44526ed..0d5c50d 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
@@ -159,6 +159,10 @@
return isStatic();
}
+ public boolean isVolatile() {
+ return accessFlags.isVolatile();
+ }
+
public boolean hasAnnotation() {
return !annotations().isEmpty();
}
diff --git a/src/main/java/com/android/tools/r8/graph/classmerging/VerticallyMergedClasses.java b/src/main/java/com/android/tools/r8/graph/classmerging/VerticallyMergedClasses.java
index 237f9ab..944d680 100644
--- a/src/main/java/com/android/tools/r8/graph/classmerging/VerticallyMergedClasses.java
+++ b/src/main/java/com/android/tools/r8/graph/classmerging/VerticallyMergedClasses.java
@@ -39,6 +39,10 @@
return mergedClasses.containsKey(type);
}
+ public boolean isTarget(DexType type) {
+ return !getSourcesFor(type).isEmpty();
+ }
+
@Override
public boolean verifyAllSourcesPruned(AppView<AppInfoWithLiveness> appView) {
for (List<DexType> sourcesForTarget : sources.values()) {
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRMetadata.java b/src/main/java/com/android/tools/r8/ir/code/IRMetadata.java
index 5c488c4..e46af2c 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRMetadata.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRMetadata.java
@@ -116,6 +116,10 @@
return result;
}
+ public boolean mayHaveInitClass() {
+ return get(Opcodes.INIT_CLASS);
+ }
+
public boolean mayHaveInstanceGet() {
return get(Opcodes.INSTANCE_GET);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
index e94bb7b..a5501a6 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.analysis.value.SingleValue;
import com.android.tools.r8.ir.code.BasicBlock;
@@ -31,10 +32,8 @@
import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
import com.android.tools.r8.utils.SetUtils;
import com.google.common.collect.Sets;
-import java.util.ArrayList;
import java.util.HashMap;
import java.util.IdentityHashMap;
-import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -58,16 +57,12 @@
// Maps keeping track of fields that have an already loaded value at basic block entry.
private final Map<BasicBlock, Set<DexType>> activeInitializedClassesAtEntry =
new IdentityHashMap<>();
- private final Map<BasicBlock, Map<FieldAndObject, FieldValue>> activeInstanceFieldsAtEntry =
- new IdentityHashMap<>();
- private final Map<BasicBlock, Map<DexField, FieldValue>> activeStaticFieldsAtEntry =
- new IdentityHashMap<>();
+ private final Map<BasicBlock, FieldValuesMap> activeFieldsAtEntry = new IdentityHashMap<>();
// Maps keeping track of fields with already loaded values for the current block during
// elimination.
private Set<DexType> activeInitializedClasses;
- private Map<FieldAndObject, FieldValue> activeInstanceFieldValues;
- private Map<DexField, FieldValue> activeStaticFieldValues;
+ private FieldValuesMap activeFieldValues;
public RedundantFieldLoadElimination(AppView<?> appView, IRCode code) {
this.appView = appView;
@@ -78,7 +73,7 @@
public static boolean shouldRun(AppView<?> appView, IRCode code) {
return appView.options().enableRedundantFieldLoadElimination
- && code.metadata().mayHaveFieldGet();
+ && (code.metadata().mayHaveFieldGet() || code.metadata().mayHaveInitClass());
}
private interface FieldValue {
@@ -145,17 +140,14 @@
}
}
- private boolean couldBeVolatile(DexField field) {
- DexEncodedField definition;
+ private DexEncodedField resolveField(DexField field) {
if (appView.enableWholeProgramOptimizations()) {
- definition = appView.appInfo().resolveField(field);
- } else {
- if (field.holder != method.holder()) {
- return true;
- }
- definition = appView.definitionFor(field);
+ return appView.appInfo().resolveField(field);
}
- return definition == null || definition.accessFlags.isVolatile();
+ if (field.holder == method.holder()) {
+ return appView.definitionFor(field);
+ }
+ return null;
}
public void run() {
@@ -165,21 +157,18 @@
activeInitializedClassesAtEntry.containsKey(block)
? activeInitializedClassesAtEntry.get(block)
: Sets.newIdentityHashSet();
- activeInstanceFieldValues =
- activeInstanceFieldsAtEntry.containsKey(block)
- ? activeInstanceFieldsAtEntry.get(block)
- : new HashMap<>();
- activeStaticFieldValues =
- activeStaticFieldsAtEntry.containsKey(block)
- ? activeStaticFieldsAtEntry.get(block)
- : new IdentityHashMap<>();
+ activeFieldValues =
+ activeFieldsAtEntry.containsKey(block)
+ ? activeFieldsAtEntry.get(block)
+ : new FieldValuesMap();
InstructionListIterator it = block.listIterator(code);
while (it.hasNext()) {
Instruction instruction = it.next();
if (instruction.isFieldInstruction()) {
DexField field = instruction.asFieldInstruction().getField();
- if (couldBeVolatile(field)) {
- killAllActiveFields();
+ DexEncodedField definition = resolveField(field);
+ if (definition == null || definition.isVolatile()) {
+ killAllNonFinalActiveFields();
continue;
}
@@ -190,41 +179,54 @@
}
Value object = instanceGet.object().getAliasedValue();
FieldAndObject fieldAndObject = new FieldAndObject(field, object);
- if (activeInstanceFieldValues.containsKey(fieldAndObject)) {
- FieldValue replacement = activeInstanceFieldValues.get(fieldAndObject);
+ FieldValue replacement = activeFieldValues.getInstanceFieldValue(fieldAndObject);
+ if (replacement != null) {
replacement.eliminateRedundantRead(it, instanceGet);
} else {
- activeInstanceFieldValues.put(fieldAndObject, new ExistingValue(instanceGet.value()));
+ activeFieldValues.putNonFinalInstanceField(
+ fieldAndObject, new ExistingValue(instanceGet.value()));
}
} else if (instruction.isInstancePut()) {
InstancePut instancePut = instruction.asInstancePut();
// An instance-put instruction can potentially write the given field on all objects
// because of aliases.
- killActiveFields(instancePut);
+ killNonFinalActiveFields(instancePut);
// ... but at least we know the field value for this particular object.
Value object = instancePut.object().getAliasedValue();
FieldAndObject fieldAndObject = new FieldAndObject(field, object);
- activeInstanceFieldValues.put(fieldAndObject, new ExistingValue(instancePut.value()));
+ ExistingValue value = new ExistingValue(instancePut.value());
+ if (definition.isFinal()) {
+ assert method.isInstanceInitializer() || verifyWasInstanceInitializer();
+ activeFieldValues.putFinalInstanceField(fieldAndObject, value);
+ } else {
+ activeFieldValues.putNonFinalInstanceField(fieldAndObject, value);
+ }
} else if (instruction.isStaticGet()) {
StaticGet staticGet = instruction.asStaticGet();
if (staticGet.outValue().hasLocalInfo()) {
continue;
}
- if (activeStaticFieldValues.containsKey(field)) {
- FieldValue replacement = activeStaticFieldValues.get(field);
+ FieldValue replacement = activeFieldValues.getStaticFieldValue(field);
+ if (replacement != null) {
replacement.eliminateRedundantRead(it, staticGet);
} else {
// A field get on a different class can cause <clinit> to run and change static
// field values.
- killActiveFields(staticGet);
- activeStaticFieldValues.put(field, new ExistingValue(staticGet.value()));
+ killNonFinalActiveFields(staticGet);
+ activeFieldValues.putNonFinalStaticField(field, new ExistingValue(staticGet.value()));
}
} else if (instruction.isStaticPut()) {
StaticPut staticPut = instruction.asStaticPut();
// A field put on a different class can cause <clinit> to run and change static
// field values.
- killActiveFields(staticPut);
- activeStaticFieldValues.put(field, new ExistingValue(staticPut.value()));
+ killNonFinalActiveFields(staticPut);
+ ExistingValue value = new ExistingValue(staticPut.value());
+ if (definition.isFinal()) {
+ assert method.isClassInitializer();
+ activeFieldValues.putFinalStaticField(field, value);
+ } else {
+ activeFieldValues.putNonFinalStaticField(field, value);
+ }
}
} else if (instruction.isInitClass()) {
InitClass initClass = instruction.asInitClass();
@@ -234,12 +236,12 @@
}
} else if (instruction.isMonitor()) {
if (instruction.asMonitor().isEnter()) {
- killAllActiveFields();
+ killAllNonFinalActiveFields();
}
} else if (instruction.isInvokeDirect()) {
handleInvokeDirect(instruction.asInvokeDirect());
} else if (instruction.isInvokeMethod() || instruction.isInvokeCustom()) {
- killAllActiveFields();
+ killAllNonFinalActiveFields();
} else if (instruction.isNewInstance()) {
NewInstance newInstance = instruction.asNewInstance();
if (newInstance.clazz.classInitializationMayHaveSideEffects(
@@ -247,7 +249,7 @@
// Types that are a super type of `context` are guaranteed to be initialized already.
type -> appView.isSubtype(context, type).isTrue(),
Sets.newIdentityHashSet())) {
- killAllActiveFields();
+ killAllNonFinalActiveFields();
}
} else {
// If the current instruction could trigger a method invocation, it could also cause field
@@ -294,22 +296,33 @@
assert code.isConsistentSSA();
}
+ private boolean verifyWasInstanceInitializer() {
+ VerticallyMergedClasses verticallyMergedClasses = appView.verticallyMergedClasses();
+ assert verticallyMergedClasses != null;
+ assert verticallyMergedClasses.isTarget(method.holder());
+ assert appView
+ .dexItemFactory()
+ .isConstructor(appView.graphLense().getOriginalMethodSignature(method.method));
+ assert method.getOptimizationInfo().forceInline();
+ return true;
+ }
+
private void handleInvokeDirect(InvokeDirect invoke) {
if (!appView.enableWholeProgramOptimizations()) {
- killAllActiveFields();
+ killAllNonFinalActiveFields();
return;
}
DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, method.holder());
if (singleTarget == null || !singleTarget.isInstanceInitializer()) {
- killAllActiveFields();
+ killAllNonFinalActiveFields();
return;
}
InstanceInitializerInfo instanceInitializerInfo =
singleTarget.getOptimizationInfo().getInstanceInitializerInfo();
if (instanceInitializerInfo.mayHaveOtherSideEffectsThanInstanceFieldAssignments()) {
- killAllActiveFields();
+ killAllNonFinalActiveFields();
}
InstanceFieldInitializationInfoCollection fieldInitializationInfos =
@@ -325,13 +338,14 @@
invoke.getArgument(info.asArgumentInitializationInfo().getArgumentIndex());
Value object = invoke.getReceiver().getAliasedValue();
FieldAndObject fieldAndObject = new FieldAndObject(field.field, object);
- activeInstanceFieldValues.put(fieldAndObject, new ExistingValue(value));
+ activeFieldValues.putNonFinalInstanceField(fieldAndObject, new ExistingValue(value));
} else if (info.isSingleValue()) {
SingleValue value = info.asSingleValue();
if (value.isMaterializableInContext(appView, method.holder())) {
Value object = invoke.getReceiver().getAliasedValue();
FieldAndObject fieldAndObject = new FieldAndObject(field.field, object);
- activeInstanceFieldValues.put(fieldAndObject, new MaterializableValue(value));
+ activeFieldValues.putNonFinalInstanceField(
+ fieldAndObject, new MaterializableValue(value));
}
} else {
assert info.isTypeInitializationInfo();
@@ -357,44 +371,36 @@
assert !activeInitializedClassesAtEntry.containsKey(successor);
activeInitializedClassesAtEntry.put(
successor, SetUtils.newIdentityHashSet(activeInitializedClasses));
- assert !activeInstanceFieldsAtEntry.containsKey(successor);
- activeInstanceFieldsAtEntry.put(successor, new HashMap<>(activeInstanceFieldValues));
- assert !activeStaticFieldsAtEntry.containsKey(successor);
- activeStaticFieldsAtEntry.put(successor, new IdentityHashMap<>(activeStaticFieldValues));
+ assert !activeFieldsAtEntry.containsKey(successor);
+ activeFieldsAtEntry.put(successor, new FieldValuesMap(activeFieldValues));
}
}
}
- private void killAllActiveFields() {
- activeInstanceFieldValues.clear();
- activeStaticFieldValues.clear();
+ private void killAllNonFinalActiveFields() {
+ activeFieldValues.clearNonFinalInstanceFields();
+ activeFieldValues.clearNonFinalStaticFields();
}
- private void killActiveFields(FieldInstruction instruction) {
+ private void killNonFinalActiveFields(FieldInstruction instruction) {
DexField field = instruction.getField();
if (instruction.isInstancePut()) {
// Remove all the field/object pairs that refer to this field to make sure
// that we are conservative.
- List<FieldAndObject> keysToRemove = new ArrayList<>();
- for (FieldAndObject key : activeInstanceFieldValues.keySet()) {
- if (key.field == field) {
- keysToRemove.add(key);
- }
- }
- keysToRemove.forEach(activeInstanceFieldValues::remove);
+ activeFieldValues.removeNonFinalInstanceFields(field);
} else if (instruction.isStaticPut()) {
if (field.holder != code.method.holder()) {
// Accessing a static field on a different object could cause <clinit> to run which
// could modify any static field on any other object.
- activeStaticFieldValues.clear();
+ activeFieldValues.clearNonFinalStaticFields();
} else {
- activeStaticFieldValues.remove(field);
+ activeFieldValues.removeNonFinalStaticField(field);
}
} else if (instruction.isStaticGet()) {
if (field.holder != code.method.holder()) {
// Accessing a static field on a different object could cause <clinit> to run which
// could modify any static field on any other object.
- activeStaticFieldValues.clear();
+ activeFieldValues.clearNonFinalStaticFields();
}
} else if (instruction.isInstanceGet()) {
throw new Unreachable();
@@ -410,13 +416,99 @@
if (instruction.isInstanceGet()) {
Value object = instruction.asInstanceGet().object().getAliasedValue();
FieldAndObject fieldAndObject = new FieldAndObject(field, object);
- activeInstanceFieldValues.remove(fieldAndObject);
+ activeFieldValues.removeInstanceField(fieldAndObject);
} else if (instruction.isStaticGet()) {
- activeStaticFieldValues.remove(field);
+ activeFieldValues.removeStaticField(field);
}
}
private void killActiveInitializedClassesForExceptionalExit(InitClass instruction) {
activeInitializedClasses.remove(instruction.getClassValue());
}
+
+ static class FieldValuesMap {
+
+ private final Map<FieldAndObject, FieldValue> finalInstanceFieldValues = new HashMap<>();
+
+ private final Map<DexField, FieldValue> finalStaticFieldValues = new IdentityHashMap<>();
+
+ private final Map<FieldAndObject, FieldValue> nonFinalInstanceFieldValues = new HashMap<>();
+
+ private final Map<DexField, FieldValue> nonFinalStaticFieldValues = new IdentityHashMap<>();
+
+ public FieldValuesMap() {}
+
+ public FieldValuesMap(FieldValuesMap map) {
+ finalInstanceFieldValues.putAll(map.finalInstanceFieldValues);
+ finalStaticFieldValues.putAll(map.finalStaticFieldValues);
+ nonFinalInstanceFieldValues.putAll(map.nonFinalInstanceFieldValues);
+ nonFinalStaticFieldValues.putAll(map.nonFinalStaticFieldValues);
+ }
+
+ public void clearNonFinalInstanceFields() {
+ nonFinalInstanceFieldValues.clear();
+ }
+
+ public void clearNonFinalStaticFields() {
+ nonFinalStaticFieldValues.clear();
+ }
+
+ public FieldValue getInstanceFieldValue(FieldAndObject field) {
+ FieldValue value = nonFinalInstanceFieldValues.get(field);
+ return value != null ? value : finalInstanceFieldValues.get(field);
+ }
+
+ public FieldValue getStaticFieldValue(DexField field) {
+ FieldValue value = nonFinalStaticFieldValues.get(field);
+ return value != null ? value : finalStaticFieldValues.get(field);
+ }
+
+ public void removeInstanceField(FieldAndObject field) {
+ removeFinalInstanceField(field);
+ removeNonFinalInstanceField(field);
+ }
+
+ public void removeFinalInstanceField(FieldAndObject field) {
+ finalInstanceFieldValues.remove(field);
+ }
+
+ public void removeNonFinalInstanceField(FieldAndObject field) {
+ nonFinalInstanceFieldValues.remove(field);
+ }
+
+ public void removeNonFinalInstanceFields(DexField field) {
+ nonFinalInstanceFieldValues.keySet().removeIf(key -> key.field == field);
+ }
+
+ public void removeStaticField(DexField field) {
+ removeFinalStaticField(field);
+ removeNonFinalStaticField(field);
+ }
+
+ public void removeFinalStaticField(DexField field) {
+ finalStaticFieldValues.remove(field);
+ }
+
+ public void removeNonFinalStaticField(DexField field) {
+ nonFinalStaticFieldValues.remove(field);
+ }
+
+ public void putFinalInstanceField(FieldAndObject field, FieldValue value) {
+ finalInstanceFieldValues.put(field, value);
+ }
+
+ public void putFinalStaticField(DexField field, FieldValue value) {
+ finalStaticFieldValues.put(field, value);
+ }
+
+ public void putNonFinalInstanceField(FieldAndObject field, FieldValue value) {
+ assert !finalInstanceFieldValues.containsKey(field);
+ nonFinalInstanceFieldValues.put(field, value);
+ }
+
+ public void putNonFinalStaticField(DexField field, FieldValue value) {
+ assert !nonFinalStaticFieldValues.containsKey(field);
+ nonFinalStaticFieldValues.put(field, value);
+ }
+ }
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/RedundantFinalInstanceFieldLoadAfterStoreTest.java b/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/RedundantFinalInstanceFieldLoadAfterStoreTest.java
index 4958163..329e574 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/RedundantFinalInstanceFieldLoadAfterStoreTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/RedundantFinalInstanceFieldLoadAfterStoreTest.java
@@ -61,9 +61,8 @@
MethodSubject initMethodSubject = aClassSubject.init();
assertThat(initMethodSubject, isPresent());
- // TODO(b/152196923): Should be 0.
assertEquals(
- 2,
+ 0,
countInstanceGetInstructions(
initMethodSubject.asFoundMethodSubject(), fFieldSubject.asFoundFieldSubject()));
@@ -119,22 +118,26 @@
System.out.println(f); // Not redundant, since `f` is not guaranteed to be initialized.
}
+ @NeverInline
void fork() {
new Thread(this::m).start();
}
+ @NeverInline
void killNonFinalActiveFields() {
if (System.currentTimeMillis() < 0) {
System.out.println(this);
}
}
+ @NeverInline
void waitUntilInitialized() {
while (!initialized) {
Thread.yield();
}
}
+ @NeverInline
void waitUntilRead() {
while (!read) {
Thread.yield();
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/RedundantFinalStaticFieldLoadAfterStoreTest.java b/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/RedundantFinalStaticFieldLoadAfterStoreTest.java
index 8f3ed33..fe3d79a 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/RedundantFinalStaticFieldLoadAfterStoreTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/RedundantFinalStaticFieldLoadAfterStoreTest.java
@@ -58,9 +58,8 @@
MethodSubject initMethodSubject = aClassSubject.clinit();
assertThat(initMethodSubject, isPresent());
- // TODO(b/152196923): Should be 0.
assertEquals(
- 2,
+ 0,
countStaticGetInstructions(
initMethodSubject.asFoundMethodSubject(), fFieldSubject.asFoundFieldSubject()));
diff --git a/src/test/java/com/android/tools/r8/rewrite/enums/EnumOptimizationTest.java b/src/test/java/com/android/tools/r8/rewrite/enums/EnumOptimizationTest.java
index 91f0c4d..ae4947a 100644
--- a/src/test/java/com/android/tools/r8/rewrite/enums/EnumOptimizationTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/enums/EnumOptimizationTest.java
@@ -85,6 +85,7 @@
assertOrdinalReplacedWithConst(clazz.uniqueMethodWithName("inSwitch"), 11);
assertOrdinalReplacedWithConst(clazz.uniqueMethodWithName("differentTypeStaticField"), 1);
assertOrdinalReplacedWithConst(clazz.uniqueMethodWithName("nonStaticGet"), 1);
+ assertOrdinalReplacedWithConst(clazz.uniqueMethodWithName("nonValueStaticField"), 1);
} else {
assertOrdinalWasNotReplaced(clazz.uniqueMethodWithName("simple"));
assertOrdinalWasNotReplaced(clazz.uniqueMethodWithName("local"));
@@ -96,7 +97,6 @@
}
assertOrdinalWasNotReplaced(clazz.uniqueMethodWithName("libraryType"));
- assertOrdinalWasNotReplaced(clazz.uniqueMethodWithName("nonValueStaticField"));
assertOrdinalWasNotReplaced(clazz.uniqueMethodWithName("phi"));
}
@@ -129,6 +129,7 @@
assertNameReplacedWithConst(clazz.uniqueMethodWithName("inlined"), "TWO");
assertNameReplacedWithConst(clazz.uniqueMethodWithName("differentTypeStaticField"), "DOWN");
assertNameReplacedWithConst(clazz.uniqueMethodWithName("nonStaticGet"), "TWO");
+ assertNameReplacedWithConst(clazz.uniqueMethodWithName("nonValueStaticField"), "TWO");
} else {
assertNameWasNotReplaced(clazz.uniqueMethodWithName("simple"));
assertNameWasNotReplaced(clazz.uniqueMethodWithName("local"));
@@ -141,7 +142,6 @@
// TODO(jakew) this should be allowed!
assertNameWasNotReplaced(clazz.uniqueMethodWithName("libraryType"));
- assertNameWasNotReplaced(clazz.uniqueMethodWithName("nonValueStaticField"));
assertNameWasNotReplaced(clazz.uniqueMethodWithName("phi"));
}
diff --git a/src/test/java/com/android/tools/r8/rewrite/enums/Names.java b/src/test/java/com/android/tools/r8/rewrite/enums/Names.java
index 2ecc9eb..5dd5ec0 100644
--- a/src/test/java/com/android/tools/r8/rewrite/enums/Names.java
+++ b/src/test/java/com/android/tools/r8/rewrite/enums/Names.java
@@ -63,6 +63,7 @@
return Number.DOWN.name();
}
+ @AssumeMayHaveSideEffects
@NeverInline
private static String nonValueStaticField() {
return Number.DEFAULT.name();
diff --git a/src/test/java/com/android/tools/r8/rewrite/enums/Ordinals.java b/src/test/java/com/android/tools/r8/rewrite/enums/Ordinals.java
index 534b16c..e1eee2c 100644
--- a/src/test/java/com/android/tools/r8/rewrite/enums/Ordinals.java
+++ b/src/test/java/com/android/tools/r8/rewrite/enums/Ordinals.java
@@ -73,6 +73,7 @@
return Number.DOWN.ordinal();
}
+ @AssumeMayHaveSideEffects
@NeverInline
private static long nonValueStaticField() {
return Number.DEFAULT.ordinal();
diff --git a/src/test/java/com/android/tools/r8/shaking/keptgraph/WhyAreYouKeepingAllTest.java b/src/test/java/com/android/tools/r8/shaking/keptgraph/WhyAreYouKeepingAllTest.java
index 2387e7c..292cb21 100644
--- a/src/test/java/com/android/tools/r8/shaking/keptgraph/WhyAreYouKeepingAllTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/keptgraph/WhyAreYouKeepingAllTest.java
@@ -46,7 +46,6 @@
@Test
public void test() throws Throwable {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
testForR8(Backend.CF)
.addProgramFiles(ToolHelper.R8_WITH_RELOCATED_DEPS_JAR)
.addKeepRuleFiles(MAIN_KEEP)