Version 2.1.51

Cherry-pick: Fix the fix for enum single field values.
CL: https://r8-review.googlesource.com/c/r8/+/52528

Cherry-pick: Determine enum single field values using holder.
CL: https://r8-review.googlesource.com/c/r8/+/52525
Bug: 160831625

Cherry-pick: Remove trivial phis in class inliner.
CL: https://r8-review.googlesource.com/c/r8/+/52507
Bug: 160394262
Change-Id: If3d67b8c3ebcfbc958e54bd705bdb3ec33842cef
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index e9f227a..5f079ea 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
 
   // This field is accessed from release scripts using simple pattern matching.
   // Therefore, changing this field could break our release scripts.
-  public static final String LABEL = "2.1.50";
+  public static final String LABEL = "2.1.51";
 
   private Version() {
   }
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 14dee6f..7bdcc84 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
@@ -109,12 +109,14 @@
   @Override
   public SingleValue rewrittenWithLens(AppView<AppInfoWithLiveness> appView, GraphLense lens) {
     AbstractValueFactory factory = appView.abstractValueFactory();
-    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());
+    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());
+      }
     }
     return factory.createSingleFieldValue(
         lens.lookupField(field), getState().rewrittenWithLens(appView, lens));
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerCostAnalysis.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerCostAnalysis.java
index f80e1d7..3feac87 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerCostAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerCostAnalysis.java
@@ -12,11 +12,8 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.analysis.type.TypeElement;
-import com.android.tools.r8.ir.code.AssumeAndCheckCastAliasedValueConfiguration;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.Instruction;
-import com.android.tools.r8.ir.code.InstructionIterator;
 import com.android.tools.r8.ir.code.InvokeMethod;
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.ir.optimize.inliner.InliningIRProvider;
@@ -26,7 +23,6 @@
 import java.util.Map;
 import java.util.Set;
 
-// TODO(b/160634549): Rename or refactor this to reflect its non-cost related analysis.
 /** Analysis that estimates the cost of class inlining an object allocation. */
 class ClassInlinerCostAnalysis {
 
@@ -77,28 +73,6 @@
         continue;
       }
       IRCode inliningIR = inliningIRProvider.getAndCacheInliningIR(invoke, inlinee);
-
-      // If the instance is part of a phi, then inlining will invalidate the inliner assumptions.
-      // TODO(b/160634549): This is not a budget miss but a hard requirement.
-      InstructionIterator iterator = inliningIR.entryBlock().iterator();
-      while (iterator.hasNext()) {
-        Instruction next = iterator.next();
-        if (!next.isArgument()) {
-          break;
-        }
-        Value argumentValue = next.outValue();
-        TypeElement argumentType = argumentValue.getType();
-        if (argumentType.isClassType()
-            && argumentType.asClassType().getClassType() == eligibleClass.type) {
-          assert argumentValue.uniqueUsers().stream()
-              .noneMatch(
-                  AssumeAndCheckCastAliasedValueConfiguration.getInstance()::isIntroducingAnAlias);
-          if (argumentValue.hasPhiUsers()) {
-            return true;
-          }
-        }
-      }
-
       int increment =
           inlinee.getDefinition().getCode().estimatedSizeForInlining()
               - estimateNumberOfNonMaterializingInstructions(invoke, inliningIR);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
index da8c4cc..74d07d2 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
@@ -8,6 +8,7 @@
 import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
 import static com.google.common.base.Predicates.alwaysFalse;
 
+import com.android.tools.r8.errors.InternalCompilerError;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
@@ -40,6 +41,7 @@
 import com.android.tools.r8.ir.code.InvokeDirect;
 import com.android.tools.r8.ir.code.InvokeMethod;
 import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
+import com.android.tools.r8.ir.code.Phi;
 import com.android.tools.r8.ir.code.StaticGet;
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.ir.conversion.MethodProcessor;
@@ -57,10 +59,10 @@
 import com.android.tools.r8.ir.optimize.inliner.NopWhyAreYouNotInliningReporter;
 import com.android.tools.r8.kotlin.KotlinClassLevelInfo;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.Pair;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.Timing;
+import com.android.tools.r8.utils.WorkList;
 import com.android.tools.r8.utils.collections.ProgramMethodSet;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
@@ -568,11 +570,35 @@
     while (!currentUsers.isEmpty()) {
       Set<Instruction> indirectOutValueUsers = Sets.newIdentityHashSet();
       for (Instruction instruction : currentUsers) {
-        if (instruction.isAssume() || instruction.isCheckCast()) {
-          Value src = ListUtils.first(instruction.inValues());
+        if (aliasesThroughAssumeAndCheckCasts.isIntroducingAnAlias(instruction)) {
+          Value src = aliasesThroughAssumeAndCheckCasts.getAliasForOutValue(instruction);
           Value dest = instruction.outValue();
-          indirectOutValueUsers.addAll(dest.uniqueUsers());
+          if (dest.hasPhiUsers()) {
+            // It is possible that a trivial phi is constructed upon IR building for the eligible
+            // value. It must actually be trivial so verify that it is indeed trivial and replace
+            // all of the phis involved with the value.
+            WorkList<Phi> worklist = WorkList.newIdentityWorkList(dest.uniquePhiUsers());
+            while (worklist.hasNext()) {
+              Phi phi = worklist.next();
+              for (Value operand : phi.getOperands()) {
+                operand = operand.getAliasedValue(aliasesThroughAssumeAndCheckCasts);
+                if (operand.isPhi()) {
+                  worklist.addIfNotSeen(operand.asPhi());
+                } else if (src != operand) {
+                  throw new InternalCompilerError(
+                      "Unexpected non-trivial phi in method eligible for class inlining");
+                }
+              }
+            }
+            // The only value flowing into any of the phis is src, so replace all phis by src.
+            for (Phi phi : worklist.getSeenSet()) {
+              indirectOutValueUsers.addAll(phi.uniqueUsers());
+              phi.replaceUsers(src);
+              phi.removeDeadPhi();
+            }
+          }
           assert !dest.hasPhiUsers();
+          indirectOutValueUsers.addAll(dest.uniqueUsers());
           dest.replaceUsers(src);
           removeInstruction(instruction);
         }
diff --git a/src/main/java/com/android/tools/r8/utils/WorkList.java b/src/main/java/com/android/tools/r8/utils/WorkList.java
index 3e35802..c6e554a 100644
--- a/src/main/java/com/android/tools/r8/utils/WorkList.java
+++ b/src/main/java/com/android/tools/r8/utils/WorkList.java
@@ -6,6 +6,7 @@
 
 import com.google.common.collect.Sets;
 import java.util.ArrayDeque;
+import java.util.Collections;
 import java.util.Deque;
 import java.util.HashSet;
 import java.util.Set;
@@ -70,6 +71,10 @@
     return workingList.removeFirst();
   }
 
+  public Set<T> getSeenSet() {
+    return Collections.unmodifiableSet(seen);
+  }
+
   public enum EqualityTest {
     HASH,
     IDENTITY
diff --git a/src/test/java/com/android/tools/r8/regress/Regress160394262Test.java b/src/test/java/com/android/tools/r8/regress/Regress160394262Test.java
index 0c3206c..383a7ef 100644
--- a/src/test/java/com/android/tools/r8/regress/Regress160394262Test.java
+++ b/src/test/java/com/android/tools/r8/regress/Regress160394262Test.java
@@ -57,15 +57,13 @@
   }
 
   private void checkJoinerIsClassInlined(CodeInspector inspector) {
-    // TODO(b/160640028): When compiling to DEX a trivial phi remains in the inline code preventing
-    //  class inlining of Joiner and the anonymous Joiner subclass.
+    assertThat(inspector.clazz(Joiner.class.getTypeName() + "$1"), not(isPresent()));
+    // TODO(b/160640028): When compiling to DEX the outer Joiner class is not inlined.
     if (parameters.isDexRuntime()) {
       assertThat(inspector.clazz(Joiner.class), isPresent());
-      assertThat(inspector.clazz(Joiner.class.getTypeName() + "$1"), isPresent());
-      return;
+    } else {
+      assertThat(inspector.clazz(Joiner.class), not(isPresent()));
     }
-    assertThat(inspector.clazz(Joiner.class), not(isPresent()));
-    assertThat(inspector.clazz(Joiner.class.getTypeName() + "$1"), not(isPresent()));
   }
 
   static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/regress/Regress160831625Test.java b/src/test/java/com/android/tools/r8/regress/Regress160831625Test.java
new file mode 100644
index 0000000..15c0c75
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/regress/Regress160831625Test.java
@@ -0,0 +1,72 @@
+// 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.regress;
+
+import com.android.tools.r8.NeverPropagateValue;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.StringUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class Regress160831625Test extends TestBase {
+
+  static final String EXPECTED = StringUtils.lines("A", "0");
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimes().withAllApiLevels().build();
+  }
+
+  public Regress160831625Test(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testReference() throws Exception {
+    testForRuntime(parameters)
+        .addInnerClasses(getClass())
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(EXPECTED);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .setMinApi(parameters.getApiLevel())
+        .addInnerClasses(getClass())
+        .enableMemberValuePropagationAnnotations()
+        .addKeepMainRule(TestClass.class)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(EXPECTED);
+  }
+
+  enum MyEnum {
+    // Ensure that the enum field value is not inlined in the alias in MyClass.B
+    @NeverPropagateValue
+    A
+  }
+
+  static class MyClass {
+
+    private static final MyEnum B = MyEnum.A;
+
+    public static MyEnum getB() {
+      return B;
+    }
+  }
+
+  static class TestClass {
+
+    public static void main(String[] args) {
+      System.out.println(MyClass.getB().name());
+      System.out.println(MyClass.getB().ordinal());
+    }
+  }
+}