Revert "Stop relying on $VALUES name"
This reverts commit dff97021b297a11433530c80a1416b53c07fe9bf.
Reason for revert: b/178496619
Change-Id: I22c53a1cedc4c7a32c283a7919581fc1f58dcdd9
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 30ac84b..e775ac6 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -293,11 +293,6 @@
createString(Constants.TEMPORARY_INSTANCE_INITIALIZER_PREFIX);
public final DexString thisName = createString("this");
-
- // As much as possible, R8 should rely on the content of the static enum field, using
- // enumMembers.isValuesFieldCandidate or checking the object state in the optimization info.
- // The field name is unrealiable since the filed can be minified prior to this compilation.
- // We keep enumValuesFieldName as a heuristic only.
public final DexString enumValuesFieldName = createString("$VALUES");
public final DexString enabledFieldName = createString("ENABLED");
@@ -1403,17 +1398,21 @@
return field == nameField || field == ordinalField;
}
- public boolean isEnumField(DexEncodedField staticField, DexType enumType) {
- assert staticField.isStatic();
- return staticField.getType() == enumType && staticField.isEnum() && staticField.isFinal();
+ public boolean isValuesMethod(DexMethod method, DexClass enumClass) {
+ assert enumClass.isEnum();
+ return method.holder == enumClass.type
+ && method.proto.returnType == enumClass.type.toArrayType(1, DexItemFactory.this)
+ && method.proto.parameters.size() == 0
+ && method.name == valuesMethodName;
}
- public boolean isValuesFieldCandidate(DexEncodedField staticField, DexType enumType) {
- assert staticField.isStatic();
- return staticField.getType().isArrayType()
- && staticField.getType().toArrayElementType(DexItemFactory.this) == enumType
- && staticField.isSynthetic()
- && staticField.isFinal();
+ public boolean isValueOfMethod(DexMethod method, DexClass enumClass) {
+ assert enumClass.isEnum();
+ return method.holder == enumClass.type
+ && method.proto.returnType == enumClass.type
+ && method.proto.parameters.size() == 1
+ && method.proto.parameters.values[0] == stringType
+ && method.name == valueOfMethodName;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
index fc01602..281215f 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
@@ -12,6 +12,9 @@
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexValue;
import com.android.tools.r8.graph.DexValue.DexValueNull;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
@@ -23,7 +26,6 @@
import com.android.tools.r8.ir.analysis.value.NullOrAbstractValue;
import com.android.tools.r8.ir.analysis.value.ObjectState;
import com.android.tools.r8.ir.analysis.value.SingleFieldValue;
-import com.android.tools.r8.ir.analysis.value.UnknownValue;
import com.android.tools.r8.ir.code.ArrayPut;
import com.android.tools.r8.ir.code.FieldInstruction;
import com.android.tools.r8.ir.code.IRCode;
@@ -38,13 +40,10 @@
import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoCollection;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.Timing;
-import java.util.IdentityHashMap;
-import java.util.Map;
public class StaticFieldValueAnalysis extends FieldValueAnalysis {
private final StaticFieldValues.Builder builder;
- private final Map<Value, AbstractValue> computedValues = new IdentityHashMap<>();
private StaticFieldValueAnalysis(
AppView<AppInfoWithLiveness> appView, IRCode code, OptimizationFeedback feedback) {
@@ -64,7 +63,7 @@
timing.begin("Analyze class initializer");
StaticFieldValues result =
new StaticFieldValueAnalysis(appView.withLiveness(), code, feedback)
- .analyze(classInitializerDefaultsResult);
+ .analyze(classInitializerDefaultsResult, code.context().getHolderType());
timing.end();
return result;
}
@@ -79,7 +78,8 @@
return this;
}
- StaticFieldValues analyze(ClassInitializerDefaultsResult classInitializerDefaultsResult) {
+ StaticFieldValues analyze(
+ ClassInitializerDefaultsResult classInitializerDefaultsResult, DexType holderType) {
computeFieldOptimizationInfo(classInitializerDefaultsResult);
return builder.build();
}
@@ -218,41 +218,16 @@
return null;
}
assert !value.hasAliasedValue();
- if (value.isPhi()) {
- return null;
- }
- if (value.definition.isNewArrayEmpty()) {
+ if (isEnumValuesArray(value)) {
return computeSingleEnumFieldValueForValuesArray(value);
}
- if (value.definition.isNewInstance()) {
- return computeSingleEnumFieldValueForInstance(value);
- }
- return null;
+ return computeSingleEnumFieldValueForInstance(value);
}
private SingleFieldValue computeSingleEnumFieldValueForValuesArray(Value value) {
- if (!value.definition.isNewArrayEmpty()) {
+ if (!value.isDefinedByInstructionSatisfying(Instruction::isNewArrayEmpty)) {
return null;
}
- AbstractValue valuesValue = computedValues.get(value);
- if (valuesValue != null) {
- // This implicitely answers null if the value could not get computed.
- if (valuesValue.isSingleFieldValue()) {
- SingleFieldValue fieldValue = valuesValue.asSingleFieldValue();
- if (fieldValue.getState().isEnumValuesObjectState()) {
- return fieldValue;
- }
- }
- return null;
- }
- SingleFieldValue singleFieldValue = internalComputeSingleEnumFieldValueForValuesArray(value);
- computedValues.put(
- value, singleFieldValue == null ? UnknownValue.getInstance() : singleFieldValue);
- return singleFieldValue;
- }
-
- private SingleFieldValue internalComputeSingleEnumFieldValueForValuesArray(Value value) {
- assert value.isDefinedByInstructionSatisfying(Instruction::isNewArrayEmpty);
NewArrayEmpty newArrayEmpty = value.definition.asNewArrayEmpty();
if (newArrayEmpty.type.toBaseType(appView.dexItemFactory()) != context.getHolder().type) {
@@ -292,9 +267,7 @@
// We need the state of all fields for the analysis to be valuable.
return null;
}
- if (!valuesArrayIndexMatchesOrdinal(index, objectState)) {
- return null;
- }
+ assert verifyValuesArrayIndexMatchesOrdinal(index, objectState);
if (valuesState[index] != null) {
return null;
}
@@ -353,25 +326,24 @@
return ObjectState.empty();
}
- private boolean valuesArrayIndexMatchesOrdinal(int ordinal, ObjectState objectState) {
+ private boolean verifyValuesArrayIndexMatchesOrdinal(int ordinal, ObjectState objectState) {
DexEncodedField ordinalField =
appView
.appInfo()
.resolveField(appView.dexItemFactory().enumMembers.ordinalField, context)
.getResolvedField();
- if (ordinalField == null) {
- return false;
- }
+ assert ordinalField != null;
AbstractValue ordinalState = objectState.getAbstractFieldValue(ordinalField);
- if (ordinalState == null || !ordinalState.isSingleNumberValue()) {
- return false;
- }
- int intValue = ordinalState.asSingleNumberValue().getIntValue();
- return intValue == ordinal;
+ assert ordinalState != null;
+ assert ordinalState.isSingleNumberValue();
+ assert ordinalState.asSingleNumberValue().getIntValue() == ordinal;
+ return true;
}
private SingleFieldValue computeSingleEnumFieldValueForInstance(Value value) {
- assert value.isDefinedByInstructionSatisfying(Instruction::isNewInstance);
+ if (!value.isDefinedByInstructionSatisfying(Instruction::isNewInstance)) {
+ return null;
+ }
NewInstance newInstance = value.definition.asNewInstance();
// Some enums have direct subclasses, and the subclass is instantiated here.
@@ -487,7 +459,30 @@
}
private boolean isEnumValuesArray(Value value) {
- SingleFieldValue singleFieldValue = computeSingleEnumFieldValueForValuesArray(value);
- return singleFieldValue != null && singleFieldValue.getState().isEnumValuesObjectState();
+ assert context.getHolder().isEnum();
+ DexItemFactory dexItemFactory = appView.dexItemFactory();
+ DexField valuesField =
+ dexItemFactory.createField(
+ context.getHolderType(),
+ context.getHolderType().toArrayType(1, dexItemFactory),
+ dexItemFactory.enumValuesFieldName);
+
+ Value root = value.getAliasedValue();
+ if (root.isPhi()) {
+ return false;
+ }
+
+ Instruction definition = root.definition;
+ if (definition.isNewArrayEmpty()) {
+ for (Instruction user : root.aliasedUsers()) {
+ if (user.isStaticPut() && user.asStaticPut().getField() == valuesField) {
+ return true;
+ }
+ }
+ } else if (definition.isStaticGet()) {
+ return definition.asStaticGet().getField() == valuesField;
+ }
+
+ return false;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValues.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValues.java
index 65e1ea3..b2dcdcd 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValues.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValues.java
@@ -37,10 +37,17 @@
// All the abstract values stored here may match a pinned field, using them requires therefore
// to check the field is not pinned or prove it is no longer pinned.
public static class EnumStaticFieldValues extends StaticFieldValues {
- private final ImmutableMap<DexField, ObjectState> enumAbstractValues;
+ private final ImmutableMap<DexField, AbstractValue> enumAbstractValues;
+ private final DexField valuesField;
+ private final AbstractValue valuesAbstractValue;
- public EnumStaticFieldValues(ImmutableMap<DexField, ObjectState> enumAbstractValues) {
+ public EnumStaticFieldValues(
+ ImmutableMap<DexField, AbstractValue> enumAbstractValues,
+ DexField valuesField,
+ AbstractValue valuesAbstractValue) {
this.enumAbstractValues = enumAbstractValues;
+ this.valuesField = valuesField;
+ this.valuesAbstractValue = valuesAbstractValue;
}
static StaticFieldValues.Builder builder() {
@@ -48,38 +55,33 @@
}
public static class Builder extends StaticFieldValues.Builder {
- private final ImmutableMap.Builder<DexField, ObjectState> enumObjectStateBuilder =
+ private final ImmutableMap.Builder<DexField, AbstractValue> enumAbstractValuesBuilder =
ImmutableMap.builder();
- private AbstractValue valuesCandidateAbstractValue;
+ private DexField valuesFields;
+ private AbstractValue valuesAbstractValue;
Builder() {}
@Override
public void recordStaticField(
DexEncodedField staticField, AbstractValue value, DexItemFactory factory) {
- if (factory.enumMembers.isValuesFieldCandidate(staticField, staticField.getHolderType())) {
- if (value.isSingleFieldValue()
- && value.asSingleFieldValue().getState().isEnumValuesObjectState()) {
- assert valuesCandidateAbstractValue == null
- || valuesCandidateAbstractValue.equals(value);
- valuesCandidateAbstractValue = value;
- enumObjectStateBuilder.put(staticField.field, value.asSingleFieldValue().getState());
- }
- } else if (factory.enumMembers.isEnumField(staticField, staticField.getHolderType())) {
- if (value.isSingleFieldValue() && !value.asSingleFieldValue().getState().isEmpty()) {
- enumObjectStateBuilder.put(staticField.field, value.asSingleFieldValue().getState());
- }
+ // TODO(b/166532388): Stop relying on the values name.
+ if (staticField.getName() == factory.enumValuesFieldName) {
+ valuesFields = staticField.field;
+ valuesAbstractValue = value;
+ } else if (staticField.isEnum()) {
+ enumAbstractValuesBuilder.put(staticField.field, value);
}
}
@Override
public StaticFieldValues build() {
- ImmutableMap<DexField, ObjectState> enumAbstractValues = enumObjectStateBuilder.build();
- if (enumAbstractValues.isEmpty()) {
+ ImmutableMap<DexField, AbstractValue> enumAbstractValues =
+ enumAbstractValuesBuilder.build();
+ if (valuesAbstractValue == null && enumAbstractValues.isEmpty()) {
return EmptyStaticValues.getInstance();
}
- assert enumAbstractValues.values().stream().noneMatch(ObjectState::isEmpty);
- return new EnumStaticFieldValues(enumAbstractValues);
+ return new EnumStaticFieldValues(enumAbstractValues, valuesFields, valuesAbstractValue);
}
}
@@ -94,7 +96,20 @@
}
public ObjectState getObjectStateForPossiblyPinnedField(DexField field) {
- return enumAbstractValues.get(field);
+ AbstractValue fieldValue = enumAbstractValues.get(field);
+ if (fieldValue == null || fieldValue.isZero()) {
+ return null;
+ }
+ if (fieldValue.isSingleFieldValue()) {
+ return fieldValue.asSingleFieldValue().getState();
+ }
+ assert fieldValue.isUnknown();
+ return ObjectState.empty();
+ }
+
+ public AbstractValue getValuesAbstractValueForPossiblyPinnedField(DexField field) {
+ assert valuesField == field || valuesAbstractValue == null;
+ return valuesAbstractValue;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
index cf0ecb5..166022e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
@@ -491,36 +491,34 @@
// Step 1: We iterate over the field to find direct enum instance information and the values
// fields.
for (DexEncodedField staticField : enumClass.staticFields()) {
- if (factory.enumMembers.isEnumField(staticField, enumClass.type)) {
+ if (EnumUnboxingCandidateAnalysis.isEnumField(staticField, enumClass.type)) {
ObjectState enumState =
enumStaticFieldValues.getObjectStateForPossiblyPinnedField(staticField.field);
- if (enumState == null) {
- if (!isFinalFieldInitialized(staticField, enumClass)) {
- continue;
+ if (enumState != null) {
+ OptionalInt optionalOrdinal = getOrdinal(enumState);
+ if (!optionalOrdinal.isPresent()) {
+ return null;
}
- // Tracking the content of the field yielded either an empty object state, or something
- // incoherent. We bail out.
+ int ordinal = optionalOrdinal.getAsInt();
+ unboxedValues.put(staticField.field, ordinalToUnboxedInt(ordinal));
+ ordinalToObjectState.put(ordinal, enumState);
+ }
+ } else if (EnumUnboxingCandidateAnalysis.matchesValuesField(
+ staticField, enumClass.type, factory)) {
+ AbstractValue valuesValue =
+ enumStaticFieldValues.getValuesAbstractValueForPossiblyPinnedField(staticField.field);
+ if (valuesValue == null || valuesValue.isZero()) {
+ // Unused field
+ continue;
+ }
+ if (valuesValue.isUnknown()) {
return null;
}
- OptionalInt optionalOrdinal = getOrdinal(enumState);
- if (!optionalOrdinal.isPresent()) {
+ assert valuesValue.isSingleFieldValue();
+ ObjectState valuesState = valuesValue.asSingleFieldValue().getState();
+ if (!valuesState.isEnumValuesObjectState()) {
return null;
}
- int ordinal = optionalOrdinal.getAsInt();
- unboxedValues.put(staticField.field, ordinalToUnboxedInt(ordinal));
- ordinalToObjectState.put(ordinal, enumState);
- } else if (factory.enumMembers.isValuesFieldCandidate(staticField, enumClass.type)) {
- ObjectState valuesState =
- enumStaticFieldValues.getObjectStateForPossiblyPinnedField(staticField.field);
- if (valuesState == null) {
- if (!isFinalFieldInitialized(staticField, enumClass)) {
- continue;
- }
- // We could not track the content of that field, and the field could be a values field.
- // We conservatively bail out.
- return null;
- }
- assert valuesState.isEnumValuesObjectState();
assert valuesContents == null
|| valuesContents.equals(valuesState.asEnumValuesObjectState());
valuesContents = valuesState.asEnumValuesObjectState();
@@ -566,13 +564,6 @@
valuesContents == null ? EnumData.INVALID_VALUES_SIZE : valuesContents.getEnumValuesSize());
}
- private boolean isFinalFieldInitialized(DexEncodedField staticField, DexProgramClass enumClass) {
- assert staticField.isFinal();
- return appView
- .appInfo()
- .isFieldOnlyWrittenInMethodIgnoringPinning(staticField, enumClass.getClassInitializer());
- }
-
private EnumInstanceFieldData computeEnumFieldData(
DexField instanceField,
DexProgramClass enumClass,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
index 4c7581e..6c20c4f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
@@ -434,7 +434,7 @@
return factory.createField(
relocator.getNewMemberLocationFor(enumType),
factory.intArrayType,
- "$$values$$field$" + compatibleName(enumType));
+ factory.enumValuesFieldName + "$field$" + compatibleName(enumType));
}
private DexEncodedField computeValuesEncodedField(DexField field) {
@@ -451,7 +451,7 @@
return factory.createMethod(
relocator.getNewMemberLocationFor(enumType),
factory.createProto(factory.intArrayType),
- "$$values$$method$" + compatibleName(enumType));
+ factory.enumValuesFieldName + "$method$" + compatibleName(enumType));
}
private DexEncodedMethod computeValuesEncodedMethod(
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/EnumToStringLibTest.java b/src/test/java/com/android/tools/r8/enumunboxing/EnumToStringLibTest.java
index b21cbd0..89395ef 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/EnumToStringLibTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/EnumToStringLibTest.java
@@ -97,6 +97,13 @@
return testForR8(Backend.CF)
.addProgramClasses(ToStringLib.class, ToStringLib.LibEnum.class)
.addKeepRules(enumKeepRules.getKeepRules())
+ // TODO(b/160535629): Work-around on some optimizations relying on $VALUES name.
+ .addKeepRules(
+ "-keep enum "
+ + ToStringLib.LibEnum.class.getName()
+ + " { static "
+ + ToStringLib.LibEnum.class.getName()
+ + "[] $VALUES; }")
.addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
.addKeepMethodRules(
Reference.methodFromMethod(