Reenable enum name() and toString() optimization

Bug: 160667929
Bug: 155239749
Change-Id: I1497485fe038873d290b641cb07633afc302763c
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 8b2bd65..aa1d5a9 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -462,7 +462,7 @@
   public final AssertionErrorMethods assertionErrorMethods = new AssertionErrorMethods();
   public final ClassMethods classMethods = new ClassMethods();
   public final ConstructorMethods constructorMethods = new ConstructorMethods();
-  public final EnumMethods enumMethods = new EnumMethods();
+  public final EnumMembers enumMembers = new EnumMembers();
   public final NullPointerExceptionMethods npeMethods = new NullPointerExceptionMethods();
   public final IllegalArgumentExceptionMethods illegalArgumentExceptionMethods =
       new IllegalArgumentExceptionMethods();
@@ -688,7 +688,7 @@
   // If not, that library method should not be added here because it literally has side effects.
   public Map<DexMethod, Predicate<InvokeMethod>> libraryMethodsWithoutSideEffects =
       Streams.<Pair<DexMethod, Predicate<InvokeMethod>>>concat(
-              Stream.of(new Pair<>(enumMethods.constructor, alwaysTrue())),
+              Stream.of(new Pair<>(enumMembers.constructor, alwaysTrue())),
               Stream.of(new Pair<>(npeMethods.init, alwaysTrue())),
               Stream.of(new Pair<>(npeMethods.initWithMessage, alwaysTrue())),
               Stream.of(new Pair<>(objectMembers.constructor, alwaysTrue())),
@@ -1268,11 +1268,14 @@
     }
   }
 
-  public class EnumMethods {
+  public class EnumMembers {
+
+    public final DexField nameField = createField(enumType, stringType, "name");
+    public final DexField ordinalField = createField(enumType, intType, "ordinal");
 
     public final DexMethod valueOf;
-    public final DexMethod ordinal;
-    public final DexMethod name;
+    public final DexMethod ordinalMethod;
+    public final DexMethod nameMethod;
     public final DexMethod toString;
     public final DexMethod compareTo;
     public final DexMethod equals;
@@ -1283,25 +1286,17 @@
     public final DexMethod finalize =
         createMethod(enumType, createProto(voidType), finalizeMethodName);
 
-    private EnumMethods() {
+    private EnumMembers() {
       valueOf =
           createMethod(
               enumDescriptor,
               valueOfMethodName,
               enumDescriptor,
               new DexString[] {classDescriptor, stringDescriptor});
-      ordinal =
-          createMethod(
-              enumDescriptor,
-              ordinalMethodName,
-              intDescriptor,
-              DexString.EMPTY_ARRAY);
-      name =
-          createMethod(
-              enumDescriptor,
-              nameMethodName,
-              stringDescriptor,
-              DexString.EMPTY_ARRAY);
+      ordinalMethod =
+          createMethod(enumDescriptor, ordinalMethodName, intDescriptor, DexString.EMPTY_ARRAY);
+      nameMethod =
+          createMethod(enumDescriptor, nameMethodName, stringDescriptor, DexString.EMPTY_ARRAY);
       toString =
           createMethod(
               enumDescriptor,
@@ -1321,6 +1316,15 @@
           createMethod(enumDescriptor, hashCodeMethodName, intDescriptor, DexString.EMPTY_ARRAY);
     }
 
+    public void forEachField(Consumer<DexField> fn) {
+      fn.accept(nameField);
+      fn.accept(ordinalField);
+    }
+
+    public boolean isNameOrOrdinalField(DexField field) {
+      return field == nameField || field == ordinalField;
+    }
+
     public boolean isValuesMethod(DexMethod method, DexClass enumClass) {
       assert enumClass.isEnum();
       return method.holder == enumClass.type
diff --git a/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java b/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java
index 35d8146..7711eb3 100644
--- a/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java
@@ -63,6 +63,10 @@
     assert kind == Kind.CF : "Invalid kind " + kind + " for library-path class " + type;
   }
 
+  public static DexLibraryClass asLibraryClassOrNull(DexClass clazz) {
+    return clazz != null ? clazz.asLibraryClass() : null;
+  }
+
   private static boolean verifyLibraryMethod(DexEncodedMethod method) {
     assert !method.isClassInitializer();
     assert !method.isPrivateMethod();
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 81df82a..3d8df1a 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
@@ -229,7 +229,14 @@
     initializationInfos.forEach(
         appView,
         (field, initializationInfo) -> {
-          if (!appView.appInfo().isInstanceFieldWrittenOnlyInInstanceInitializers(field)) {
+          // If the instance field is not written only in the instance initializer, then we can't
+          // conclude that this field will have a constant value.
+          //
+          // We have special handling for library fields that satisfy the property that they are
+          // only written in their corresponding instance initializers. This is needed since we
+          // don't analyze these instance initializers in the Enqueuer, as they are in the library.
+          if (!appView.appInfo().isInstanceFieldWrittenOnlyInInstanceInitializers(field)
+              && !appView.dexItemFactory().enumMembers.isNameOrOrdinalField(field.toReference())) {
             return;
           }
           if (initializationInfo.isArgumentInitializationInfo()) {
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewInstance.java b/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
index 81af63c..3b8fc7f 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
@@ -189,7 +189,7 @@
             .resolveMethodOnClass(dexItemFactory.objectMembers.finalize, clazz);
     if (finalizeResolutionResult.isSingleResolution()) {
       DexMethod finalizeMethod = finalizeResolutionResult.getSingleTarget().method;
-      if (finalizeMethod != dexItemFactory.enumMethods.finalize
+      if (finalizeMethod != dexItemFactory.enumMembers.finalize
           && finalizeMethod != dexItemFactory.objectMembers.finalize) {
         return true;
       }
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 c902868..0a82859 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
@@ -266,7 +266,7 @@
     }
     for (Instruction user : constClass.outValue().uniqueUsers()) {
       if (!(user.isInvokeStatic()
-          && user.asInvokeStatic().getInvokedMethod() == factory.enumMethods.valueOf)) {
+          && user.asInvokeStatic().getInvokedMethod() == factory.enumMembers.valueOf)) {
         markEnumAsUnboxable(
             Reason.CONST_CLASS, appView.definitionForProgramType(constClass.getValue()));
         return;
@@ -401,7 +401,7 @@
 
       // Enum candidates have necessarily only one constructor matching enumMethods.constructor
       // signature.
-      DexEncodedMethod initializer = enumClass.lookupDirectMethod(factory.enumMethods.constructor);
+      DexEncodedMethod initializer = enumClass.lookupDirectMethod(factory.enumMembers.constructor);
       if (initializer == null) {
         // This case typically happens when a programmer uses EnumSet/EnumMap without using the
         // enum keep rules. The code is incorrect in this case (EnumSet/EnumMap won't work).
@@ -482,19 +482,19 @@
         return Reason.UNSUPPORTED_LIBRARY_CALL;
       }
       // TODO(b/147860220): EnumSet and EnumMap may be interesting to model.
-      if (singleTarget == factory.enumMethods.compareTo) {
+      if (singleTarget == factory.enumMembers.compareTo) {
         return Reason.ELIGIBLE;
-      } else if (singleTarget == factory.enumMethods.equals) {
+      } else if (singleTarget == factory.enumMembers.equals) {
         return Reason.ELIGIBLE;
-      } else if (singleTarget == factory.enumMethods.name) {
+      } else if (singleTarget == factory.enumMembers.nameMethod) {
         return Reason.ELIGIBLE;
-      } else if (singleTarget == factory.enumMethods.toString) {
+      } else if (singleTarget == factory.enumMembers.toString) {
         return Reason.ELIGIBLE;
-      } else if (singleTarget == factory.enumMethods.ordinal) {
+      } else if (singleTarget == factory.enumMembers.ordinalMethod) {
         return Reason.ELIGIBLE;
-      } else if (singleTarget == factory.enumMethods.hashCode) {
+      } else if (singleTarget == factory.enumMembers.hashCode) {
         return Reason.ELIGIBLE;
-      } else if (singleTarget == factory.enumMethods.constructor) {
+      } else if (singleTarget == factory.enumMembers.constructor) {
         // Enum constructor call is allowed only if first call of an enum initializer.
         if (code.method().isInstanceInitializer()
             && code.method().holder() == enumClass.type
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 08ba8b4..c4a6bb4 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
@@ -83,7 +83,7 @@
 
   private boolean isStandardEnumInitializer(DexEncodedMethod method) {
     return method.isInstanceInitializer()
-        && method.method.proto == factory.enumMethods.constructor.proto;
+        && method.method.proto == factory.enumMembers.constructor.proto;
   }
 
   // The enum should have the $VALUES static field and only fields directly referencing the enum
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 1832a2a..087e808 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
@@ -129,21 +129,21 @@
         DexMethod invokedMethod = invokeMethod.getInvokedMethod();
         DexType enumType = getEnumTypeOrNull(invokeMethod.getReceiver(), convertedEnums);
         if (enumType != null) {
-          if (invokedMethod == factory.enumMethods.ordinal
-              || invokedMethod == factory.enumMethods.hashCode) {
+          if (invokedMethod == factory.enumMembers.ordinalMethod
+              || invokedMethod == factory.enumMembers.hashCode) {
             replaceEnumInvoke(
                 iterator, invokeMethod, ordinalUtilityMethod, m -> synthesizeOrdinalMethod());
             continue;
-          } else if (invokedMethod == factory.enumMethods.equals) {
+          } else if (invokedMethod == factory.enumMembers.equals) {
             replaceEnumInvoke(
                 iterator, invokeMethod, equalsUtilityMethod, m -> synthesizeEqualsMethod());
             continue;
-          } else if (invokedMethod == factory.enumMethods.compareTo) {
+          } else if (invokedMethod == factory.enumMembers.compareTo) {
             replaceEnumInvoke(
                 iterator, invokeMethod, compareToUtilityMethod, m -> synthesizeCompareToMethod());
             continue;
-          } else if (invokedMethod == factory.enumMethods.name
-              || invokedMethod == factory.enumMethods.toString) {
+          } else if (invokedMethod == factory.enumMembers.nameMethod
+              || invokedMethod == factory.enumMembers.toString) {
             DexMethod toStringMethod = computeDefaultToStringUtilityMethod(enumType);
             iterator.replaceCurrentInstruction(
                 new InvokeStatic(
@@ -155,7 +155,7 @@
       } else if (instruction.isInvokeStatic()) {
         InvokeStatic invokeStatic = instruction.asInvokeStatic();
         DexMethod invokedMethod = invokeStatic.getInvokedMethod();
-        if (invokedMethod == factory.enumMethods.valueOf
+        if (invokedMethod == factory.enumMembers.valueOf
             && invokeStatic.inValues().get(0).isConstClass()) {
           DexType enumType =
               invokeStatic.inValues().get(0).getConstInstruction().asConstClass().getValue();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java
index 789f4d9..ebe815d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java
@@ -8,6 +8,7 @@
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexItemFactory;
@@ -18,6 +19,10 @@
 import com.android.tools.r8.graph.EnumValueInfoMapCollection.EnumValueInfoMap;
 import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
 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.ObjectState;
+import com.android.tools.r8.ir.analysis.value.SingleNumberValue;
+import com.android.tools.r8.ir.analysis.value.SingleStringValue;
 import com.android.tools.r8.ir.code.ArrayGet;
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.BasicBlock.ThrowingInfo;
@@ -72,15 +77,9 @@
       if (current.isInvokeMethodWithReceiver()) {
         InvokeMethodWithReceiver methodWithReceiver = current.asInvokeMethodWithReceiver();
         DexMethod invokedMethod = methodWithReceiver.getInvokedMethod();
-        boolean isOrdinalInvoke = invokedMethod == factory.enumMethods.ordinal;
-        boolean isNameInvoke = invokedMethod == factory.enumMethods.name;
-        boolean isToStringInvoke = invokedMethod == factory.enumMethods.toString;
-
-        // TODO(b/160667929): Re-enable name()/toString() optimizations.
-        if (!isOrdinalInvoke) {
-          continue;
-        }
-
+        boolean isOrdinalInvoke = invokedMethod == factory.enumMembers.ordinalMethod;
+        boolean isNameInvoke = invokedMethod == factory.enumMembers.nameMethod;
+        boolean isToStringInvoke = invokedMethod == factory.enumMembers.toString;
         if (!isOrdinalInvoke && !isNameInvoke && !isToStringInvoke) {
           continue;
         }
@@ -89,57 +88,98 @@
         if (receiver.isPhi()) {
           continue;
         }
-        Instruction definition = receiver.getDefinition();
-        if (!definition.isStaticGet()) {
-          continue;
-        }
-        DexField enumField = definition.asStaticGet().getField();
-        EnumValueInfoMap valueInfoMap =
-            appView.appInfo().withLiveness().getEnumValueInfoMap(enumField.type);
-        if (valueInfoMap == null) {
+
+        StaticGet staticGet = receiver.getDefinition().asStaticGet();
+        if (staticGet == null) {
           continue;
         }
 
-        // The receiver value is identified as being from a constant enum field lookup by the fact
-        // that it is a static-get to a field whose type is the same as the enclosing class (which
-        // is known to be an enum type). An enum may still define a static field using the enum type
-        // so ensure the field is present in the ordinal map for final validation.
-        EnumValueInfo valueInfo = valueInfoMap.getEnumValueInfo(enumField);
-        if (valueInfo == null) {
+        DexField field = staticGet.getField();
+        DexEncodedField definition = field.lookupOnClass(appView.definitionForHolder(field));
+        if (definition == null) {
+          continue;
+        }
+
+        AbstractValue abstractValue = definition.getOptimizationInfo().getAbstractValue();
+        if (!abstractValue.isSingleFieldValue()) {
+          continue;
+        }
+
+        ObjectState objectState = abstractValue.asSingleFieldValue().getState();
+        if (objectState.isEmpty()) {
           continue;
         }
 
         Value outValue = methodWithReceiver.outValue();
         if (isOrdinalInvoke) {
-          iterator.replaceCurrentInstruction(new ConstNumber(outValue, valueInfo.ordinal));
-        } else if (isNameInvoke) {
-          Value newValue =
-              code.createValue(TypeElement.stringClassType(appView, definitelyNotNull()));
-          iterator.replaceCurrentInstruction(
-              new ConstString(
-                  newValue, enumField.name, ThrowingInfo.defaultForConstString(appView.options())));
-          newValue.addAffectedValuesTo(affectedValues);
-        } else {
-          assert isToStringInvoke;
-          DexClass enumClazz = appView.appInfo().definitionFor(enumField.type);
-          if (!enumClazz.accessFlags.isFinal()) {
-            continue;
+          DexField ordinalField = appView.dexItemFactory().enumMembers.ordinalField;
+          DexEncodedField ordinalDefinition =
+              ordinalField.lookupOnClass(appView.definitionForHolder(ordinalField));
+          if (ordinalDefinition != null) {
+            SingleNumberValue ordinalValue =
+                objectState.getAbstractFieldValue(ordinalDefinition).asSingleNumberValue();
+            if (ordinalValue != null) {
+              iterator.replaceCurrentInstruction(
+                  new ConstNumber(outValue, ordinalValue.getValue()));
+            }
           }
-          DexEncodedMethod singleTarget =
-              appView
-                  .appInfo()
-                  .resolveMethodOnClass(factory.objectMembers.toString, valueInfo.type)
-                  .getSingleTarget();
-          if (singleTarget != null && singleTarget.method != factory.enumMethods.toString) {
-            continue;
-          }
-          Value newValue =
-              code.createValue(TypeElement.stringClassType(appView, definitelyNotNull()));
-          iterator.replaceCurrentInstruction(
-              new ConstString(
-                  newValue, enumField.name, ThrowingInfo.defaultForConstString(appView.options())));
-          newValue.addAffectedValuesTo(affectedValues);
+          continue;
         }
+
+        DexField nameField = appView.dexItemFactory().enumMembers.nameField;
+        DexEncodedField nameDefinition =
+            nameField.lookupOnClass(appView.definitionForHolder(nameField));
+        if (nameField == null) {
+          continue;
+        }
+
+        SingleStringValue nameValue =
+            objectState.getAbstractFieldValue(nameDefinition).asSingleStringValue();
+        if (nameValue == null) {
+          continue;
+        }
+
+        if (isNameInvoke) {
+          Value newValue =
+              code.createValue(TypeElement.stringClassType(appView, definitelyNotNull()));
+          iterator.replaceCurrentInstruction(
+              new ConstString(
+                  newValue,
+                  nameValue.getDexString(),
+                  ThrowingInfo.defaultForConstString(appView.options())));
+          newValue.addAffectedValuesTo(affectedValues);
+          continue;
+        }
+
+        assert isToStringInvoke;
+
+        DexClass enumClazz = appView.appInfo().definitionFor(field.type);
+        if (!enumClazz.isFinal()) {
+          continue;
+        }
+
+        EnumValueInfo valueInfo = appView.appInfo().withLiveness().getEnumValueInfo(field);
+        if (valueInfo == null) {
+          continue;
+        }
+
+        DexEncodedMethod singleTarget =
+            appView
+                .appInfo()
+                .resolveMethodOnClass(factory.objectMembers.toString, valueInfo.type)
+                .getSingleTarget();
+        if (singleTarget != null && singleTarget.method != factory.enumMembers.toString) {
+          continue;
+        }
+
+        Value newValue =
+            code.createValue(TypeElement.stringClassType(appView, definitelyNotNull()));
+        iterator.replaceCurrentInstruction(
+            new ConstString(
+                newValue,
+                nameValue.getDexString(),
+                ThrowingInfo.defaultForConstString(appView.options())));
+        newValue.addAffectedValuesTo(affectedValues);
       } else if (current.isArrayLength()) {
         // Rewrites MyEnum.values().length to a constant int.
         Instruction arrayDefinition = current.asArrayLength().array().getAliasedValue().definition;
@@ -148,7 +188,7 @@
           DexProgramClass enumClass = appView.definitionForProgramType(invokedMethod.holder);
           if (enumClass != null
               && enumClass.isEnum()
-              && factory.enumMethods.isValuesMethod(invokedMethod, enumClass)) {
+              && factory.enumMembers.isValuesMethod(invokedMethod, enumClass)) {
             EnumValueInfoMap enumValueInfoMap =
                 appView.appInfo().withLiveness().getEnumValueInfoMap(invokedMethod.holder);
             if (enumValueInfoMap != null) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
index 5542908..b189a57 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
@@ -563,7 +563,7 @@
                   return null;
                 }
                 // java.lang.Enum.<init>() and java.lang.Object.<init>() are considered trivial.
-                if (invokedMethod == dexItemFactory.enumMethods.constructor
+                if (invokedMethod == dexItemFactory.enumMembers.constructor
                     || invokedMethod == dexItemFactory.objectMembers.constructor) {
                   builder.setParent(invokedMethod);
                   break;
@@ -1098,7 +1098,7 @@
             .resolveMethodOnClass(appView.dexItemFactory().objectMembers.finalize, clazz);
     DexEncodedMethod target = resolutionResult.getSingleTarget();
     return target != null
-        && target.method != dexItemFactory.enumMethods.finalize
+        && target.method != dexItemFactory.enumMembers.finalize
         && target.method != dexItemFactory.objectMembers.finalize;
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
index 822e0c5..62edebf 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
@@ -162,7 +162,7 @@
   @Override
   public void setInstanceInitializerInfo(
       DexEncodedMethod method, InstanceInitializerInfo instanceInitializerInfo) {
-    // Ignored.
+    method.getMutableOptimizationInfo().setInstanceInitializerInfo(instanceInitializerInfo);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfoCollection.java
index d26de6d..5979031 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfoCollection.java
@@ -47,9 +47,10 @@
       recordInitializationInfo(field.field, info);
     }
 
-    public void recordInitializationInfo(DexField field, InstanceFieldInitializationInfo info) {
+    public Builder recordInitializationInfo(DexField field, InstanceFieldInitializationInfo info) {
       assert !infos.containsKey(field);
       infos.put(field, info);
+      return this;
     }
 
     public InstanceFieldInitializationInfoCollection build() {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/EnumMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/EnumMethodOptimizer.java
index cdc1793..658fcb5 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/EnumMethodOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/EnumMethodOptimizer.java
@@ -38,7 +38,7 @@
       InvokeMethod invoke,
       DexEncodedMethod singleTarget,
       Set<Value> affectedValues) {
-    if (singleTarget.method == appView.dexItemFactory().enumMethods.valueOf
+    if (singleTarget.method == appView.dexItemFactory().enumMembers.valueOf
         && invoke.inValues().get(0).isConstClass()) {
       insertAssumeDynamicType(code, instructionIterator, invoke);
     }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryFieldSynthesis.java b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryFieldSynthesis.java
new file mode 100644
index 0000000..3f3277f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryFieldSynthesis.java
@@ -0,0 +1,47 @@
+// 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.library;
+
+import static com.android.tools.r8.graph.DexLibraryClass.asLibraryClassOrNull;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexAnnotationSet;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexLibraryClass;
+import com.android.tools.r8.graph.FieldAccessFlags;
+
+/**
+ * This class synthesizes library fields that we rely on for modeling.
+ *
+ * <p>For example, we synthesize the field `java.lang.String java.lang.Enum.name` if it is not
+ * present. We use this to model that the constructor `void java.lang.Enum.<init>(java.lang.String,
+ * int)` initializes `java.lang.String java.lang.Enum.name` to the first argument of the
+ * constructor.
+ */
+public class LibraryFieldSynthesis {
+
+  public static void synthesizeEnumFields(AppView<?> appView) {
+    DexItemFactory dexItemFactory = appView.dexItemFactory();
+    DexLibraryClass enumClass =
+        asLibraryClassOrNull(appView.definitionFor(dexItemFactory.enumType));
+    if (enumClass != null) {
+      dexItemFactory.enumMembers.forEachField(
+          field -> {
+            DexEncodedField definition = enumClass.lookupField(field);
+            if (definition == null) {
+              enumClass.appendInstanceField(
+                  new DexEncodedField(
+                      field,
+                      FieldAccessFlags.fromCfAccessFlags(
+                          Constants.ACC_PRIVATE | Constants.ACC_FINAL),
+                      DexAnnotationSet.empty(),
+                      null));
+            }
+          });
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryOptimizationInfoInitializer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryOptimizationInfoInitializer.java
index e7e0fe6..d0412b7 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryOptimizationInfoInitializer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryOptimizationInfoInitializer.java
@@ -11,12 +11,16 @@
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexItemFactory.EnumMembers;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.analysis.value.AbstractValueFactory;
 import com.android.tools.r8.ir.analysis.value.ObjectState;
 import com.android.tools.r8.ir.optimize.info.LibraryOptimizationInfoInitializerFeedback;
+import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoCollection;
+import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoFactory;
+import com.android.tools.r8.ir.optimize.info.initializer.NonTrivialInstanceInitializerInfo;
 import com.google.common.collect.Sets;
 import java.util.BitSet;
 import java.util.Set;
@@ -38,6 +42,7 @@
   }
 
   void run(Set<DexEncodedField> finalLibraryFields) {
+    modelInstanceInitializers();
     modelStaticFinalLibraryFields(finalLibraryFields);
     modelLibraryMethodsReturningNonNull();
     modelLibraryMethodsReturningReceiver();
@@ -48,6 +53,28 @@
     return modeledLibraryTypes;
   }
 
+  private void modelInstanceInitializers() {
+    EnumMembers enumMembers = dexItemFactory.enumMembers;
+    DexEncodedMethod enumConstructor = lookupMethod(enumMembers.constructor);
+    if (enumConstructor != null) {
+      LibraryFieldSynthesis.synthesizeEnumFields(appView);
+      InstanceFieldInitializationInfoFactory factory =
+          appView.instanceFieldInitializationInfoFactory();
+      InstanceFieldInitializationInfoCollection fieldInitializationInfos =
+          InstanceFieldInitializationInfoCollection.builder()
+              .recordInitializationInfo(
+                  enumMembers.nameField, factory.createArgumentInitializationInfo(1))
+              .recordInitializationInfo(
+                  enumMembers.ordinalField, factory.createArgumentInitializationInfo(2))
+              .build();
+      feedback.setInstanceInitializerInfo(
+          enumConstructor,
+          NonTrivialInstanceInitializerInfo.builder(fieldInitializationInfos)
+              .setParent(dexItemFactory.objectMembers.constructor)
+              .build());
+    }
+  }
+
   private void modelStaticFinalLibraryFields(Set<DexEncodedField> finalLibraryFields) {
     for (DexEncodedField field : finalLibraryFields) {
       if (field.isStatic()) {
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 5f68d46..e9d4c27 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -22,6 +22,7 @@
 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.EnumValueInfo;
 import com.android.tools.r8.graph.EnumValueInfoMapCollection.EnumValueInfoMap;
 import com.android.tools.r8.graph.FieldAccessInfo;
 import com.android.tools.r8.graph.FieldAccessInfoCollection;
@@ -747,6 +748,12 @@
     return enumValueInfoMaps.getEnumValueInfoMap(enumType);
   }
 
+  public EnumValueInfo getEnumValueInfo(DexField field) {
+    assert checkIfObsolete();
+    EnumValueInfoMap map = enumValueInfoMaps.getEnumValueInfoMap(field.type);
+    return map != null ? map.getEnumValueInfo(field) : null;
+  }
+
   public Int2ReferenceMap<DexField> getSwitchMap(DexField field) {
     assert checkIfObsolete();
     return switchMaps.get(field);
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 6d4939f..df7690c 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -1119,7 +1119,7 @@
       pendingReflectiveUses.add(context);
     }
     // See comment in handleJavaLangEnumValueOf.
-    if (invokedMethod == dexItemFactory.enumMethods.valueOf) {
+    if (invokedMethod == dexItemFactory.enumMembers.valueOf) {
       pendingReflectiveUses.add(context);
     }
     // Handling of application services.
@@ -3666,7 +3666,7 @@
       handleJavaLangReflectConstructorNewInstance(method, invoke);
       return;
     }
-    if (invokedMethod == dexItemFactory.enumMethods.valueOf) {
+    if (invokedMethod == dexItemFactory.enumMembers.valueOf) {
       handleJavaLangEnumValueOf(method, invoke);
       return;
     }
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 1bf5f83..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
@@ -20,7 +20,6 @@
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
 import java.util.List;
 import java.util.Objects;
-import org.junit.Assume;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -75,14 +74,13 @@
     if (enableOptimization) {
       assertOrdinalReplacedWithConst(clazz.uniqueMethodWithName("simple"), 1);
       assertOrdinalReplacedWithConst(clazz.uniqueMethodWithName("local"), 1);
-      // TODO(b/160667929): Re-enable name()/toString() optimizations.
       // String concatenation optimization is enabled for DEX output.
       // Even replaced ordinal is concatenated (and gone).
-      //      if (parameters.isDexRuntime()) {
-      //        assertOrdinalReplacedAndGone(clazz.uniqueMethodWithName("multipleUsages"));
-      //      } else {
-      assertOrdinalReplacedWithConst(clazz.uniqueMethodWithName("multipleUsages"), 1);
-      //      }
+      if (parameters.isDexRuntime()) {
+        assertOrdinalReplacedAndGone(clazz.uniqueMethodWithName("multipleUsages"));
+      } else {
+        assertOrdinalReplacedWithConst(clazz.uniqueMethodWithName("multipleUsages"), 1);
+      }
       assertOrdinalReplacedWithConst(clazz.uniqueMethodWithName("inlined"), 1);
       assertOrdinalReplacedWithConst(clazz.uniqueMethodWithName("inSwitch"), 11);
       assertOrdinalReplacedWithConst(clazz.uniqueMethodWithName("differentTypeStaticField"), 1);
@@ -123,7 +121,6 @@
     assertTrue(clazz.isPresent());
 
     if (enableOptimization) {
-      Assume.assumeTrue("TODO(b/160667929): Re-enable name()/toString() optimizations.", false);
       assertNameReplacedWithConst(clazz.uniqueMethodWithName("simple"), "TWO");
       assertNameReplacedWithConst(clazz.uniqueMethodWithName("local"), "TWO");
       // String concatenation optimization is enabled for DEX output.
@@ -174,7 +171,6 @@
     assertToStringWasNotReplaced(clazz.uniqueMethodWithName("valueWithoutToString"));
 
     if (enableOptimization) {
-      Assume.assumeTrue("TODO(b/160667929): Re-enable name()/toString() optimizations.", false);
       assertToStringReplacedWithConst(clazz.uniqueMethodWithName("noToString"), "TWO");
       assertToStringReplacedWithConst(clazz.uniqueMethodWithName("local"), "TWO");
       assertToStringReplacedWithConst(clazz.uniqueMethodWithName("multipleUsages"), "TWO");