Collect the set of root aliases in the class inliner

This will be used to improve the precision of the class inliner's cost analysis. For example, reading a field from the root or an alias of the root should not contribute to the cost.

Change-Id: Ic0ad43c8b8d62febc276985a622c7699795aff54
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 90c746b..3e11ca6 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
@@ -44,6 +44,7 @@
 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.SetUtils;
 import com.android.tools.r8.utils.StringUtils;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
@@ -84,6 +85,11 @@
 
   private int estimatedCombinedSizeForInlining = 0;
 
+  // Set of values that may be an alias of the "root" instance (including the root instance itself).
+  // TODO(b/144825216): Distinguish the "may-aliases" from the "must-aliases" such that the cost
+  //  analysis is not optimistic.
+  private final Set<Value> receivers;
+
   InlineCandidateProcessor(
       AppView<AppInfoWithLiveness> appView,
       LambdaRewriter lambdaRewriter,
@@ -99,12 +105,17 @@
     this.method = method;
     this.root = root;
     this.isProcessedConcurrently = isProcessedConcurrently;
+    this.receivers = SetUtils.newIdentityHashSet(root.outValue());
   }
 
   int getEstimatedCombinedSizeForInlining() {
     return estimatedCombinedSizeForInlining;
   }
 
+  Set<Value> getReceivers() {
+    return receivers;
+  }
+
   // Checks if the root instruction defines eligible value, i.e. the value
   // exists and we have a definition of its class.
   EligibilityStatus isInstanceEligible() {
@@ -258,7 +269,7 @@
    */
   InstructionOrPhi areInstanceUsersEligible(Supplier<InliningOracle> defaultOracle) {
     // No Phi users.
-    if (eligibleInstance.numberOfPhiUsers() > 0) {
+    if (eligibleInstance.hasPhiUsers()) {
       return eligibleInstance.firstPhiUser(); // Not eligible.
     }
 
@@ -267,10 +278,12 @@
       Set<Instruction> indirectUsers = Sets.newIdentityHashSet();
       for (Instruction user : currentUsers) {
         if (user.isAssume()) {
-          if (user.outValue().numberOfPhiUsers() > 0) {
-            return user.outValue().firstPhiUser(); // Not eligible.
+          Value alias = user.outValue();
+          if (alias.hasPhiUsers()) {
+            return alias.firstPhiUser(); // Not eligible.
           }
-          indirectUsers.addAll(user.outValue().uniqueUsers());
+          receivers.add(alias);
+          indirectUsers.addAll(alias.uniqueUsers());
           continue;
         }
         // Field read/write.
@@ -434,6 +447,9 @@
     if (methodCallsOnInstance.isEmpty()) {
       return false;
     }
+    assert methodCallsOnInstance.keySet().stream()
+        .map(InvokeMethodWithReceiver::getReceiver)
+        .allMatch(receivers::contains);
     inliner.performForcedInlining(method, code, methodCallsOnInstance);
     return true;
   }
@@ -446,6 +462,7 @@
       Assume<?> assumeInstruction = user.asAssume();
       Value src = assumeInstruction.src();
       Value dest = assumeInstruction.outValue();
+      assert receivers.contains(dest);
       assert !dest.hasPhiUsers();
       dest.replaceUsers(src);
       removeInstruction(user);
@@ -647,7 +664,7 @@
   // - if it is a regular chaining pattern where the only users of the out value are receivers to
   //   other invocations. In that case, we should add all indirect users of the out value to ensure
   //   they can also be inlined.
-  private static boolean isEligibleInvokeWithAllUsersAsReceivers(
+  private boolean isEligibleInvokeWithAllUsersAsReceivers(
       ClassInlinerEligibility eligibility,
       InvokeMethodWithReceiver invoke,
       Set<Instruction> indirectUsers) {
@@ -667,6 +684,10 @@
       return false;
     }
 
+    // Since the invoke-instruction may return the receiver, the out-value may be an alias of the
+    // receiver.
+    receivers.add(outValue);
+
     Set<Instruction> currentUsers = outValue.uniqueUsers();
     while (!currentUsers.isEmpty()) {
       Set<Instruction> indirectOutValueUsers = Sets.newIdentityHashSet();
@@ -676,6 +697,7 @@
           if (outValueAlias.hasPhiUsers() || outValueAlias.hasDebugUsers()) {
             return false;
           }
+          receivers.add(outValueAlias);
           indirectOutValueUsers.addAll(outValueAlias.uniqueUsers());
           continue;
         }