Refactor class inlining eligibility check to new ClassInlinerMethodConstraint

Bug: 173337498
Change-Id: If23d96e0a050a934893de0d5edfe2f49e0d78569
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java b/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java
index b1b14fb..0cdf967 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java
@@ -14,6 +14,7 @@
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.classinliner.ClassInlinerEligibilityInfo;
+import com.android.tools.r8.ir.optimize.classinliner.constraint.ClassInlinerMethodConstraint;
 import com.android.tools.r8.ir.optimize.info.ParameterUsagesInfo;
 import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
 import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfoCollection;
@@ -60,6 +61,9 @@
 
   void setBridgeInfo(DexEncodedMethod method, BridgeInfo bridgeInfo);
 
+  void setClassInlinerMethodConstraint(
+      ProgramMethod method, ClassInlinerMethodConstraint classInlinerConstraint);
+
   void setClassInlinerEligibility(DexEncodedMethod method, ClassInlinerEligibilityInfo eligibility);
 
   void setInstanceInitializerInfoCollection(
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 35eaaf5..78e756d 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
@@ -21,8 +21,8 @@
    */
   final OptionalBool returnsReceiver;
 
-  final boolean hasMonitorOnReceiver;
-  final boolean modifiesInstanceFields;
+  public final boolean hasMonitorOnReceiver;
+  public final boolean modifiesInstanceFields;
 
   public ClassInlinerEligibilityInfo(
       List<Pair<Invoke.Type, DexMethod>> callsReceiver,
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 371b79a..32e25f1 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
@@ -53,6 +53,7 @@
 import com.android.tools.r8.ir.optimize.Inliner.Reason;
 import com.android.tools.r8.ir.optimize.InliningOracle;
 import com.android.tools.r8.ir.optimize.classinliner.ClassInliner.EligibilityStatus;
+import com.android.tools.r8.ir.optimize.classinliner.constraint.ClassInlinerMethodConstraint;
 import com.android.tools.r8.ir.optimize.info.FieldOptimizationInfo;
 import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo;
 import com.android.tools.r8.ir.optimize.info.ParameterUsagesInfo.ParameterUsage;
@@ -1096,33 +1097,22 @@
     }
 
     MethodOptimizationInfo optimizationInfo = singleTarget.getDefinition().getOptimizationInfo();
-    ClassInlinerEligibilityInfo eligibility = optimizationInfo.getClassInlinerEligibility();
-    if (eligibility == null) {
-      return false;
-    }
-
-    if (root.isStaticGet()) {
-      // If we are class inlining a singleton instance from a static-get, then we don't know the
-      // value of the fields.
-      ParameterUsage receiverUsage = optimizationInfo.getParameterUsages(0);
-      if (receiverUsage == null || receiverUsage.hasFieldRead) {
+    ClassInlinerMethodConstraint classInlinerMethodConstraint =
+        optimizationInfo.getClassInlinerMethodConstraint();
+    if (root.isNewInstance()) {
+      if (!classInlinerMethodConstraint.isEligibleForNewInstanceClassInlining(singleTarget)) {
         return false;
       }
-      if (eligibility.hasMonitorOnReceiver) {
-        // We will not be able to remove the monitor instruction afterwards.
-        return false;
-      }
-      if (eligibility.modifiesInstanceFields) {
-        // The static instance could be accessed from elsewhere. Therefore, we cannot
-        // allow side-effects to be removed and therefore cannot class inline method
-        // calls that modifies the instance.
+    } else {
+      assert root.isStaticGet();
+      if (!classInlinerMethodConstraint.isEligibleForStaticGetClassInlining(singleTarget)) {
         return false;
       }
     }
 
     // If the method returns receiver and the return value is actually
     // used in the code we need to make some additional checks.
-    if (!eligibilityAcceptanceCheck.test(eligibility)) {
+    if (!eligibilityAcceptanceCheck.test(optimizationInfo.getClassInlinerEligibility())) {
       return false;
     }
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/AlwaysFalseClassInlinerMethodConstraint.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/AlwaysFalseClassInlinerMethodConstraint.java
new file mode 100644
index 0000000..c441134
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/AlwaysFalseClassInlinerMethodConstraint.java
@@ -0,0 +1,29 @@
+// Copyright (c) 2021, 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.classinliner.constraint;
+
+import com.android.tools.r8.graph.ProgramMethod;
+
+public class AlwaysFalseClassInlinerMethodConstraint implements ClassInlinerMethodConstraint {
+
+  private static final AlwaysFalseClassInlinerMethodConstraint INSTANCE =
+      new AlwaysFalseClassInlinerMethodConstraint();
+
+  private AlwaysFalseClassInlinerMethodConstraint() {}
+
+  public static AlwaysFalseClassInlinerMethodConstraint getInstance() {
+    return INSTANCE;
+  }
+
+  @Override
+  public boolean isEligibleForNewInstanceClassInlining(ProgramMethod method) {
+    return false;
+  }
+
+  @Override
+  public boolean isEligibleForStaticGetClassInlining(ProgramMethod method) {
+    return false;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/AlwaysTrueClassInlinerMethodConstraint.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/AlwaysTrueClassInlinerMethodConstraint.java
new file mode 100644
index 0000000..3488111
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/AlwaysTrueClassInlinerMethodConstraint.java
@@ -0,0 +1,29 @@
+// Copyright (c) 2021, 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.classinliner.constraint;
+
+import com.android.tools.r8.graph.ProgramMethod;
+
+public class AlwaysTrueClassInlinerMethodConstraint implements ClassInlinerMethodConstraint {
+
+  private static final AlwaysTrueClassInlinerMethodConstraint INSTANCE =
+      new AlwaysTrueClassInlinerMethodConstraint();
+
+  private AlwaysTrueClassInlinerMethodConstraint() {}
+
+  public static AlwaysTrueClassInlinerMethodConstraint getInstance() {
+    return INSTANCE;
+  }
+
+  @Override
+  public boolean isEligibleForNewInstanceClassInlining(ProgramMethod method) {
+    return true;
+  }
+
+  @Override
+  public boolean isEligibleForStaticGetClassInlining(ProgramMethod method) {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/ClassInlinerMethodConstraint.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/ClassInlinerMethodConstraint.java
new file mode 100644
index 0000000..e8ab3ab
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/ClassInlinerMethodConstraint.java
@@ -0,0 +1,14 @@
+// Copyright (c) 2021, 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.classinliner.constraint;
+
+import com.android.tools.r8.graph.ProgramMethod;
+
+public interface ClassInlinerMethodConstraint {
+
+  boolean isEligibleForNewInstanceClassInlining(ProgramMethod method);
+
+  boolean isEligibleForStaticGetClassInlining(ProgramMethod method);
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/ClassInlinerMethodConstraintAnalysis.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/ClassInlinerMethodConstraintAnalysis.java
new file mode 100644
index 0000000..fea67a4
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/ClassInlinerMethodConstraintAnalysis.java
@@ -0,0 +1,73 @@
+// Copyright (c) 2021, 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.classinliner.constraint;
+
+import com.android.tools.r8.ir.optimize.classinliner.ClassInlinerEligibilityInfo;
+import com.android.tools.r8.ir.optimize.info.ParameterUsagesInfo;
+import com.android.tools.r8.ir.optimize.info.ParameterUsagesInfo.ParameterUsage;
+
+public class ClassInlinerMethodConstraintAnalysis {
+
+  public static ClassInlinerMethodConstraint analyze(
+      ClassInlinerEligibilityInfo classInlinerEligibilityInfo,
+      ParameterUsagesInfo parameterUsagesInfo) {
+    boolean isEligibleForNewInstanceClassInlining =
+        isEligibleForNewInstanceClassInlining(classInlinerEligibilityInfo);
+    boolean isEligibleForStaticGetClassInlining =
+        isEligibleForStaticGetClassInlining(classInlinerEligibilityInfo, parameterUsagesInfo);
+    if (isEligibleForNewInstanceClassInlining) {
+      if (isEligibleForStaticGetClassInlining) {
+        return alwaysTrue();
+      }
+      return onlyNewInstanceClassInlining();
+    }
+    assert !isEligibleForStaticGetClassInlining;
+    return alwaysFalse();
+  }
+
+  private static boolean isEligibleForNewInstanceClassInlining(
+      ClassInlinerEligibilityInfo classInlinerEligibilityInfo) {
+    return classInlinerEligibilityInfo != null;
+  }
+
+  private static boolean isEligibleForStaticGetClassInlining(
+      ClassInlinerEligibilityInfo classInlinerEligibilityInfo,
+      ParameterUsagesInfo parameterUsagesInfo) {
+    if (classInlinerEligibilityInfo == null || parameterUsagesInfo == null) {
+      return false;
+    }
+    if (classInlinerEligibilityInfo.hasMonitorOnReceiver) {
+      // We will not be able to remove the monitor instruction afterwards.
+      return false;
+    }
+    if (classInlinerEligibilityInfo.modifiesInstanceFields) {
+      // The static instance could be accessed from elsewhere. Therefore, we cannot allow
+      // side-effects to be removed and therefore cannot class inline method calls that modifies the
+      // instance.
+      return false;
+    }
+    ParameterUsage receiverUsage = parameterUsagesInfo.getParameterUsage(0);
+    if (receiverUsage == null) {
+      return false;
+    }
+    if (receiverUsage.hasFieldRead) {
+      // We don't know the value of the field.
+      return false;
+    }
+    return true;
+  }
+
+  private static AlwaysFalseClassInlinerMethodConstraint alwaysFalse() {
+    return AlwaysFalseClassInlinerMethodConstraint.getInstance();
+  }
+
+  private static AlwaysTrueClassInlinerMethodConstraint alwaysTrue() {
+    return AlwaysTrueClassInlinerMethodConstraint.getInstance();
+  }
+
+  private static OnlyNewInstanceClassInlinerMethodConstraint onlyNewInstanceClassInlining() {
+    return OnlyNewInstanceClassInlinerMethodConstraint.getInstance();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/OnlyNewInstanceClassInlinerMethodConstraint.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/OnlyNewInstanceClassInlinerMethodConstraint.java
new file mode 100644
index 0000000..d3f35d4
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/OnlyNewInstanceClassInlinerMethodConstraint.java
@@ -0,0 +1,29 @@
+// Copyright (c) 2021, 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.classinliner.constraint;
+
+import com.android.tools.r8.graph.ProgramMethod;
+
+public class OnlyNewInstanceClassInlinerMethodConstraint implements ClassInlinerMethodConstraint {
+
+  private static final OnlyNewInstanceClassInlinerMethodConstraint INSTANCE =
+      new OnlyNewInstanceClassInlinerMethodConstraint();
+
+  private OnlyNewInstanceClassInlinerMethodConstraint() {}
+
+  public static OnlyNewInstanceClassInlinerMethodConstraint getInstance() {
+    return INSTANCE;
+  }
+
+  @Override
+  public boolean isEligibleForNewInstanceClassInlining(ProgramMethod method) {
+    return true;
+  }
+
+  @Override
+  public boolean isEligibleForStaticGetClassInlining(ProgramMethod method) {
+    return false;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
index a9854ee..45764df 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
@@ -13,6 +13,8 @@
 import com.android.tools.r8.ir.analysis.value.UnknownValue;
 import com.android.tools.r8.ir.code.InvokeDirect;
 import com.android.tools.r8.ir.optimize.classinliner.ClassInlinerEligibilityInfo;
+import com.android.tools.r8.ir.optimize.classinliner.constraint.AlwaysFalseClassInlinerMethodConstraint;
+import com.android.tools.r8.ir.optimize.classinliner.constraint.ClassInlinerMethodConstraint;
 import com.android.tools.r8.ir.optimize.info.ParameterUsagesInfo.ParameterUsage;
 import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
 import com.android.tools.r8.ir.optimize.info.initializer.DefaultInstanceInitializerInfo;
@@ -74,6 +76,11 @@
   }
 
   @Override
+  public ClassInlinerMethodConstraint getClassInlinerMethodConstraint() {
+    return AlwaysFalseClassInlinerMethodConstraint.getInstance();
+  }
+
+  @Override
   public TypeElement getDynamicUpperBoundType() {
     return UNKNOWN_TYPE;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
index 93a330e..7cced9d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
@@ -11,6 +11,7 @@
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
 import com.android.tools.r8.ir.code.InvokeDirect;
 import com.android.tools.r8.ir.optimize.classinliner.ClassInlinerEligibilityInfo;
+import com.android.tools.r8.ir.optimize.classinliner.constraint.ClassInlinerMethodConstraint;
 import com.android.tools.r8.ir.optimize.info.ParameterUsagesInfo.ParameterUsage;
 import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
 import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
@@ -35,6 +36,8 @@
 
   public abstract boolean classInitializerMayBePostponed();
 
+  public abstract ClassInlinerMethodConstraint getClassInlinerMethodConstraint();
+
   public abstract TypeElement getDynamicUpperBoundType();
 
   public final TypeElement getDynamicUpperBoundTypeOrElse(TypeElement orElse) {
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 7432e43..804b6b3 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
@@ -90,6 +90,8 @@
 import com.android.tools.r8.ir.optimize.DynamicTypeOptimization;
 import com.android.tools.r8.ir.optimize.classinliner.ClassInlinerEligibilityInfo;
 import com.android.tools.r8.ir.optimize.classinliner.ClassInlinerReceiverAnalysis;
+import com.android.tools.r8.ir.optimize.classinliner.constraint.ClassInlinerMethodConstraint;
+import com.android.tools.r8.ir.optimize.classinliner.constraint.ClassInlinerMethodConstraintAnalysis;
 import com.android.tools.r8.ir.optimize.info.ParameterUsagesInfo.ParameterUsage;
 import com.android.tools.r8.ir.optimize.info.ParameterUsagesInfo.ParameterUsageBuilder;
 import com.android.tools.r8.ir.optimize.info.bridge.BridgeAnalyzer;
@@ -143,12 +145,16 @@
       Timing timing) {
     DexEncodedMethod definition = method.getDefinition();
     identifyBridgeInfo(definition, code, feedback, timing);
-    identifyClassInlinerEligibility(code, feedback, timing);
-    identifyParameterUsages(definition, code, feedback, timing);
+    ClassInlinerEligibilityInfo classInlinerEligibilityInfo =
+        identifyClassInlinerEligibility(code, feedback, timing);
+    ParameterUsagesInfo parameterUsagesInfo =
+        identifyParameterUsages(definition, code, feedback, timing);
     analyzeReturns(code, feedback, timing);
     if (options.enableInlining) {
       identifyInvokeSemanticsForInlining(definition, code, feedback, timing);
     }
+    computeClassInlinerMethodConstraint(
+        method, code, feedback, classInlinerEligibilityInfo, parameterUsagesInfo, timing);
     computeSimpleInliningConstraint(method, code, feedback, timing);
     computeDynamicReturnType(dynamicTypeOptimization, feedback, definition, code, timing);
     computeInitializedClassesOnNormalExit(feedback, definition, code, timing);
@@ -167,14 +173,17 @@
     timing.end();
   }
 
-  private void identifyClassInlinerEligibility(
+  private ClassInlinerEligibilityInfo identifyClassInlinerEligibility(
       IRCode code, OptimizationFeedback feedback, Timing timing) {
     timing.begin("Identify class inliner eligibility");
-    identifyClassInlinerEligibility(code, feedback);
+    ClassInlinerEligibilityInfo classInlinerEligibilityInfo =
+        identifyClassInlinerEligibility(code, feedback);
     timing.end();
+    return classInlinerEligibilityInfo;
   }
 
-  private void identifyClassInlinerEligibility(IRCode code, OptimizationFeedback feedback) {
+  private ClassInlinerEligibilityInfo identifyClassInlinerEligibility(
+      IRCode code, OptimizationFeedback feedback) {
     // Method eligibility is calculated in similar way for regular method
     // and for the constructor. To be eligible method should only be using its
     // receiver in the following ways:
@@ -193,14 +202,14 @@
     boolean instanceInitializer = definition.isInstanceInitializer();
     if (definition.isNative()
         || (!definition.isNonAbstractVirtualMethod() && !instanceInitializer)) {
-      return;
+      return null;
     }
 
     feedback.setClassInlinerEligibility(definition, null); // To allow returns below.
 
     Value receiver = code.getThis();
     if (receiver.numberOfPhiUsers() > 0) {
-      return;
+      return null;
     }
 
     List<Pair<Invoke.Type, DexMethod>> callsReceiver = new ArrayList<>();
@@ -229,17 +238,17 @@
               InstancePut instancePutInstruction = insn.asInstancePut();
               // Only allow field writes to the receiver.
               if (!isReceiverAlias.test(instancePutInstruction.object())) {
-                return;
+                return null;
               }
               // Do not allow the receiver to escape via a field write.
               if (isReceiverAlias.test(instancePutInstruction.value())) {
-                return;
+                return null;
               }
               modifiesInstanceFields = true;
             }
             DexField field = insn.asFieldInstruction().getField();
             if (appView.appInfo().resolveField(field).isFailedOrUnknownResolution()) {
-              return;
+              return null;
             }
             break;
           }
@@ -257,7 +266,7 @@
               break;
             }
             // We don't support other direct calls yet.
-            return;
+            return null;
           }
 
         case INVOKE_STATIC:
@@ -265,27 +274,27 @@
             InvokeStatic invoke = insn.asInvokeStatic();
             DexClassAndMethod singleTarget = invoke.lookupSingleTarget(appView, context);
             if (singleTarget == null) {
-              return; // Not allowed.
+              return null; // Not allowed.
             }
             if (singleTarget.getReference() == dexItemFactory.objectsMethods.requireNonNull) {
               if (!invoke.hasOutValue() || !invoke.outValue().hasAnyUsers()) {
                 continue;
               }
             }
-            return;
+            return null;
           }
 
         case INVOKE_VIRTUAL:
           {
             InvokeVirtual invoke = insn.asInvokeVirtual();
             if (ListUtils.lastIndexMatching(invoke.arguments(), isReceiverAlias) != 0) {
-              return; // Not allowed.
+              return null; // Not allowed.
             }
             DexMethod invokedMethod = invoke.getInvokedMethod();
             DexType returnType = invokedMethod.proto.returnType;
             if (returnType.isClassType()
                 && appView.appInfo().inSameHierarchy(returnType, context.getHolderType())) {
-              return; // Not allowed, could introduce an alias of the receiver.
+              return null; // Not allowed, could introduce an alias of the receiver.
             }
             callsReceiver.add(new Pair<>(Invoke.Type.VIRTUAL, invokedMethod));
           }
@@ -293,34 +302,35 @@
 
         default:
           // Other receiver usages make the method not eligible.
-          return;
+          return null;
       }
     }
 
     if (instanceInitializer && !seenSuperInitCall) {
       // Call to super constructor not found?
-      return;
+      return null;
     }
 
     boolean synchronizedVirtualMethod = definition.isSynchronized() && definition.isVirtualMethod();
-
-    feedback.setClassInlinerEligibility(
-        definition,
+    ClassInlinerEligibilityInfo classInlinerEligibilityInfo =
         new ClassInlinerEligibilityInfo(
             callsReceiver,
             new ClassInlinerReceiverAnalysis(appView, definition, code).computeReturnsReceiver(),
             seenMonitor || synchronizedVirtualMethod,
-            modifiesInstanceFields));
+            modifiesInstanceFields);
+    feedback.setClassInlinerEligibility(definition, classInlinerEligibilityInfo);
+    return classInlinerEligibilityInfo;
   }
 
-  private void identifyParameterUsages(
+  private ParameterUsagesInfo identifyParameterUsages(
       DexEncodedMethod method, IRCode code, OptimizationFeedback feedback, Timing timing) {
     timing.begin("Identify parameter usages");
-    identifyParameterUsages(method, code, feedback);
+    ParameterUsagesInfo parameterUsagesInfo = identifyParameterUsages(method, code, feedback);
     timing.end();
+    return parameterUsagesInfo;
   }
 
-  private void identifyParameterUsages(
+  private ParameterUsagesInfo identifyParameterUsages(
       DexEncodedMethod method, IRCode code, OptimizationFeedback feedback) {
     List<ParameterUsage> usages = new ArrayList<>();
     List<Value> values = code.collectArguments();
@@ -331,11 +341,10 @@
         usages.add(usage);
       }
     }
-    feedback.setParameterUsages(
-        method,
-        usages.isEmpty()
-            ? DefaultMethodOptimizationInfo.UNKNOWN_PARAMETER_USAGE_INFO
-            : new ParameterUsagesInfo(usages));
+    ParameterUsagesInfo parameterUsagesInfo =
+        !usages.isEmpty() ? new ParameterUsagesInfo(usages) : null;
+    feedback.setParameterUsages(method, parameterUsagesInfo);
+    return parameterUsagesInfo;
   }
 
   private ParameterUsage collectParameterUsages(int i, Value root) {
@@ -969,6 +978,31 @@
     return true;
   }
 
+  private void computeClassInlinerMethodConstraint(
+      ProgramMethod method,
+      IRCode code,
+      OptimizationFeedback feedback,
+      ClassInlinerEligibilityInfo classInlinerEligibilityInfo,
+      ParameterUsagesInfo parameterUsagesInfo,
+      Timing timing) {
+    timing.begin("Compute class inlining constraint");
+    computeClassInlinerMethodConstraint(
+        method, code, feedback, classInlinerEligibilityInfo, parameterUsagesInfo);
+    timing.end();
+  }
+
+  private void computeClassInlinerMethodConstraint(
+      ProgramMethod method,
+      IRCode code,
+      OptimizationFeedback feedback,
+      ClassInlinerEligibilityInfo classInlinerEligibilityInfo,
+      ParameterUsagesInfo parameterUsagesInfo) {
+    ClassInlinerMethodConstraint classInlinerMethodConstraint =
+        ClassInlinerMethodConstraintAnalysis.analyze(
+            classInlinerEligibilityInfo, parameterUsagesInfo);
+    feedback.setClassInlinerMethodConstraint(method, classInlinerMethodConstraint);
+  }
+
   private void computeSimpleInliningConstraint(
       ProgramMethod method, IRCode code, OptimizationFeedback feedback, Timing timing) {
     if (appView.options().enableSimpleInliningConstraints) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
index 1236ed6..39f725d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
@@ -15,6 +15,7 @@
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.classinliner.ClassInlinerEligibilityInfo;
+import com.android.tools.r8.ir.optimize.classinliner.constraint.ClassInlinerMethodConstraint;
 import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
 import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfoCollection;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -250,11 +251,18 @@
   }
 
   @Override
-  public void setBridgeInfo(DexEncodedMethod method, BridgeInfo bridgeInfo) {
+  public synchronized void setBridgeInfo(DexEncodedMethod method, BridgeInfo bridgeInfo) {
     getMethodOptimizationInfoForUpdating(method).setBridgeInfo(bridgeInfo);
   }
 
   @Override
+  public synchronized void setClassInlinerMethodConstraint(
+      ProgramMethod method, ClassInlinerMethodConstraint classInlinerConstraint) {
+    getMethodOptimizationInfoForUpdating(method)
+        .setClassInlinerMethodConstraint(classInlinerConstraint);
+  }
+
+  @Override
   public synchronized void setClassInlinerEligibility(
       DexEncodedMethod method, ClassInlinerEligibilityInfo eligibility) {
     getMethodOptimizationInfoForUpdating(method).setClassInlinerEligibility(eligibility);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java
index 299f7ce..3b46f4e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java
@@ -15,6 +15,7 @@
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.classinliner.ClassInlinerEligibilityInfo;
+import com.android.tools.r8.ir.optimize.classinliner.constraint.ClassInlinerMethodConstraint;
 import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
 import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfoCollection;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -113,6 +114,10 @@
   public void setBridgeInfo(DexEncodedMethod method, BridgeInfo bridgeInfo) {}
 
   @Override
+  public void setClassInlinerMethodConstraint(
+      ProgramMethod method, ClassInlinerMethodConstraint classInlinerConstraint) {}
+
+  @Override
   public void setClassInlinerEligibility(
       DexEncodedMethod method, ClassInlinerEligibilityInfo eligibility) {}
 
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 945d84a..2624486 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
@@ -15,6 +15,7 @@
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.classinliner.ClassInlinerEligibilityInfo;
+import com.android.tools.r8.ir.optimize.classinliner.constraint.ClassInlinerMethodConstraint;
 import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
 import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfoCollection;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -163,6 +164,12 @@
   }
 
   @Override
+  public void setClassInlinerMethodConstraint(
+      ProgramMethod method, ClassInlinerMethodConstraint classInlinerConstraint) {
+    // Ignored.
+  }
+
+  @Override
   public void setClassInlinerEligibility(
       DexEncodedMethod method, ClassInlinerEligibilityInfo eligibility) {
     // Ignored.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/ParameterUsagesInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/ParameterUsagesInfo.java
index 921ae28..4389193 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/ParameterUsagesInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/ParameterUsagesInfo.java
@@ -39,7 +39,7 @@
         parametersUsages.stream().map(usage -> usage.index).collect(Collectors.toSet()).size();
   }
 
-  ParameterUsage getParameterUsage(int index) {
+  public ParameterUsage getParameterUsage(int index) {
     for (ParameterUsage usage : parametersUsages) {
       if (usage.index == index) {
         return usage;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java
index a179561..43667c8 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java
@@ -16,6 +16,8 @@
 import com.android.tools.r8.ir.analysis.value.UnknownValue;
 import com.android.tools.r8.ir.code.InvokeDirect;
 import com.android.tools.r8.ir.optimize.classinliner.ClassInlinerEligibilityInfo;
+import com.android.tools.r8.ir.optimize.classinliner.constraint.AlwaysFalseClassInlinerMethodConstraint;
+import com.android.tools.r8.ir.optimize.classinliner.constraint.ClassInlinerMethodConstraint;
 import com.android.tools.r8.ir.optimize.info.ParameterUsagesInfo.ParameterUsage;
 import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
 import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
@@ -33,6 +35,8 @@
   private int returnedArgument = DefaultMethodOptimizationInfo.UNKNOWN_RETURNED_ARGUMENT;
   private AbstractValue abstractReturnValue =
       DefaultMethodOptimizationInfo.UNKNOWN_ABSTRACT_RETURN_VALUE;
+  private ClassInlinerMethodConstraint classInlinerConstraint =
+      AlwaysFalseClassInlinerMethodConstraint.getInstance();
   private TypeElement returnsObjectWithUpperBoundType = DefaultMethodOptimizationInfo.UNKNOWN_TYPE;
   private ClassTypeElement returnsObjectWithLowerBoundType =
       DefaultMethodOptimizationInfo.UNKNOWN_CLASS_TYPE;
@@ -239,6 +243,15 @@
   }
 
   @Override
+  public ClassInlinerMethodConstraint getClassInlinerMethodConstraint() {
+    return classInlinerConstraint;
+  }
+
+  void setClassInlinerMethodConstraint(ClassInlinerMethodConstraint classInlinerConstraint) {
+    this.classInlinerConstraint = classInlinerConstraint;
+  }
+
+  @Override
   public TypeElement getDynamicUpperBoundType() {
     return returnsObjectWithUpperBoundType;
   }