Towards support for recursive inlining in class inliner

Change-Id: Ifaef9bef01002e1224a8e078a976c4b1d3a99d89
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java b/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
index 1a7de86..a24212f 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
@@ -468,6 +468,12 @@
     return !getTypeInfo(type).directSubtypes.isEmpty();
   }
 
+  public boolean isRelatedBySubtyping(DexType type, DexType other) {
+    assert type.isClassType();
+    assert other.isClassType();
+    return isSubtype(type, other) || isSubtype(other, type);
+  }
+
   @Override
   public boolean isSubtype(DexType subtype, DexType supertype) {
     if (subtype == supertype || isStrictSubtypeOf(subtype, supertype)) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerEligibilityInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerEligibilityInfo.java
index a80a9ec..bf06bc1 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerEligibilityInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerEligibilityInfo.java
@@ -4,10 +4,16 @@
 
 package com.android.tools.r8.ir.optimize.classinliner;
 
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.ir.code.Invoke;
 import com.android.tools.r8.utils.OptionalBool;
+import com.android.tools.r8.utils.Pair;
+import java.util.List;
 
 public class ClassInlinerEligibilityInfo {
 
+  final List<Pair<Invoke.Type, DexMethod>> callsReceiver;
+
   /**
    * Set to {@link OptionalBool#TRUE} if the method is guaranteed to return the receiver, {@link
    * OptionalBool#FALSE} if the method is guaranteed not to return the receiver, and {@link
@@ -15,7 +21,9 @@
    */
   final OptionalBool returnsReceiver;
 
-  public ClassInlinerEligibilityInfo(OptionalBool returnsReceiver) {
+  public ClassInlinerEligibilityInfo(
+      List<Pair<Invoke.Type, DexMethod>> callsReceiver, OptionalBool returnsReceiver) {
+    this.callsReceiver = callsReceiver;
     this.returnsReceiver = returnsReceiver;
   }
 }
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 a831b86..c522ff3 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
@@ -334,8 +334,7 @@
                       && !invoke.inValues().isEmpty()
                       && root.outValue() == invoke.getReceiver();
               if (isCorrespondingConstructorCall) {
-                InliningInfo inliningInfo =
-                    isEligibleConstructorCall(invoke, singleTarget, defaultOracle);
+                InliningInfo inliningInfo = isEligibleConstructorCall(invoke, singleTarget);
                 if (inliningInfo != null) {
                   methodCallsOnInstance.put(invoke, inliningInfo);
                   continue;
@@ -676,7 +675,7 @@
   }
 
   private InliningInfo isEligibleConstructorCall(
-      InvokeDirect invoke, DexEncodedMethod singleTarget, Supplier<InliningOracle> defaultOracle) {
+      InvokeDirect invoke, DexEncodedMethod singleTarget) {
     assert appView.dexItemFactory().isConstructor(invoke.getInvokedMethod());
     assert isEligibleSingleTarget(singleTarget);
 
@@ -702,8 +701,9 @@
     }
 
     // Check that the `eligibleInstance` does not escape via the constructor.
-    ParameterUsage parameterUsage = singleTarget.getOptimizationInfo().getParameterUsages(0);
-    if (!isEligibleParameterUsage(parameterUsage, invoke, defaultOracle)) {
+    InstanceInitializerInfo instanceInitializerInfo =
+        singleTarget.getOptimizationInfo().getInstanceInitializerInfo();
+    if (instanceInitializerInfo.receiverMayEscapeOutsideConstructorChain()) {
       return null;
     }
 
@@ -715,7 +715,7 @@
 
     // Check that the entire constructor chain can be inlined into the current context.
     DexItemFactory dexItemFactory = appView.dexItemFactory();
-    DexMethod parent = singleTarget.getOptimizationInfo().getInstanceInitializerInfo().getParent();
+    DexMethod parent = instanceInitializerInfo.getParent();
     while (parent != dexItemFactory.objectMethods.constructor) {
       if (parent == null) {
         return null;
@@ -734,9 +734,7 @@
       parent = encodedParent.getOptimizationInfo().getInstanceInitializerInfo().getParent();
     }
 
-    return singleTarget.getOptimizationInfo().getClassInlinerEligibility() != null
-        ? new InliningInfo(singleTarget, eligibleClass)
-        : null;
+    return new InliningInfo(singleTarget, eligibleClass);
   }
 
   // An invoke is eligible for inlining in the following cases:
@@ -876,7 +874,7 @@
 
     MethodOptimizationInfo optimizationInfo = singleTarget.getOptimizationInfo();
     ClassInlinerEligibilityInfo eligibility = optimizationInfo.getClassInlinerEligibility();
-    if (eligibility == null) {
+    if (eligibility == null || !eligibility.callsReceiver.isEmpty()) {
       return 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 172076a..1fb2e0c 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
@@ -67,9 +67,11 @@
 import com.android.tools.r8.ir.code.InstancePut;
 import com.android.tools.r8.ir.code.Instruction;
 import com.android.tools.r8.ir.code.InstructionIterator;
+import com.android.tools.r8.ir.code.Invoke;
 import com.android.tools.r8.ir.code.InvokeDirect;
 import com.android.tools.r8.ir.code.InvokeMethod;
 import com.android.tools.r8.ir.code.InvokeNewArray;
+import com.android.tools.r8.ir.code.InvokeVirtual;
 import com.android.tools.r8.ir.code.NewInstance;
 import com.android.tools.r8.ir.code.Return;
 import com.android.tools.r8.ir.code.Value;
@@ -86,6 +88,7 @@
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.MethodSignatureEquivalence;
+import com.android.tools.r8.utils.Pair;
 import com.google.common.base.Equivalence.Wrapper;
 import com.google.common.collect.Sets;
 import com.google.common.collect.Streams;
@@ -161,6 +164,7 @@
       return;
     }
 
+    List<Pair<Invoke.Type, DexMethod>> callsReceiver = new ArrayList<>();
     boolean seenSuperInitCall = false;
     for (Instruction insn : receiver.aliasedUsers()) {
       if (insn.isAssume()) {
@@ -208,6 +212,27 @@
         return;
       }
 
+      if (insn.isInvokeVirtual()) {
+        InvokeVirtual invoke = insn.asInvokeVirtual();
+        if (invoke.getReceiver().getAliasedValue() != receiver) {
+          return; // Not allowed.
+        }
+        for (int i = 1; i < invoke.arguments().size(); i++) {
+          Value argument = invoke.arguments().get(i);
+          if (argument.getAliasedValue() == receiver) {
+            return; // Not allowed.
+          }
+        }
+        DexMethod invokedMethod = invoke.getInvokedMethod();
+        DexType returnType = invokedMethod.proto.returnType;
+        if (returnType.isClassType()
+            && appView.appInfo().isRelatedBySubtyping(returnType, method.method.holder)) {
+          return; // Not allowed, could introduce an alias of the receiver.
+        }
+        callsReceiver.add(new Pair<>(Invoke.Type.VIRTUAL, invokedMethod));
+        continue;
+      }
+
       if (insn.isReturn()) {
         continue;
       }
@@ -224,6 +249,7 @@
     feedback.setClassInlinerEligibility(
         method,
         new ClassInlinerEligibilityInfo(
+            callsReceiver,
             new ClassInlinerReceiverAnalysis(appView, method, code).computeReturnsReceiver()));
   }