Remove EnumValueInfoMap uses in enum unboxer

Bug: 172528424
Bug: 160939354
Change-Id: Ieace568931216a114908549e28d78378727d6ea4
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 5521ef9..dc81ff4 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -33,7 +33,6 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DirectMappedDexApplication;
 import com.android.tools.r8.graph.DirectMappedDexApplication.Builder;
-import com.android.tools.r8.graph.EnumValueInfoMapCollection;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.GraphLens.NestedGraphLens;
 import com.android.tools.r8.graph.InitClassLens;
@@ -626,10 +625,6 @@
       // TODO: we should avoid removing liveness.
       Set<DexType> prunedTypes = appView.withLiveness().appInfo().getPrunedTypes();
 
-      // TODO: move to appview.
-      EnumValueInfoMapCollection enumValueInfoMapCollection =
-          appViewWithLiveness.appInfo().getEnumValueInfoMapCollection();
-
       timing.begin("Create IR");
       CfgPrinter printer = options.printCfg ? new CfgPrinter() : null;
       try {
@@ -729,13 +724,11 @@
                   missingClasses,
                   prunedTypes);
           appView.setAppInfo(
-              enqueuer
-                  .traceApplication(
-                      appView.rootSet(),
-                      options.getProguardConfiguration().getDontWarnPatterns(),
-                      executorService,
-                      timing)
-                  .withEnumValueInfoMaps(enumValueInfoMapCollection));
+              enqueuer.traceApplication(
+                  appView.rootSet(),
+                  options.getProguardConfiguration().getDontWarnPatterns(),
+                  executorService,
+                  timing));
           // Rerunning the enqueuer should not give rise to any method rewritings.
           assert enqueuer.buildGraphLens() == null;
           appView.withGeneratedMessageLiteBuilderShrinker(
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index dd13c7d..8e0800b 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -22,6 +22,7 @@
 import com.android.tools.r8.ir.conversion.MethodProcessingId;
 import com.android.tools.r8.ir.desugar.PrefixRewritingMapper;
 import com.android.tools.r8.ir.optimize.CallSiteOptimizationInfoPropagator;
+import com.android.tools.r8.ir.optimize.enums.EnumDataMap;
 import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoFactory;
 import com.android.tools.r8.ir.optimize.library.LibraryMemberOptimizer;
 import com.android.tools.r8.ir.optimize.library.LibraryMethodSideEffectModelCollection;
@@ -85,7 +86,7 @@
   private HorizontallyMergedClasses horizontallyMergedClasses;
   private StaticallyMergedClasses staticallyMergedClasses;
   private VerticallyMergedClasses verticallyMergedClasses;
-  private EnumValueInfoMapCollection unboxedEnums = EnumValueInfoMapCollection.empty();
+  private EnumDataMap unboxedEnums = EnumDataMap.empty();
   // TODO(b/169115389): Remove
   private Set<DexMethod> cfByteCodePassThrough = ImmutableSet.of();
 
@@ -510,18 +511,18 @@
     testing().verticallyMergedClassesConsumer.accept(dexItemFactory(), verticallyMergedClasses);
   }
 
-  public EnumValueInfoMapCollection unboxedEnums() {
+  public EnumDataMap unboxedEnums() {
     return unboxedEnums;
   }
 
-  public void setUnboxedEnums(EnumValueInfoMapCollection unboxedEnums) {
+  public void setUnboxedEnums(EnumDataMap unboxedEnums) {
     assert this.unboxedEnums.isEmpty();
     this.unboxedEnums = unboxedEnums;
     testing().unboxedEnumsConsumer.accept(dexItemFactory(), unboxedEnums);
   }
 
   public boolean validateUnboxedEnumsHaveBeenPruned() {
-    for (DexType unboxedEnum : unboxedEnums.enumSet()) {
+    for (DexType unboxedEnum : unboxedEnums.getUnboxedEnums()) {
       assert appInfo.definitionForWithoutExistenceAssert(unboxedEnum) == null
           : "Enum " + unboxedEnum + " has been unboxed but is still in the program.";
       assert appInfo().withLiveness().wasPruned(unboxedEnum)
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 8a02e12..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
@@ -95,41 +95,22 @@
       return this;
     }
 
-    // We need to access the enum instance object state to figure out if it contains known constant
-    // field values. The enum instance may be accessed in two ways, directly through the enum
-    // static field, or through the enum $VALUES field. If none of them are kept, the instance is
-    // effectively unused. The object state may be stored in the enum static field optimization
-    // info, if kept, or in the $VALUES optimization info, if kept.
-    // If the enum instance is unused, this method answers null.
-    public ObjectState getObjectStateForPossiblyPinnedEnumInstance(DexField field, int ordinal) {
-
-      // Attempt 1: Get object state from the instance field's optimization info.
+    public ObjectState getObjectStateForPossiblyPinnedField(DexField field) {
       AbstractValue fieldValue = enumAbstractValues.get(field);
-      if (fieldValue != null) {
-        if (fieldValue.isSingleFieldValue()) {
-          return fieldValue.asSingleFieldValue().getState();
-        }
-        if (fieldValue.isUnknown()) {
-          return ObjectState.empty();
-        }
-        assert fieldValue.isZero();
-      }
-
-      // Attempt 2: Get object state from the values field's optimization info.
-      if (valuesAbstractValue.isZero()) {
-        // Unused enum instance.
+      if (fieldValue == null || fieldValue.isZero()) {
         return null;
       }
-      if (valuesAbstractValue.isUnknown()) {
-        return ObjectState.empty();
+      if (fieldValue.isSingleFieldValue()) {
+        return fieldValue.asSingleFieldValue().getState();
       }
-      assert valuesAbstractValue.isSingleFieldValue();
-      ObjectState valuesState = this.valuesAbstractValue.asSingleFieldValue().getState();
-      if (valuesState.isEnumValuesObjectState()) {
-        return valuesState.asEnumValuesObjectState().getObjectStateForOrdinal(ordinal);
-      }
+      assert fieldValue.isUnknown();
       return ObjectState.empty();
     }
+
+    public AbstractValue getValuesAbstractValueForPossiblyPinnedField(DexField field) {
+      assert valuesField == field || valuesAbstractValue == null;
+      return valuesAbstractValue;
+    }
   }
 
   public static class EmptyStaticValues extends StaticFieldValues {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/EnumValuesObjectState.java b/src/main/java/com/android/tools/r8/ir/analysis/value/EnumValuesObjectState.java
index f1ec89c..2d8ddc2 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/EnumValuesObjectState.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/EnumValuesObjectState.java
@@ -33,6 +33,10 @@
     return state[ordinal];
   }
 
+  public int getEnumValuesSize() {
+    return state.length;
+  }
+
   @Override
   public boolean isEnumValuesObjectState() {
     return true;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java
index 8de1c91..911174b 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java
@@ -12,7 +12,6 @@
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.EnumValueInfoMapCollection.EnumValueInfoMap;
 import com.android.tools.r8.graph.FieldResolutionResult.SuccessfulFieldResolutionResult;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -23,6 +22,7 @@
 import com.android.tools.r8.ir.code.StaticGet;
 import com.android.tools.r8.ir.code.TypeAndLocalInfoSupplier;
 import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.optimize.enums.EnumDataMap;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 
 public abstract class SingleFieldValue extends SingleValue {
@@ -107,12 +107,9 @@
   public SingleValue rewrittenWithLens(AppView<AppInfoWithLiveness> appView, GraphLens lens) {
     AbstractValueFactory factory = appView.abstractValueFactory();
     if (field.holder == field.type) {
-      EnumValueInfoMap unboxedEnumInfo = appView.unboxedEnums().getEnumValueInfoMap(field.type);
-      if (unboxedEnumInfo != null) {
-        // Return the ordinal of the unboxed enum.
-        assert unboxedEnumInfo.hasEnumValueInfo(field);
-        return factory.createSingleNumberValue(
-            unboxedEnumInfo.getEnumValueInfo(field).convertToInt());
+      EnumDataMap enumDataMap = appView.unboxedEnums();
+      if (enumDataMap.hasUnboxedValueFor(field)) {
+        return factory.createSingleNumberValue(enumDataMap.getUnboxedValue(field));
       }
     }
     return factory.createSingleFieldValue(
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 00e3372..7861771 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -22,7 +22,6 @@
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DexTypeList;
-import com.android.tools.r8.graph.EnumValueInfoMapCollection;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.classmerging.HorizontallyMergedLambdaClasses;
@@ -74,6 +73,7 @@
 import com.android.tools.r8.ir.optimize.ReflectionOptimizer;
 import com.android.tools.r8.ir.optimize.ServiceLoaderRewriter;
 import com.android.tools.r8.ir.optimize.classinliner.ClassInliner;
+import com.android.tools.r8.ir.optimize.enums.EnumDataMap;
 import com.android.tools.r8.ir.optimize.enums.EnumUnboxer;
 import com.android.tools.r8.ir.optimize.enums.EnumValueOptimizer;
 import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfoCollector;
@@ -753,7 +753,7 @@
     if (enumUnboxer != null) {
       enumUnboxer.unboxEnums(postMethodProcessorBuilder, executorService, feedback);
     } else {
-      appView.setUnboxedEnums(EnumValueInfoMapCollection.empty());
+      appView.setUnboxedEnums(EnumDataMap.empty());
     }
     if (!options.debug) {
       new TrivialFieldAccessReprocessor(appView.withLiveness(), postMethodProcessorBuilder)
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumDataMap.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumDataMap.java
new file mode 100644
index 0000000..07471e7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumDataMap.java
@@ -0,0 +1,109 @@
+// Copyright (c) 2020, 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.enums;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.optimize.enums.EnumInstanceFieldData.EnumInstanceFieldKnownData;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import java.util.Set;
+
+public class EnumDataMap {
+  private final ImmutableMap<DexType, EnumData> map;
+
+  public static EnumDataMap empty() {
+    return new EnumDataMap(ImmutableMap.of());
+  }
+
+  public EnumDataMap(ImmutableMap<DexType, EnumData> map) {
+    this.map = map;
+  }
+
+  public boolean isUnboxedEnum(DexType type) {
+    return map.containsKey(type);
+  }
+
+  public boolean isEmpty() {
+    return map.isEmpty();
+  }
+
+  public Set<DexType> getUnboxedEnums() {
+    return map.keySet();
+  }
+
+  public EnumInstanceFieldKnownData getInstanceFieldData(
+      DexType enumType, DexField enumInstanceField) {
+    assert map.containsKey(enumType);
+    return map.get(enumType).getInstanceFieldData(enumInstanceField);
+  }
+
+  public boolean hasUnboxedValueFor(DexField enumStaticField) {
+    return isUnboxedEnum(enumStaticField.holder)
+        && map.get(enumStaticField.holder).hasUnboxedValueFor(enumStaticField);
+  }
+
+  public int getUnboxedValue(DexField enumStaticField) {
+    assert map.containsKey(enumStaticField.holder);
+    return map.get(enumStaticField.holder).getUnboxedValue(enumStaticField);
+  }
+
+  public int getValuesSize(DexType enumType) {
+    assert map.containsKey(enumType);
+    return map.get(enumType).getValuesSize();
+  }
+
+  public boolean matchesValuesField(DexField staticField) {
+    assert map.containsKey(staticField.holder);
+    return map.get(staticField.holder).matchesValuesField(staticField);
+  }
+
+  public static class EnumData {
+    static final int INVALID_VALUES_SIZE = -1;
+
+    // Map each enum instance field to the list of field known data.
+    final ImmutableMap<DexField, EnumInstanceFieldKnownData> instanceFieldMap;
+    // Map each enum instance (static field) to the unboxed integer value.
+    final ImmutableMap<DexField, Integer> unboxedValues;
+    // Fields matching the $VALUES content and type, usually one.
+    final ImmutableSet<DexField> valuesFields;
+    // Size of the $VALUES field, if the valuesFields set is empty, set to INVALID_VALUES_SIZE.
+    final int valuesSize;
+
+    public EnumData(
+        ImmutableMap<DexField, EnumInstanceFieldKnownData> instanceFieldMap,
+        ImmutableMap<DexField, Integer> unboxedValues,
+        ImmutableSet<DexField> valuesFields,
+        int valuesSize) {
+      this.instanceFieldMap = instanceFieldMap;
+      this.unboxedValues = unboxedValues;
+      this.valuesFields = valuesFields;
+      this.valuesSize = valuesSize;
+    }
+
+    public EnumInstanceFieldKnownData getInstanceFieldData(DexField enumInstanceField) {
+      assert instanceFieldMap.containsKey(enumInstanceField);
+      return instanceFieldMap.get(enumInstanceField);
+    }
+
+    public int getUnboxedValue(DexField field) {
+      assert unboxedValues.containsKey(field);
+      return unboxedValues.get(field);
+    }
+
+    public boolean hasUnboxedValueFor(DexField field) {
+      return unboxedValues.get(field) != null;
+    }
+
+    public boolean matchesValuesField(DexField field) {
+      return valuesFields.contains(field);
+    }
+
+    public int getValuesSize() {
+      assert valuesSize != INVALID_VALUES_SIZE;
+      return valuesSize;
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumInstanceFieldDataMap.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumInstanceFieldDataMap.java
deleted file mode 100644
index f02e488..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumInstanceFieldDataMap.java
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (c) 2020, 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.enums;
-
-import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.optimize.enums.EnumInstanceFieldData.EnumInstanceFieldKnownData;
-import com.google.common.collect.ImmutableMap;
-
-public class EnumInstanceFieldDataMap {
-  private final ImmutableMap<DexType, ImmutableMap<DexField, EnumInstanceFieldKnownData>>
-      instanceFieldMap;
-
-  public EnumInstanceFieldDataMap(
-      ImmutableMap<DexType, ImmutableMap<DexField, EnumInstanceFieldKnownData>> instanceFieldMap) {
-    this.instanceFieldMap = instanceFieldMap;
-  }
-
-  public EnumInstanceFieldKnownData getInstanceFieldData(
-      DexType enumType, DexField enumInstanceField) {
-    assert instanceFieldMap.containsKey(enumType);
-    assert instanceFieldMap.get(enumType).containsKey(enumInstanceField);
-    return instanceFieldMap.get(enumType).get(enumInstanceField);
-  }
-}
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 9e95486..0d776a1 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
@@ -20,7 +20,6 @@
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DirectMappedDexApplication;
-import com.android.tools.r8.graph.EnumValueInfoMapCollection;
 import com.android.tools.r8.graph.FieldResolutionResult;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.GraphLens.NestedGraphLens;
@@ -34,6 +33,7 @@
 import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
+import com.android.tools.r8.ir.analysis.value.EnumValuesObjectState;
 import com.android.tools.r8.ir.analysis.value.ObjectState;
 import com.android.tools.r8.ir.code.ArrayPut;
 import com.android.tools.r8.ir.code.BasicBlock;
@@ -53,6 +53,7 @@
 import com.android.tools.r8.ir.conversion.IRConverter;
 import com.android.tools.r8.ir.conversion.PostMethodProcessor;
 import com.android.tools.r8.ir.optimize.Inliner.Constraint;
+import com.android.tools.r8.ir.optimize.enums.EnumDataMap.EnumData;
 import com.android.tools.r8.ir.optimize.enums.EnumInstanceFieldData.EnumInstanceFieldKnownData;
 import com.android.tools.r8.ir.optimize.enums.EnumInstanceFieldData.EnumInstanceFieldMappingData;
 import com.android.tools.r8.ir.optimize.enums.EnumInstanceFieldData.EnumInstanceFieldOrdinalData;
@@ -71,8 +72,11 @@
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceArrayMap;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
 import java.util.Arrays;
 import java.util.Map;
+import java.util.OptionalInt;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ExecutionException;
@@ -91,6 +95,8 @@
   private final Map<DexType, EnumStaticFieldValues> staticFieldValuesMap =
       new ConcurrentHashMap<>();
 
+  private final DexEncodedField ordinalField;
+
   private EnumUnboxingRewriter enumUnboxerRewriter;
 
   private final boolean debugLogEnabled;
@@ -108,6 +114,13 @@
     }
     assert !appView.options().debug;
     enumUnboxingCandidatesInfo = new EnumUnboxingCandidateAnalysis(appView, this).findCandidates();
+
+    ordinalField =
+        appView.appInfo().resolveField(factory.enumMembers.ordinalField).getResolvedField();
+    if (ordinalField == null) {
+      assert false : "Missing library field " + factory.enumMembers.ordinalField;
+      enumUnboxingCandidatesInfo.clear();
+    }
   }
 
   public static int ordinalToUnboxedInt(int ordinal) {
@@ -359,7 +372,7 @@
       ExecutorService executorService,
       OptimizationFeedbackDelayed feedback)
       throws ExecutionException {
-    EnumInstanceFieldDataMap enumInstanceFieldDataMap = finishAnalysis();
+    EnumDataMap enumDataMap = finishAnalysis();
     // At this point the enum unboxing candidates are no longer candidates, they will all be
     // unboxed. We extract the now immutable enums to unbox information and clear the candidate
     // info.
@@ -379,13 +392,12 @@
             .synthesizeEnumUnboxingUtilityClasses(
                 enumClassesToUnbox, enumsToUnboxWithPackageRequirement, appBuilder)
             .build();
-    enumUnboxerRewriter =
-        new EnumUnboxingRewriter(appView, enumsToUnbox, enumInstanceFieldDataMap, relocator);
+    enumUnboxerRewriter = new EnumUnboxingRewriter(appView, enumDataMap, relocator);
     NestedGraphLens enumUnboxingLens =
         new EnumUnboxingTreeFixer(appView, enumsToUnbox, relocator, enumUnboxerRewriter)
             .fixupTypeReferences();
     enumUnboxerRewriter.setEnumUnboxingLens(enumUnboxingLens);
-    appView.setUnboxedEnums(enumUnboxerRewriter.getEnumsToUnbox());
+    appView.setUnboxedEnums(enumDataMap);
     GraphLens previousLens = appView.graphLens();
     appView.rewriteWithLensAndApplication(enumUnboxingLens, appBuilder.build());
     updateOptimizationInfos(executorService, feedback);
@@ -434,23 +446,22 @@
     keepInfo.mutate(mutator -> mutator.removeKeepInfoForPrunedItems(enumsToUnbox));
   }
 
-  public EnumInstanceFieldDataMap finishAnalysis() {
+  public EnumDataMap finishAnalysis() {
     analyzeInitializers();
     analyzeAccessibility();
-    EnumInstanceFieldDataMap enumInstanceFieldDataMap = analyzeFields();
+    EnumDataMap enumDataMap = analyzeEnumInstances();
+    assert enumDataMap.getUnboxedEnums().size() == enumUnboxingCandidatesInfo.candidates().size();
     if (debugLogEnabled) {
       reportEnumsAnalysis();
     }
-    return enumInstanceFieldDataMap;
+    return enumDataMap;
   }
 
-  private EnumInstanceFieldDataMap analyzeFields() {
-    ImmutableMap.Builder<DexType, ImmutableMap<DexField, EnumInstanceFieldKnownData>> builder =
-        ImmutableMap.builder();
+  private EnumDataMap analyzeEnumInstances() {
+    ImmutableMap.Builder<DexType, EnumData> builder = ImmutableMap.builder();
     enumUnboxingCandidatesInfo.forEachCandidateAndRequiredInstanceFieldData(
         (enumClass, fields) -> {
-          ImmutableMap<DexField, EnumInstanceFieldKnownData> data =
-              buildEnumInstanceFieldData(enumClass, fields);
+          EnumData data = buildData(enumClass, fields);
           if (data == null) {
             markEnumAsUnboxable(Reason.MISSING_INSTANCE_FIELD_DATA, enumClass);
             return;
@@ -458,20 +469,136 @@
           builder.put(enumClass.type, data);
         });
     staticFieldValuesMap.clear();
-    return new EnumInstanceFieldDataMap(builder.build());
+    return new EnumDataMap(builder.build());
   }
 
-  private ImmutableMap<DexField, EnumInstanceFieldKnownData> buildEnumInstanceFieldData(
-      DexProgramClass enumClass, Set<DexField> fields) {
-    ImmutableMap.Builder<DexField, EnumInstanceFieldKnownData> typeBuilder = ImmutableMap.builder();
-    for (DexField field : fields) {
-      EnumInstanceFieldData enumInstanceFieldData = computeEnumFieldData(field, enumClass);
-      if (enumInstanceFieldData.isUnknown()) {
+  private EnumData buildData(DexProgramClass enumClass, Set<DexField> fields) {
+    // This map holds all the accessible fields to their unboxed value, so we can remap the field
+    // read to the unboxed value.
+    ImmutableMap.Builder<DexField, Integer> unboxedValues = ImmutableMap.builder();
+    // This maps the ordinal to the object state, note that some fields may have been removed,
+    // hence the entry is in this map but not the enumToOrdinalMap.
+    Int2ReferenceMap<ObjectState> ordinalToObjectState = new Int2ReferenceArrayMap<>();
+    // Any fields matching the expected $VALUES content can be recorded here, they have however
+    // all the same content.
+    ImmutableSet.Builder<DexField> valuesField = ImmutableSet.builder();
+    EnumValuesObjectState valuesContents = null;
+
+    EnumStaticFieldValues enumStaticFieldValues = staticFieldValuesMap.get(enumClass.type);
+
+    // Step 1: We iterate over the field to find direct enum instance information and the values
+    // fields.
+    for (DexEncodedField staticField : enumClass.staticFields()) {
+      if (EnumUnboxingCandidateAnalysis.isEnumField(staticField, enumClass.type)) {
+        ObjectState enumState =
+            enumStaticFieldValues.getObjectStateForPossiblyPinnedField(staticField.field);
+        if (enumState != null) {
+          OptionalInt optionalOrdinal = getOrdinal(enumState);
+          if (!optionalOrdinal.isPresent()) {
+            return null;
+          }
+          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;
+        }
+        assert valuesValue.isSingleFieldValue();
+        ObjectState valuesState = valuesValue.asSingleFieldValue().getState();
+        if (!valuesState.isEnumValuesObjectState()) {
+          return null;
+        }
+        assert valuesContents == null
+            || valuesContents.equals(valuesState.asEnumValuesObjectState());
+        valuesContents = valuesState.asEnumValuesObjectState();
+        valuesField.add(staticField.field);
+      }
+    }
+
+    // Step 2: We complete the information based on the values content, since some enum instances
+    // may be reachable only though the $VALUES field.
+    if (valuesContents != null) {
+      for (int ordinal = 0; ordinal < valuesContents.getEnumValuesSize(); ordinal++) {
+        if (!ordinalToObjectState.containsKey(ordinal)) {
+          ObjectState enumState = valuesContents.getObjectStateForOrdinal(ordinal);
+          if (enumState.isEmpty()) {
+            // If $VALUES is used, we need data for all enums, at least the ordinal.
+            return null;
+          }
+          assert getOrdinal(enumState).isPresent();
+          assert getOrdinal(enumState).getAsInt() == ordinal;
+          ordinalToObjectState.put(ordinal, enumState);
+        }
+      }
+    }
+
+    // The ordinalToObjectState map may have holes at this point, if some enum instances are never
+    // used ($VALUES unused or removed, and enum instance field unused or removed), it contains
+    // only data for reachable enum instance, that is what we're interested in.
+    ImmutableMap.Builder<DexField, EnumInstanceFieldKnownData> instanceFieldBuilder =
+        ImmutableMap.builder();
+    for (DexField instanceField : fields) {
+      EnumInstanceFieldData fieldData =
+          computeEnumFieldData(instanceField, enumClass, ordinalToObjectState);
+      if (fieldData.isUnknown()) {
         return null;
       }
-      typeBuilder.put(field, enumInstanceFieldData.asEnumFieldKnownData());
+      instanceFieldBuilder.put(instanceField, fieldData.asEnumFieldKnownData());
     }
-    return typeBuilder.build();
+
+    return new EnumData(
+        instanceFieldBuilder.build(),
+        unboxedValues.build(),
+        valuesField.build(),
+        valuesContents == null ? EnumData.INVALID_VALUES_SIZE : valuesContents.getEnumValuesSize());
+  }
+
+  private EnumInstanceFieldData computeEnumFieldData(
+      DexField instanceField,
+      DexProgramClass enumClass,
+      Int2ReferenceMap<ObjectState> ordinalToObjectState) {
+    DexEncodedField encodedInstanceField =
+        appView.appInfo().resolveFieldOn(enumClass, instanceField).getResolvedField();
+    assert encodedInstanceField != null;
+    boolean canBeOrdinal = instanceField.type.isIntType();
+    ImmutableInt2ReferenceSortedMap.Builder<AbstractValue> data =
+        ImmutableInt2ReferenceSortedMap.builder();
+    for (Integer ordinal : ordinalToObjectState.keySet()) {
+      ObjectState state = ordinalToObjectState.get(ordinal);
+      AbstractValue fieldValue = state.getAbstractFieldValue(encodedInstanceField);
+      if (!(fieldValue.isSingleNumberValue() || fieldValue.isSingleStringValue())) {
+        return EnumInstanceFieldUnknownData.getInstance();
+      }
+      data.put(ordinalToUnboxedInt(ordinal), fieldValue);
+      if (canBeOrdinal) {
+        assert fieldValue.isSingleNumberValue();
+        int computedValue = fieldValue.asSingleNumberValue().getIntValue();
+        if (computedValue != ordinal) {
+          canBeOrdinal = false;
+        }
+      }
+    }
+    if (canBeOrdinal) {
+      return new EnumInstanceFieldOrdinalData();
+    }
+    return new EnumInstanceFieldMappingData(data.build());
+  }
+
+  private OptionalInt getOrdinal(ObjectState state) {
+    AbstractValue field = state.getAbstractFieldValue(ordinalField);
+    if (field.isSingleNumberValue()) {
+      return OptionalInt.of(field.asSingleNumberValue().getIntValue());
+    }
+    return OptionalInt.empty();
   }
 
   private void analyzeAccessibility() {
@@ -958,49 +1085,6 @@
     return Reason.OTHER_UNSUPPORTED_INSTRUCTION;
   }
 
-  private EnumInstanceFieldData computeEnumFieldData(
-      DexField instanceField, DexProgramClass enumClass) {
-    DexEncodedField encodedInstanceField =
-        appView.appInfo().resolveFieldOn(enumClass, instanceField).getResolvedField();
-    assert encodedInstanceField != null;
-    boolean canBeOrdinal = instanceField.type.isIntType();
-    ImmutableInt2ReferenceSortedMap.Builder<AbstractValue> data =
-        ImmutableInt2ReferenceSortedMap.builder();
-    EnumValueInfoMapCollection.EnumValueInfoMap enumValueInfoMap =
-        appView.appInfo().getEnumValueInfoMap(enumClass.type);
-    for (DexField staticField : enumValueInfoMap.enumValues()) {
-      EnumStaticFieldValues enumStaticFieldValues = staticFieldValuesMap.get(enumClass.type);
-      if (enumStaticFieldValues == null) {
-        return EnumInstanceFieldUnknownData.getInstance();
-      }
-      ObjectState enumInstanceState =
-          enumStaticFieldValues.getObjectStateForPossiblyPinnedEnumInstance(
-              staticField, enumValueInfoMap.getEnumValueInfo(staticField).ordinal);
-      if (enumInstanceState == null) {
-        // The enum instance is effectively unused. No need to generate anything for it, the path
-        // will never be taken.
-      } else {
-        AbstractValue fieldValue = enumInstanceState.getAbstractFieldValue(encodedInstanceField);
-        if (!(fieldValue.isSingleNumberValue() || fieldValue.isSingleStringValue())) {
-          return EnumInstanceFieldUnknownData.getInstance();
-        }
-        data.put(enumValueInfoMap.getEnumValueInfo(staticField).convertToInt(), fieldValue);
-        if (canBeOrdinal) {
-          int ordinalValue = enumValueInfoMap.getEnumValueInfo(staticField).ordinal;
-          assert fieldValue.isSingleNumberValue();
-          int computedValue = fieldValue.asSingleNumberValue().getIntValue();
-          if (computedValue != ordinalValue) {
-            canBeOrdinal = false;
-          }
-        }
-      }
-    }
-    if (canBeOrdinal) {
-      return new EnumInstanceFieldOrdinalData();
-    }
-    return new EnumInstanceFieldMappingData(data.build());
-  }
-
   private void reportEnumsAnalysis() {
     assert debugLogEnabled;
     Reporter reporter = appView.options().reporter;
@@ -1070,7 +1154,6 @@
     VALUES_INVOKE,
     COMPARE_TO_INVOKE,
     UNSUPPORTED_LIBRARY_CALL,
-    MISSING_INFO_MAP,
     MISSING_INSTANCE_FIELD_DATA,
     INVALID_FIELD_READ,
     INVALID_FIELD_PUT,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java
index b4bd339..fd9b279 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java
@@ -12,7 +12,6 @@
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.EnumValueInfoMapCollection.EnumValueInfoMap;
 import com.android.tools.r8.ir.optimize.enums.EnumUnboxer.Reason;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.KeepInfoCollection;
@@ -66,12 +65,6 @@
       enumUnboxer.reportFailure(clazz.type, Reason.UNEXPECTED_STATIC_FIELD);
       return false;
     }
-    EnumValueInfoMap enumValueInfoMap =
-        appView.appInfo().withLiveness().getEnumValueInfoMap(clazz.type);
-    if (enumValueInfoMap == null) {
-      enumUnboxer.reportFailure(clazz.type, Reason.MISSING_INFO_MAP);
-      return false;
-    }
     return true;
   }
 
@@ -79,15 +72,9 @@
   // instances.
   private boolean enumHasBasicStaticFields(DexProgramClass clazz) {
     for (DexEncodedField staticField : clazz.staticFields()) {
-      if (staticField.field.type == clazz.type
-          && staticField.accessFlags.isEnum()
-          && staticField.accessFlags.isFinal()) {
+      if (isEnumField(staticField, clazz.type)) {
         // Enum field, valid, do nothing.
-      } else if (staticField.field.type.isArrayType()
-          && staticField.field.type.toArrayElementType(factory) == clazz.type
-          && staticField.accessFlags.isSynthetic()
-          && staticField.accessFlags.isFinal()
-          && staticField.field.name == factory.enumValuesFieldName) {
+      } else if (matchesValuesField(staticField, clazz.type, factory)) {
         // Field $VALUES, valid, do nothing.
       } else if (appView.appInfo().isFieldRead(staticField)) {
         // Only non read static fields are valid, and they are assumed unused.
@@ -97,6 +84,21 @@
     return true;
   }
 
+  static boolean isEnumField(DexEncodedField staticField, DexType enumType) {
+    return staticField.field.type == enumType
+        && staticField.accessFlags.isEnum()
+        && staticField.accessFlags.isFinal();
+  }
+
+  static boolean matchesValuesField(
+      DexEncodedField staticField, DexType enumType, DexItemFactory factory) {
+    return staticField.field.type.isArrayType()
+        && staticField.field.type.toArrayElementType(factory) == enumType
+        && staticField.accessFlags.isSynthetic()
+        && staticField.accessFlags.isFinal()
+        && staticField.field.name == factory.enumValuesFieldName;
+  }
+
   private void removeEnumsInAnnotations() {
     for (DexProgramClass clazz : appView.appInfo().classes()) {
       if (clazz.isAnnotation()) {
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 c0b1831..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
@@ -21,9 +21,6 @@
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.EnumValueInfoMapCollection;
-import com.android.tools.r8.graph.EnumValueInfoMapCollection.EnumValueInfo;
-import com.android.tools.r8.graph.EnumValueInfoMapCollection.EnumValueInfoMap;
 import com.android.tools.r8.graph.FieldAccessFlags;
 import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
 import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
@@ -76,8 +73,7 @@
 
   private final AppView<AppInfoWithLiveness> appView;
   private final DexItemFactory factory;
-  private final EnumValueInfoMapCollection enumsToUnbox;
-  private final EnumInstanceFieldDataMap unboxedEnumsInstanceFieldData;
+  private final EnumDataMap unboxedEnumsData;
   private final UnboxedEnumMemberRelocator relocator;
   private NestedGraphLens enumUnboxingLens;
 
@@ -93,18 +89,11 @@
 
   EnumUnboxingRewriter(
       AppView<AppInfoWithLiveness> appView,
-      Set<DexType> enumsToUnbox,
-      EnumInstanceFieldDataMap unboxedEnumsInstanceFieldData,
+      EnumDataMap unboxedEnumsInstanceFieldData,
       UnboxedEnumMemberRelocator relocator) {
     this.appView = appView;
     this.factory = appView.dexItemFactory();
-    EnumValueInfoMapCollection.Builder builder = EnumValueInfoMapCollection.builder();
-    for (DexType toUnbox : enumsToUnbox) {
-      assert appView.appInfo().withLiveness().getEnumValueInfoMap(toUnbox) != null;
-      builder.put(toUnbox, appView.appInfo().withLiveness().getEnumValueInfoMap(toUnbox));
-    }
-    this.enumsToUnbox = builder.build();
-    this.unboxedEnumsInstanceFieldData = unboxedEnumsInstanceFieldData;
+    this.unboxedEnumsData = unboxedEnumsInstanceFieldData;
     this.relocator = relocator;
 
     // Custom methods for java.lang.Enum methods ordinal, equals and compareTo.
@@ -147,14 +136,10 @@
     this.enumUnboxingLens = enumUnboxingLens;
   }
 
-  public EnumValueInfoMapCollection getEnumsToUnbox() {
-    return enumsToUnbox;
-  }
-
   Set<Phi> rewriteCode(IRCode code) {
     // We should not process the enum methods, they will be removed and they may contain invalid
     // rewriting rules.
-    if (enumsToUnbox.isEmpty()) {
+    if (unboxedEnumsData.isEmpty()) {
       return Sets.newIdentityHashSet();
     }
     assert code.isConsistentSSABeforeTypesAreCorrect();
@@ -223,7 +208,7 @@
               && invokeStatic.getArgument(0).isConstClass()) {
             DexType enumType =
                 invokeStatic.getArgument(0).getConstInstruction().asConstClass().getValue();
-            if (enumsToUnbox.containsEnum(enumType)) {
+            if (unboxedEnumsData.isUnboxedEnum(enumType)) {
               DexMethod valueOfMethod = computeValueOfUtilityMethod(enumType);
               Value outValue = invokeStatic.outValue();
               Value rewrittenOutValue = null;
@@ -282,17 +267,15 @@
         }
         if (instruction.isStaticGet()) {
           StaticGet staticGet = instruction.asStaticGet();
-          DexType holder = staticGet.getField().holder;
-          if (enumsToUnbox.containsEnum(holder)) {
+          DexField field = staticGet.getField();
+          DexType holder = field.holder;
+          if (unboxedEnumsData.isUnboxedEnum(holder)) {
             if (staticGet.outValue() == null) {
               iterator.removeOrReplaceByDebugLocalRead();
               continue;
             }
-            EnumValueInfoMap enumValueInfoMap = enumsToUnbox.getEnumValueInfoMap(holder);
-            assert enumValueInfoMap != null;
             affectedPhis.addAll(staticGet.outValue().uniquePhiUsers());
-            EnumValueInfo enumValueInfo = enumValueInfoMap.getEnumValueInfo(staticGet.getField());
-            if (enumValueInfo == null && staticGet.getField().name == factory.enumValuesFieldName) {
+            if (unboxedEnumsData.matchesValuesField(field)) {
               utilityMethods.computeIfAbsent(
                   valuesUtilityMethod, m -> synthesizeValuesUtilityMethod());
               DexField fieldValues = createValuesField(holder);
@@ -300,7 +283,9 @@
               DexMethod methodValues = createValuesMethod(holder);
               utilityMethods.computeIfAbsent(
                   methodValues,
-                  m -> computeValuesEncodedMethod(m, fieldValues, enumValueInfoMap.size()));
+                  m ->
+                      computeValuesEncodedMethod(
+                          m, fieldValues, unboxedEnumsData.getValuesSize(holder)));
               Value rewrittenOutValue =
                   code.createValue(
                       ArrayTypeElement.create(TypeElement.getInt(), definitelyNotNull()));
@@ -310,9 +295,10 @@
               convertedEnums.put(invoke, holder);
             } else {
               // Replace by ordinal + 1 for null check (null is 0).
-              assert enumValueInfo != null
-                  : "Invalid read to " + staticGet.getField().name + ", error during enum analysis";
-              ConstNumber intConstant = code.createIntConstant(enumValueInfo.convertToInt());
+              assert unboxedEnumsData.hasUnboxedValueFor(field)
+                  : "Invalid read to " + field.name + ", error during enum analysis";
+              ConstNumber intConstant =
+                  code.createIntConstant(unboxedEnumsData.getUnboxedValue(field));
               iterator.replaceCurrentInstruction(intConstant);
               convertedEnums.put(intConstant, holder);
             }
@@ -322,7 +308,7 @@
         if (instruction.isInstanceGet()) {
           InstanceGet instanceGet = instruction.asInstanceGet();
           DexType holder = instanceGet.getField().holder;
-          if (enumsToUnbox.containsEnum(holder)) {
+          if (unboxedEnumsData.isUnboxedEnum(holder)) {
             DexMethod fieldMethod = computeInstanceFieldMethod(instanceGet.getField());
             Value rewrittenOutValue =
                 code.createValue(
@@ -332,7 +318,7 @@
                 new InvokeStatic(
                     fieldMethod, rewrittenOutValue, ImmutableList.of(instanceGet.object()));
             iterator.replaceCurrentInstruction(invoke);
-            if (enumsToUnbox.containsEnum(instanceGet.getField().type)) {
+            if (unboxedEnumsData.isUnboxedEnum(instanceGet.getField().type)) {
               convertedEnums.put(invoke, instanceGet.getField().type);
             }
           }
@@ -393,7 +379,7 @@
 
   private DexMethod computeInstanceFieldMethod(DexField field) {
     EnumInstanceFieldKnownData enumFieldKnownData =
-        unboxedEnumsInstanceFieldData.getInstanceFieldData(field.holder, field);
+        unboxedEnumsData.getInstanceFieldData(field.holder, field);
     if (enumFieldKnownData.isOrdinal()) {
       utilityMethods.computeIfAbsent(ordinalUtilityMethod, m -> synthesizeOrdinalMethod());
       return ordinalUtilityMethod;
@@ -437,7 +423,7 @@
       return null;
     }
     DexType enumType = type.asClassType().getClassType();
-    return enumsToUnbox.containsEnum(enumType) ? enumType : null;
+    return unboxedEnumsData.isUnboxedEnum(enumType) ? enumType : null;
   }
 
   public String compatibleName(DexType type) {
@@ -478,7 +464,7 @@
   }
 
   private DexMethod computeInstanceFieldUtilityMethod(DexType enumType, DexField field) {
-    assert enumsToUnbox.containsEnum(enumType);
+    assert unboxedEnumsData.isUnboxedEnum(enumType);
     assert field.holder == enumType || field.holder == factory.enumType;
     String methodName =
         "get"
@@ -498,7 +484,7 @@
 
   private DexMethod computeStringValueOfUtilityMethod(DexType enumType) {
     // TODO(b/167994636): remove duplication between instance field name read and this method.
-    assert enumsToUnbox.containsEnum(enumType);
+    assert unboxedEnumsData.isUnboxedEnum(enumType);
     String methodName = "string$valueOf$" + compatibleName(enumType);
     DexMethod fieldMethod =
         factory.createMethod(
@@ -514,7 +500,7 @@
   }
 
   private DexMethod computeValueOfUtilityMethod(DexType enumType) {
-    assert enumsToUnbox.containsEnum(enumType);
+    assert unboxedEnumsData.isUnboxedEnum(enumType);
     DexMethod valueOf =
         factory.createMethod(
             relocator.getNewMemberLocationFor(enumType),
@@ -538,7 +524,7 @@
       return null;
     }
     DexType classType = baseType.asClassType().getClassType();
-    return enumsToUnbox.containsEnum(classType) ? classType : null;
+    return unboxedEnumsData.isUnboxedEnum(classType) ? classType : null;
   }
 
   void synthesizeEnumUnboxingUtilityMethods(IRConverter converter, ExecutorService executorService)
@@ -593,15 +579,13 @@
   private DexEncodedMethod synthesizeInstanceFieldMethod(
       DexMethod method, DexType enumType, DexField field, AbstractValue nullValue) {
     assert method.proto.returnType == field.type;
-    assert unboxedEnumsInstanceFieldData.getInstanceFieldData(enumType, field).isMapping();
+    assert unboxedEnumsData.getInstanceFieldData(enumType, field).isMapping();
     CfCode cfCode =
         new EnumUnboxingCfCodeProvider.EnumUnboxingInstanceFieldCfCodeProvider(
                 appView,
                 method.holder,
                 field.type,
-                unboxedEnumsInstanceFieldData
-                    .getInstanceFieldData(enumType, field)
-                    .asEnumFieldMappingData(),
+                unboxedEnumsData.getInstanceFieldData(enumType, field).asEnumFieldMappingData(),
                 nullValue)
             .generateCfCode();
     return synthesizeUtilityMethod(cfCode, method, false);
@@ -609,7 +593,7 @@
 
   private DexEncodedMethod synthesizeValueOfUtilityMethod(DexMethod method, DexType enumType) {
     assert method.proto.returnType == factory.intType;
-    assert unboxedEnumsInstanceFieldData
+    assert unboxedEnumsData
         .getInstanceFieldData(enumType, factory.enumMembers.nameField)
         .isMapping();
     CfCode cfCode =
@@ -617,7 +601,7 @@
                 appView,
                 method.holder,
                 enumType,
-                unboxedEnumsInstanceFieldData
+                unboxedEnumsData
                     .getInstanceFieldData(enumType, factory.enumMembers.nameField)
                     .asEnumFieldMappingData())
             .generateCfCode();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldTypeInitializationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldTypeInitializationInfo.java
index 7e78f27..262671f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldTypeInitializationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldTypeInitializationInfo.java
@@ -5,10 +5,10 @@
 package com.android.tools.r8.ir.optimize.info.field;
 
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.EnumValueInfoMapCollection;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.ir.optimize.enums.EnumDataMap;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import java.util.Objects;
 
@@ -49,14 +49,14 @@
   @Override
   public InstanceFieldInitializationInfo rewrittenWithLens(
       AppView<AppInfoWithLiveness> appView, GraphLens lens) {
-    EnumValueInfoMapCollection unboxedEnums = appView.unboxedEnums();
+    EnumDataMap enumDataMap = appView.unboxedEnums();
     if (dynamicLowerBoundType != null
-        && unboxedEnums.containsEnum(dynamicLowerBoundType.getClassType())) {
+        && enumDataMap.isUnboxedEnum(dynamicLowerBoundType.getClassType())) {
       // No point in tracking the type of primitives.
       return UnknownInstanceFieldInitializationInfo.getInstance();
     }
     if (dynamicUpperBoundType.isClassType()
-        && unboxedEnums.containsEnum(dynamicUpperBoundType.asClassType().getClassType())) {
+        && enumDataMap.isUnboxedEnum(dynamicUpperBoundType.asClassType().getClassType())) {
       // No point in tracking the type of primitives.
       return UnknownInstanceFieldInitializationInfo.getInstance();
     }
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index f8b6d10..4fc8f54 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -26,7 +26,6 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DirectMappedDexApplication;
 import com.android.tools.r8.graph.EnumValueInfoMapCollection;
-import com.android.tools.r8.graph.EnumValueInfoMapCollection.EnumValueInfoMap;
 import com.android.tools.r8.graph.FieldAccessInfo;
 import com.android.tools.r8.graph.FieldAccessInfoCollection;
 import com.android.tools.r8.graph.FieldAccessInfoCollectionImpl;
@@ -696,16 +695,6 @@
     return missingTypes;
   }
 
-  public EnumValueInfoMapCollection getEnumValueInfoMapCollection() {
-    assert checkIfObsolete();
-    return enumValueInfoMaps;
-  }
-
-  public EnumValueInfoMap getEnumValueInfoMap(DexType enumType) {
-    assert checkIfObsolete();
-    return enumValueInfoMaps.getEnumValueInfoMap(enumType);
-  }
-
   public Int2ReferenceMap<DexField> getSwitchMap(DexField field) {
     assert checkIfObsolete();
     return switchMaps.get(field);
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 99a561f..f03ed26 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -39,7 +39,6 @@
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.EnumValueInfoMapCollection;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.classmerging.HorizontallyMergedLambdaClasses;
 import com.android.tools.r8.graph.classmerging.StaticallyMergedClasses;
@@ -50,6 +49,7 @@
 import com.android.tools.r8.ir.conversion.MethodProcessingId;
 import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
 import com.android.tools.r8.ir.optimize.Inliner;
+import com.android.tools.r8.ir.optimize.enums.EnumDataMap;
 import com.android.tools.r8.ir.optimize.lambda.kotlin.KotlinLambdaGroupIdFactory;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.position.Position;
@@ -1382,7 +1382,7 @@
     public BiConsumer<DexItemFactory, StaticallyMergedClasses> staticallyMergedClassesConsumer =
         ConsumerUtils.emptyBiConsumer();
 
-    public BiConsumer<DexItemFactory, EnumValueInfoMapCollection> unboxedEnumsConsumer =
+    public BiConsumer<DexItemFactory, EnumDataMap> unboxedEnumsConsumer =
         ConsumerUtils.emptyBiConsumer();
 
     public BiConsumer<DexItemFactory, VerticallyMergedClasses> verticallyMergedClassesConsumer =
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/EnumWithNonDefaultForwardingConstructorTest.java b/src/test/java/com/android/tools/r8/enumunboxing/EnumWithNonDefaultForwardingConstructorTest.java
index 92b0b54..926a7b0 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/EnumWithNonDefaultForwardingConstructorTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/EnumWithNonDefaultForwardingConstructorTest.java
@@ -54,8 +54,7 @@
         .setMinApi(parameters.getApiLevel())
         .compile()
         .run(parameters.getRuntime(), TestClass.class)
-        // TODO(b/160939354): Should succeed with 42.
-        .assertSuccessWithOutputLines(enableEnumUnboxing ? "0" : "42");
+        .assertSuccessWithOutputLines("42");
   }
 
   private void addProgramClasses(TestBuilder<?, ?> builder) throws Exception {
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/EnumUnboxingInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/EnumUnboxingInspector.java
index 8e5495e..2d41c1f 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/EnumUnboxingInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/EnumUnboxingInspector.java
@@ -8,21 +8,20 @@
 import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.EnumValueInfoMapCollection;
+import com.android.tools.r8.ir.optimize.enums.EnumDataMap;
 
 public class EnumUnboxingInspector {
 
   private final DexItemFactory dexItemFactory;
-  private final EnumValueInfoMapCollection unboxedEnums;
+  private final EnumDataMap unboxedEnums;
 
-  public EnumUnboxingInspector(
-      DexItemFactory dexItemFactory, EnumValueInfoMapCollection unboxedEnums) {
+  public EnumUnboxingInspector(DexItemFactory dexItemFactory, EnumDataMap unboxedEnums) {
     this.dexItemFactory = dexItemFactory;
     this.unboxedEnums = unboxedEnums;
   }
 
   public EnumUnboxingInspector assertUnboxed(Class<? extends Enum<?>> clazz) {
-    assertTrue(unboxedEnums.containsEnum(toDexType(clazz, dexItemFactory)));
+    assertTrue(unboxedEnums.isUnboxedEnum(toDexType(clazz, dexItemFactory)));
     return this;
   }