Refactor class inliner to use new constraint analysis

Bug: 173337498
Bug: 181746071
Change-Id: I691e06b3bcf371fbe4c7c0b8d86bba51a3087fad
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/DataflowAnalysisResult.java b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/DataflowAnalysisResult.java
index 701df38..a8a1832 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/DataflowAnalysisResult.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/DataflowAnalysisResult.java
@@ -42,6 +42,14 @@
       this.blockExitStates = blockExitStates;
     }
 
+    public StateType join() {
+      StateType result = null;
+      for (StateType blockExitState : blockExitStates.values()) {
+        result = result != null ? result.join(blockExitState) : blockExitState;
+      }
+      return result;
+    }
+
     @Override
     public boolean isSuccessfulAnalysisResult() {
       return true;
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 0cdf967..9dfe2e3 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
@@ -13,9 +13,7 @@
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 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;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -64,15 +62,11 @@
   void setClassInlinerMethodConstraint(
       ProgramMethod method, ClassInlinerMethodConstraint classInlinerConstraint);
 
-  void setClassInlinerEligibility(DexEncodedMethod method, ClassInlinerEligibilityInfo eligibility);
-
   void setInstanceInitializerInfoCollection(
       DexEncodedMethod method, InstanceInitializerInfoCollection instanceInitializerInfoCollection);
 
   void setInitializerEnablingJavaVmAssertions(DexEncodedMethod method);
 
-  void setParameterUsages(DexEncodedMethod method, ParameterUsagesInfo parameterUsagesInfo);
-
   void setNonNullParamOrThrow(DexEncodedMethod method, BitSet facts);
 
   void setNonNullParamOnNormalExits(DexEncodedMethod method, BitSet facts);
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
deleted file mode 100644
index 78e756d..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerEligibilityInfo.java
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright (c) 2019, 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;
-
-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
-   * OptionalBool#UNKNOWN} if the method may return the receiver.
-   */
-  final OptionalBool returnsReceiver;
-
-  public final boolean hasMonitorOnReceiver;
-  public final boolean modifiesInstanceFields;
-
-  public ClassInlinerEligibilityInfo(
-      List<Pair<Invoke.Type, DexMethod>> callsReceiver,
-      OptionalBool returnsReceiver,
-      boolean hasMonitorOnReceiver,
-      boolean modifiesInstanceFields) {
-    this.callsReceiver = callsReceiver;
-    this.returnsReceiver = returnsReceiver;
-    this.hasMonitorOnReceiver = hasMonitorOnReceiver;
-    this.modifiesInstanceFields = modifiesInstanceFields;
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerReceiverAnalysis.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerReceiverAnalysis.java
deleted file mode 100644
index e0f991d..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerReceiverAnalysis.java
+++ /dev/null
@@ -1,135 +0,0 @@
-// Copyright (c) 2019, 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;
-
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
-import com.android.tools.r8.ir.code.BasicBlock;
-import com.android.tools.r8.ir.code.IRCode;
-import com.android.tools.r8.ir.code.Instruction;
-import com.android.tools.r8.ir.code.InvokeMethod;
-import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.utils.BooleanLatticeElement;
-import com.android.tools.r8.utils.OptionalBool;
-import java.util.IdentityHashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * This analysis determines whether a method returns the receiver. The analysis is specific to the
- * class inliner, and the result is therefore not sound in general.
- *
- * <p>The analysis makes the following assumptions.
- *
- * <ul>
- *   <li>None of the given method's arguments is an alias of the receiver (except for the receiver
- *       itself).
- *   <li>The receiver is not stored in the heap. Thus, it is guaranteed that (i) all field-get
- *       instructions do not return an alias of the receiver, and (ii) invoke instructions can only
- *       return an alias of the receiver if the receiver is given as an argument.
- * </ul>
- */
-public class ClassInlinerReceiverAnalysis {
-
-  private final AppView<?> appView;
-  private final DexEncodedMethod method;
-  private final IRCode code;
-  private final Value receiver;
-
-  private final Map<Value, OptionalBool> isReceiverAliasCache = new IdentityHashMap<>();
-
-  public ClassInlinerReceiverAnalysis(AppView<?> appView, DexEncodedMethod method, IRCode code) {
-    this.appView = appView;
-    this.method = method;
-    this.code = code;
-    this.receiver = code.getThis();
-    assert !receiver.hasAliasedValue();
-    assert receiver.getType().isClassType();
-  }
-
-  public OptionalBool computeReturnsReceiver() {
-    if (method.method.proto.returnType.isVoidType()) {
-      return OptionalBool.FALSE;
-    }
-
-    List<BasicBlock> normalExitBlocks = code.computeNormalExitBlocks();
-    if (normalExitBlocks.isEmpty()) {
-      return OptionalBool.FALSE;
-    }
-
-    BooleanLatticeElement result = OptionalBool.BOTTOM;
-    for (BasicBlock block : normalExitBlocks) {
-      Value returnValue = block.exit().asReturn().returnValue();
-      result = result.join(getOrComputeIsReceiverAlias(returnValue));
-
-      // Stop as soon as we reach unknown.
-      if (result.isUnknown()) {
-        return OptionalBool.UNKNOWN;
-      }
-    }
-    assert !result.isBottom();
-    return result.asOptionalBool();
-  }
-
-  private OptionalBool getOrComputeIsReceiverAlias(Value value) {
-    Value root = value.getAliasedValue();
-    return isReceiverAliasCache.computeIfAbsent(root, this::computeIsReceiverAlias);
-  }
-
-  private OptionalBool computeIsReceiverAlias(Value value) {
-    assert !value.hasAliasedValue();
-
-    if (value == receiver) {
-      // Guaranteed to return the receiver.
-      return OptionalBool.TRUE;
-    }
-
-    ClassTypeElement valueType = value.getType().asClassType();
-    if (valueType == null) {
-      return OptionalBool.FALSE;
-    }
-
-    ClassTypeElement receiverType = receiver.getType().asClassType();
-    if (!valueType.isRelatedTo(receiverType, appView)) {
-      // Guaranteed not to return the receiver.
-      return OptionalBool.FALSE;
-    }
-
-    if (value.isPhi()) {
-      // Not sure what is returned.
-      return OptionalBool.UNKNOWN;
-    }
-
-    Instruction definition = value.definition;
-    if (definition.isArrayGet() || definition.isFieldGet()) {
-      // Guaranteed not to return the receiver, since the class inliner does not allow the
-      // receiver to flow into any arrays or fields.
-      return OptionalBool.FALSE;
-    }
-
-    if (definition.isConstInstruction() || definition.isCreatingInstanceOrArray()) {
-      // Guaranteed not to return the receiver.
-      return OptionalBool.FALSE;
-    }
-
-    if (definition.isInvokeMethod()) {
-      // Since the class inliner does not allow the receiver to flow into the heap, the only way for
-      // the invoked method to return the receiver is if one of the given arguments is an alias of
-      // the receiver.
-      InvokeMethod invoke = definition.asInvokeMethod();
-      for (Value argument : invoke.arguments()) {
-        if (getOrComputeIsReceiverAlias(argument).isPossiblyTrue()) {
-          return OptionalBool.UNKNOWN;
-        }
-      }
-
-      return OptionalBool.FALSE;
-    }
-
-    // Not sure what is returned.
-    return OptionalBool.UNKNOWN;
-  }
-}
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 38b0b5f..4b15332 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
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.ir.optimize.classinliner;
 
-import static com.android.tools.r8.graph.DexEncodedMethod.asProgramMethodOrNull;
 import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
 
 import com.android.tools.r8.errors.InternalCompilerError;
@@ -37,8 +36,6 @@
 import com.android.tools.r8.ir.code.InstancePut;
 import com.android.tools.r8.ir.code.Instruction;
 import com.android.tools.r8.ir.code.InstructionOrPhi;
-import com.android.tools.r8.ir.code.Invoke;
-import com.android.tools.r8.ir.code.Invoke.Type;
 import com.android.tools.r8.ir.code.InvokeDirect;
 import com.android.tools.r8.ir.code.InvokeMethod;
 import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
@@ -51,10 +48,11 @@
 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.analysis.NonEmptyParameterUsage;
+import com.android.tools.r8.ir.optimize.classinliner.analysis.ParameterUsage;
 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;
 import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
 import com.android.tools.r8.ir.optimize.inliner.InliningIRProvider;
 import com.android.tools.r8.ir.optimize.inliner.NopWhyAreYouNotInliningReporter;
@@ -69,7 +67,6 @@
 import com.android.tools.r8.utils.collections.ProgramMethodSet;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
 import java.util.ArrayList;
 import java.util.Comparator;
@@ -83,8 +80,6 @@
 
 final class InlineCandidateProcessor {
 
-  private static final ImmutableSet<If.Type> ALLOWED_ZERO_TEST_TYPES =
-      ImmutableSet.of(If.Type.EQ, If.Type.NE);
   private static final AliasedValueConfiguration aliasesThroughAssumeAndCheckCasts =
       AssumeAndCheckCastAliasedValueConfiguration.getInstance();
 
@@ -357,8 +352,6 @@
 
           // Eligible usage as an invocation argument.
           if (isExtraMethodCall(invokeMethod)) {
-            assert !invokeMethod.isInvokeSuper();
-            assert !invokeMethod.isInvokePolymorphic();
             if (isExtraMethodCallEligible(
                 invokeMethod, resolutionResult, singleProgramTarget, defaultOracle)) {
               continue;
@@ -999,42 +992,60 @@
       }
     }
 
-    if (!isEligibleVirtualMethodCall(
-        invoke.getInvokedMethod(),
-        singleTarget,
-        invoke.getType())) {
+    if (!isEligibleVirtualMethodCall(invoke.getInvokedMethod(), singleTarget)) {
       return null;
     }
 
-    ClassInlinerEligibilityInfo eligibility =
-        singleTarget.getDefinition().getOptimizationInfo().getClassInlinerEligibility();
-    if (!isEligibleInvokeWithAllUsersAsReceivers(
-        invoke, eligibility.returnsReceiver, indirectUsers)) {
+    ParameterUsage usage =
+        singleTarget
+            .getDefinition()
+            .getOptimizationInfo()
+            .getClassInlinerMethodConstraint()
+            .getParameterUsage(0);
+    assert !usage.isTop();
+
+    // If the method returns receiver and the return value is actually used in the code we need to
+    // make some additional checks.
+    OptionalBool isReceiverReturned;
+    if (usage.isParameterReturned()) {
+      if (singleTarget.getDefinition().getOptimizationInfo().returnsArgument()) {
+        assert singleTarget.getDefinition().getOptimizationInfo().getReturnedArgument() == 0;
+        isReceiverReturned = OptionalBool.TRUE;
+      } else {
+        isReceiverReturned = OptionalBool.UNKNOWN;
+      }
+    } else {
+      isReceiverReturned = OptionalBool.FALSE;
+    }
+
+    if (!isEligibleInvokeWithAllUsersAsReceivers(invoke, isReceiverReturned, indirectUsers)) {
       return null;
     }
-    if (eligibility.callsReceiver.size() > 1) {
-      return null;
-    }
-    if (!eligibility.callsReceiver.isEmpty()) {
-      assert eligibility.callsReceiver.get(0).getFirst() == Invoke.Type.VIRTUAL;
-      Pair<Type, DexMethod> invokeInfo = eligibility.callsReceiver.get(0);
-      Type invokeType = invokeInfo.getFirst();
-      DexMethod indirectlyInvokedMethod = invokeInfo.getSecond();
-      SingleResolutionResult indirectResolutionResult =
-          appView
-              .appInfo()
-              .resolveMethodOn(eligibleClass, indirectlyInvokedMethod)
-              .asSingleResolution();
-      if (indirectResolutionResult == null) {
+
+    if (!usage.isBottom()) {
+      NonEmptyParameterUsage nonEmptyUsage = usage.asNonEmpty();
+      if (nonEmptyUsage.getMethodCallsWithParameterAsReceiver().size() > 1) {
         return null;
       }
-      ProgramMethod indirectSingleTarget =
-          indirectResolutionResult.getResolutionPair().asProgramMethod();
-      if (!isEligibleIndirectVirtualMethodCall(
-          indirectlyInvokedMethod, invokeType, indirectSingleTarget)) {
-        return null;
+      if (!nonEmptyUsage.getMethodCallsWithParameterAsReceiver().isEmpty()) {
+        DexMethod indirectlyInvokedMethod =
+            nonEmptyUsage.getMethodCallsWithParameterAsReceiver().iterator().next();
+        SingleResolutionResult indirectResolutionResult =
+            appView
+                .appInfo()
+                .resolveMethodOn(eligibleClass, indirectlyInvokedMethod)
+                .asSingleResolution();
+        if (indirectResolutionResult == null) {
+          return null;
+        }
+        ProgramMethod indirectSingleTarget =
+            indirectResolutionResult.getResolutionPair().asProgramMethod();
+        if (!isEligibleIndirectVirtualMethodCall(indirectlyInvokedMethod, indirectSingleTarget)) {
+          return null;
+        }
+        markSizeOfIndirectTargetForInlining(indirectSingleTarget);
+        indirectMethodCallsOnInstance.add(indirectSingleTarget);
       }
-      indirectMethodCallsOnInstance.add(indirectSingleTarget);
     }
 
     invoke.getNonReceiverArguments().forEach(receivers::addIllegalReceiverAlias);
@@ -1042,45 +1053,41 @@
     return new InliningInfo(singleTarget, eligibleClass.type);
   }
 
-  private boolean isEligibleIndirectVirtualMethodCall(DexMethod invokedMethod, Type type) {
-    ProgramMethod singleTarget =
-        asProgramMethodOrNull(
-            appView.appInfo().resolveMethodOn(eligibleClass, invokedMethod).getSingleTarget(),
-            appView);
-    return isEligibleIndirectVirtualMethodCall(invokedMethod, type, singleTarget);
-  }
-
   private boolean isEligibleIndirectVirtualMethodCall(
-      DexMethod invokedMethod, Type type, ProgramMethod singleTarget) {
+      DexMethod invokedMethod, ProgramMethod singleTarget) {
     if (!isEligibleSingleTarget(singleTarget)) {
       return false;
     }
     if (singleTarget.getDefinition().isLibraryMethodOverride().isTrue()) {
       return false;
     }
-    if (!isEligibleVirtualMethodCall(invokedMethod, singleTarget, type)) {
+    if (!isEligibleVirtualMethodCall(invokedMethod, singleTarget)) {
       return false;
     }
-    ClassInlinerEligibilityInfo eligibility =
-        singleTarget.getDefinition().getOptimizationInfo().getClassInlinerEligibility();
-    if (!eligibility.callsReceiver.isEmpty() || eligibility.returnsReceiver.isPossiblyTrue()) {
-      return false;
+
+    ParameterUsage usage =
+        singleTarget
+            .getDefinition()
+            .getOptimizationInfo()
+            .getClassInlinerMethodConstraint()
+            .getParameterUsage(0);
+    assert !usage.isTop();
+    if (usage.isBottom()) {
+      return true;
     }
-    markSizeOfIndirectTargetForInlining(singleTarget);
-    return true;
+    NonEmptyParameterUsage nonEmptyUsage = usage.asNonEmpty();
+    return nonEmptyUsage.getMethodCallsWithParameterAsReceiver().isEmpty()
+        && !nonEmptyUsage.isParameterReturned();
   }
 
-  private boolean isEligibleVirtualMethodCall(
-      DexMethod callee,
-      ProgramMethod singleTarget,
-      Type type) {
+  private boolean isEligibleVirtualMethodCall(DexMethod callee, ProgramMethod singleTarget) {
     assert isEligibleSingleTarget(singleTarget);
 
     // We should not inline a method if the invocation has type interface or virtual and the
     // signature of the invocation resolves to a private or static method.
     // TODO(b/147212189): Why not inline private methods? If access is permitted it is valid.
     ResolutionResult resolutionResult =
-        appView.appInfo().resolveMethod(callee, type == Type.INTERFACE);
+        appView.appInfo().resolveMethodOnClass(callee, eligibleClass);
     if (resolutionResult.isSingleResolution()
         && !resolutionResult.getSingleTarget().isNonPrivateVirtualMethod()) {
       return false;
@@ -1096,17 +1103,15 @@
     MethodOptimizationInfo optimizationInfo = singleTarget.getDefinition().getOptimizationInfo();
     ClassInlinerMethodConstraint classInlinerMethodConstraint =
         optimizationInfo.getClassInlinerMethodConstraint();
+    int parameter = 0;
     if (root.isNewInstance()) {
-      if (!classInlinerMethodConstraint.isEligibleForNewInstanceClassInlining(singleTarget)) {
-        return false;
-      }
-    } else {
-      assert root.isStaticGet();
-      if (!classInlinerMethodConstraint.isEligibleForStaticGetClassInlining(singleTarget)) {
-        return false;
-      }
+      return classInlinerMethodConstraint.isEligibleForNewInstanceClassInlining(
+          singleTarget, parameter);
     }
-    return true;
+
+    assert root.isStaticGet();
+    return classInlinerMethodConstraint.isEligibleForStaticGetClassInlining(
+        singleTarget, parameter);
   }
 
   private boolean isExtraMethodCall(InvokeMethod invoke) {
@@ -1151,8 +1156,6 @@
       SingleResolutionResult resolutionResult,
       ProgramMethod singleTarget,
       Supplier<InliningOracle> defaultOracle) {
-    List<Value> arguments = Lists.newArrayList(invoke.inValues());
-
     // If we got here with invocation on receiver the user is ineligible.
     if (invoke.isInvokeMethodWithReceiver()) {
       InvokeMethodWithReceiver invokeMethodWithReceiver = invoke.asInvokeMethodWithReceiver();
@@ -1179,22 +1182,10 @@
     }
 
     // Go through all arguments, see if all usages of eligibleInstance are good.
-    if (!isEligibleParameterUsages(invoke, arguments, singleTarget.getDefinition())) {
+    if (!isEligibleParameterUsages(invoke, singleTarget.getDefinition())) {
       return false;
     }
 
-    MethodOptimizationInfo optimizationInfo = singleTarget.getDefinition().getOptimizationInfo();
-    for (int argIndex = 0; argIndex < arguments.size(); argIndex++) {
-      Value argument = arguments.get(argIndex).getAliasedValue();
-      ParameterUsage parameterUsage = optimizationInfo.getParameterUsages(argIndex);
-      if (receivers.isDefiniteReceiverAlias(argument)
-          && parameterUsage != null
-          && parameterUsage.notUsed()) {
-        // Reference can be removed since it's not used.
-        unusedArguments.add(new Pair<>(invoke, argIndex));
-      }
-    }
-
     extraMethodCalls.put(invoke, new InliningInfo(singleTarget, null));
 
     // Looks good.
@@ -1214,102 +1205,72 @@
     return false;
   }
 
-  private boolean isEligibleParameterUsages(
-      InvokeMethod invoke, List<Value> arguments, DexEncodedMethod singleTarget) {
+  private boolean isEligibleParameterUsages(InvokeMethod invoke, DexEncodedMethod singleTarget) {
     // Go through all arguments, see if all usages of eligibleInstance are good.
-    for (int argIndex = 0; argIndex < arguments.size(); argIndex++) {
+    for (int parameter = 0; parameter < invoke.arguments().size(); parameter++) {
       MethodOptimizationInfo optimizationInfo = singleTarget.getOptimizationInfo();
-      ParameterUsage parameterUsage = optimizationInfo.getParameterUsages(argIndex);
+      ParameterUsage usage =
+          optimizationInfo.getClassInlinerMethodConstraint().getParameterUsage(parameter);
 
-      Value argument = arguments.get(argIndex);
+      Value argument = invoke.getArgument(parameter);
       if (receivers.isReceiverAlias(argument)) {
         // Have parameter usage info?
-        if (!isEligibleParameterUsage(parameterUsage, invoke)) {
+        if (!isEligibleParameterUsage(usage, invoke)) {
           return false;
         }
       } else {
         // Nothing to worry about, unless `argument` becomes an alias of the receiver later.
         receivers.addDeferredAliasValidityCheck(
-            argument, () -> isEligibleParameterUsage(parameterUsage, invoke));
+            argument, () -> isEligibleParameterUsage(usage, invoke));
       }
     }
     return true;
   }
 
-  private boolean isEligibleParameterUsage(ParameterUsage parameterUsage, InvokeMethod invoke) {
-    if (parameterUsage == null) {
-      return false; // Don't know anything.
-    }
-
-    if (parameterUsage.notUsed()) {
+  private boolean isEligibleParameterUsage(ParameterUsage usage, InvokeMethod invoke) {
+    if (usage.isBottom()) {
       return true;
     }
 
-    if (parameterUsage.isAssignedToField) {
+    if (usage.isTop()) {
       return false;
     }
 
+    NonEmptyParameterUsage nonEmptyUsage = usage.asNonEmpty();
+
     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, and we also can't optimize away instance-field assignments, as
       // they have global side effects.
-      if (parameterUsage.hasFieldAssignment || parameterUsage.hasFieldRead) {
+      if (nonEmptyUsage.isParameterMutated()
+          || usage.isParameterUsedAsLock()
+          || nonEmptyUsage.hasFieldsReadFromParameter()) {
         return false;
       }
     }
 
-    if (parameterUsage.isReturned) {
-      if (invoke.outValue() != null && invoke.outValue().hasAnyUsers()) {
-        // Used as return value which is not ignored.
-        return false;
-      }
-    }
-
-    if (parameterUsage.isUsedInMonitor) {
-      return !root.isStaticGet();
-    }
-
-    if (!Sets.difference(parameterUsage.ifZeroTest, ALLOWED_ZERO_TEST_TYPES).isEmpty()) {
-      // Used in unsupported zero-check-if kinds.
+    if (usage.isParameterReturned() && invoke.hasUsedOutValue()) {
+      // Used as return value which is not ignored.
       return false;
     }
 
-    for (Pair<Type, DexMethod> call : parameterUsage.callsReceiver) {
-      Type type = call.getFirst();
-      DexMethod target = call.getSecond();
-
-      if (type == Type.VIRTUAL || type == Type.INTERFACE) {
-        // Is the method called indirectly still eligible?
-        if (!isEligibleIndirectVirtualMethodCall(target, type)) {
-          return false;
-        }
-      } else if (type == Type.DIRECT) {
-        if (!isInstanceInitializerEligibleForClassInlining(target)) {
-          // Only calls to trivial instance initializers are supported at this point.
-          return false;
-        }
-      } else {
-        // Static and super calls are not yet supported.
+    for (DexMethod invokedMethod : nonEmptyUsage.getMethodCallsWithParameterAsReceiver()) {
+      SingleResolutionResult resolutionResult =
+          appView.appInfo().resolveMethodOn(eligibleClass, invokedMethod).asSingleResolution();
+      if (resolutionResult == null || !resolutionResult.getResolvedHolder().isProgramClass()) {
         return false;
       }
+
+      // Is the method called indirectly still eligible?
+      ProgramMethod singleTarget = resolutionResult.getResolutionPair().asProgramMethod();
+      if (!isEligibleIndirectVirtualMethodCall(invokedMethod, singleTarget)) {
+        return false;
+      }
+      markSizeOfIndirectTargetForInlining(singleTarget);
     }
     return true;
   }
 
-  private boolean isInstanceInitializerEligibleForClassInlining(DexMethod method) {
-    if (method == dexItemFactory.objectMembers.constructor) {
-      return true;
-    }
-    DexProgramClass holder = asProgramClassOrNull(appView.definitionForHolder(method));
-    DexEncodedMethod definition = method.lookupOnClass(holder);
-    if (definition == null) {
-      return false;
-    }
-    InstanceInitializerInfo initializerInfo =
-        definition.getOptimizationInfo().getContextInsensitiveInstanceInitializerInfo();
-    return initializerInfo.receiverNeverEscapesOutsideConstructorChain();
-  }
-
   private boolean exemptFromInstructionLimit(ProgramMethod inlinee) {
     KotlinClassLevelInfo kotlinInfo = inlinee.getHolder().getKotlinInfo();
     return kotlinInfo.isSyntheticClass() && kotlinInfo.asSyntheticClass().isLambda();
@@ -1361,5 +1322,10 @@
     instruction.getBlock().removeInstruction(instruction);
   }
 
-  static class IllegalClassInlinerStateException extends Exception {}
+  static class IllegalClassInlinerStateException extends Exception {
+
+    IllegalClassInlinerStateException() {
+      assert false;
+    }
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/AnalysisState.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/AnalysisState.java
index 90b5ec8..a09ada2 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/AnalysisState.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/AnalysisState.java
@@ -84,10 +84,21 @@
     return BOTTOM;
   }
 
-  static AnalysisState create(Int2ObjectMap<ParameterUsagePerContext> backing) {
+  public static AnalysisState create(Int2ObjectMap<ParameterUsagePerContext> backing) {
     return backing.isEmpty() ? bottom() : new AnalysisState(backing);
   }
 
+  /**
+   * Converts instances of {@link InternalNonEmptyParameterUsage} to {@link NonEmptyParameterUsage}.
+   *
+   * <p>This is needed because {@link InternalNonEmptyParameterUsage} is not suitable for being
+   * stored in {@link com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo}, since it
+   * contains references to IR instructions.
+   */
+  AnalysisState externalize() {
+    return rebuildParameters((parameter, usagePerContext) -> usagePerContext.externalize());
+  }
+
   AnalysisState put(int parameter, ParameterUsagePerContext parameterUsagePerContext) {
     Int2ObjectOpenHashMap<ParameterUsagePerContext> newBacking =
         new Int2ObjectOpenHashMap<>(backing);
@@ -95,7 +106,7 @@
     return create(newBacking);
   }
 
-  void forEach(IntObjConsumer<ParameterUsagePerContext> consumer) {
+  public void forEach(IntObjConsumer<ParameterUsagePerContext> consumer) {
     Int2ObjectMapUtils.forEach(backing, consumer);
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/BottomParameterUsage.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/BottomParameterUsage.java
index 777c95e..930edc5 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/BottomParameterUsage.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/BottomParameterUsage.java
@@ -30,6 +30,11 @@
   }
 
   @Override
+  ParameterUsage externalize() {
+    return this;
+  }
+
+  @Override
   public boolean isBottom() {
     return true;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/BottomParameterUsagePerContext.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/BottomParameterUsagePerContext.java
index a4c6704..b535038 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/BottomParameterUsagePerContext.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/BottomParameterUsagePerContext.java
@@ -18,6 +18,11 @@
   }
 
   @Override
+  ParameterUsagePerContext externalize() {
+    return this;
+  }
+
+  @Override
   public ParameterUsage get(AnalysisContext context) {
     return ParameterUsage.bottom();
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/ClassInlinerMethodConstraintAnalysis.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/ClassInlinerMethodConstraintAnalysis.java
new file mode 100644
index 0000000..fee1048
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/ClassInlinerMethodConstraintAnalysis.java
@@ -0,0 +1,36 @@
+// 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.analysis;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.analysis.framework.intraprocedural.DataflowAnalysisResult.SuccessfulDataflowAnalysisResult;
+import com.android.tools.r8.ir.analysis.framework.intraprocedural.IntraproceduralDataflowAnalysis;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.optimize.classinliner.constraint.ClassInlinerMethodConstraint;
+import com.android.tools.r8.ir.optimize.classinliner.constraint.ConditionalClassInlinerMethodConstraint;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+
+public class ClassInlinerMethodConstraintAnalysis {
+
+  public static ClassInlinerMethodConstraint analyze(
+      AppView<AppInfoWithLiveness> appView, ProgramMethod method, IRCode code) {
+    if (method.getDefinition().isClassInitializer()) {
+      return ClassInlinerMethodConstraint.alwaysFalse();
+    }
+
+    // Analyze code.
+    IntraproceduralDataflowAnalysis<AnalysisState> analysis =
+        new IntraproceduralDataflowAnalysis<>(
+            AnalysisState.bottom(), new TransferFunction(appView, method));
+    SuccessfulDataflowAnalysisResult<AnalysisState> result =
+        analysis.run(code.entryBlock()).asSuccessfulAnalysisResult();
+    if (result == null) {
+      return ClassInlinerMethodConstraint.alwaysFalse();
+    }
+
+    return new ConditionalClassInlinerMethodConstraint(result.join().externalize());
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/InternalNonEmptyParameterUsage.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/InternalNonEmptyParameterUsage.java
index fb21140..4bba181 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/InternalNonEmptyParameterUsage.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/InternalNonEmptyParameterUsage.java
@@ -5,8 +5,10 @@
 package com.android.tools.r8.ir.optimize.classinliner.analysis;
 
 import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
 import com.android.tools.r8.utils.BooleanUtils;
+import com.google.common.collect.ImmutableMultiset;
 import com.google.common.collect.ImmutableSet;
 import java.util.Collection;
 import java.util.Objects;
@@ -88,6 +90,20 @@
   }
 
   @Override
+  ParameterUsage externalize() {
+    ImmutableMultiset.Builder<DexMethod> methodCallsWithParameterAsReceiverBuilder =
+        ImmutableMultiset.builder();
+    methodCallsWithParameterAsReceiver.forEach(
+        invoke -> methodCallsWithParameterAsReceiverBuilder.add(invoke.getInvokedMethod()));
+    return new NonEmptyParameterUsage(
+        fieldsReadFromParameter,
+        methodCallsWithParameterAsReceiverBuilder.build(),
+        isParameterMutated,
+        isParameterReturned,
+        isParameterUsedAsLock);
+  }
+
+  @Override
   public boolean isParameterMutated() {
     return isParameterMutated;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/NonEmptyParameterUsage.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/NonEmptyParameterUsage.java
index ea57edc..d377305 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/NonEmptyParameterUsage.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/NonEmptyParameterUsage.java
@@ -55,6 +55,11 @@
     return this;
   }
 
+  @Override
+  ParameterUsage externalize() {
+    throw new Unreachable();
+  }
+
   public boolean hasFieldsReadFromParameter() {
     return !getFieldsReadFromParameter().isEmpty();
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/NonEmptyParameterUsagePerContext.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/NonEmptyParameterUsagePerContext.java
index 1ea2d2d..e57fc5c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/NonEmptyParameterUsagePerContext.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/NonEmptyParameterUsagePerContext.java
@@ -54,6 +54,11 @@
   }
 
   @Override
+  ParameterUsagePerContext externalize() {
+    return rebuild((context, usage) -> usage.externalize());
+  }
+
+  @Override
   public ParameterUsage get(AnalysisContext context) {
     assert backing.containsKey(context);
     return backing.get(context);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/ParameterUsage.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/ParameterUsage.java
index 423f039..4c5c2c2 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/ParameterUsage.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/ParameterUsage.java
@@ -21,6 +21,8 @@
     return null;
   }
 
+  abstract ParameterUsage externalize();
+
   /**
    * Returns true if this is an instanceof {@link BottomParameterUsage}.
    *
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/ParameterUsagePerContext.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/ParameterUsagePerContext.java
index b81d65b..f294ea2 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/ParameterUsagePerContext.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/ParameterUsagePerContext.java
@@ -12,6 +12,8 @@
     return null;
   }
 
+  abstract ParameterUsagePerContext externalize();
+
   /** Returns the usage information for this parameter in the given context. */
   public abstract ParameterUsage get(AnalysisContext context);
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/UnknownParameterUsage.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/UnknownParameterUsage.java
index 30f0c1e..93577ad 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/UnknownParameterUsage.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/UnknownParameterUsage.java
@@ -28,6 +28,11 @@
   }
 
   @Override
+  ParameterUsage externalize() {
+    return this;
+  }
+
+  @Override
   public boolean isParameterMutated() {
     return true;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/UnknownParameterUsagePerContext.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/UnknownParameterUsagePerContext.java
index 7d5e6f1..4ead51d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/UnknownParameterUsagePerContext.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/UnknownParameterUsagePerContext.java
@@ -18,6 +18,11 @@
   }
 
   @Override
+  ParameterUsagePerContext externalize() {
+    return this;
+  }
+
+  @Override
   public ParameterUsage get(AnalysisContext context) {
     return ParameterUsage.top();
   }
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
index 0ac793a..0c6d335e 100644
--- 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
@@ -5,6 +5,7 @@
 package com.android.tools.r8.ir.optimize.classinliner.constraint;
 
 import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.optimize.classinliner.analysis.ParameterUsage;
 
 public class AlwaysFalseClassInlinerMethodConstraint implements ClassInlinerMethodConstraint {
 
@@ -18,12 +19,22 @@
   }
 
   @Override
-  public boolean isEligibleForNewInstanceClassInlining(ProgramMethod method) {
+  public ClassInlinerMethodConstraint fixupAfterRemovingThisParameter() {
+    return this;
+  }
+
+  @Override
+  public ParameterUsage getParameterUsage(int parameter) {
+    return ParameterUsage.top();
+  }
+
+  @Override
+  public boolean isEligibleForNewInstanceClassInlining(ProgramMethod method, int parameter) {
     return false;
   }
 
   @Override
-  public boolean isEligibleForStaticGetClassInlining(ProgramMethod method) {
+  public boolean isEligibleForStaticGetClassInlining(ProgramMethod method, int parameter) {
     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
deleted file mode 100644
index 56e1226..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/AlwaysTrueClassInlinerMethodConstraint.java
+++ /dev/null
@@ -1,29 +0,0 @@
-// 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() {}
-
-  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
index 640c4c7..c86f373 100644
--- 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
@@ -5,22 +5,19 @@
 package com.android.tools.r8.ir.optimize.classinliner.constraint;
 
 import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.optimize.classinliner.analysis.ParameterUsage;
 
 public interface ClassInlinerMethodConstraint {
 
-  boolean isEligibleForNewInstanceClassInlining(ProgramMethod method);
+  ClassInlinerMethodConstraint fixupAfterRemovingThisParameter();
 
-  boolean isEligibleForStaticGetClassInlining(ProgramMethod method);
+  ParameterUsage getParameterUsage(int parameter);
+
+  boolean isEligibleForNewInstanceClassInlining(ProgramMethod method, int parameter);
+
+  boolean isEligibleForStaticGetClassInlining(ProgramMethod method, int parameter);
 
   static AlwaysFalseClassInlinerMethodConstraint alwaysFalse() {
     return AlwaysFalseClassInlinerMethodConstraint.getInstance();
   }
-
-  static AlwaysTrueClassInlinerMethodConstraint alwaysTrue() {
-    return AlwaysTrueClassInlinerMethodConstraint.getInstance();
-  }
-
-  static OnlyNewInstanceClassInlinerMethodConstraint onlyNewInstanceClassInlining() {
-    return OnlyNewInstanceClassInlinerMethodConstraint.getInstance();
-  }
 }
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
deleted file mode 100644
index d36a76c..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/ClassInlinerMethodConstraintAnalysis.java
+++ /dev/null
@@ -1,61 +0,0 @@
-// 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 ClassInlinerMethodConstraint.alwaysTrue();
-      }
-      return ClassInlinerMethodConstraint.onlyNewInstanceClassInlining();
-    }
-    assert !isEligibleForStaticGetClassInlining;
-    return ClassInlinerMethodConstraint.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;
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/ConditionalClassInlinerMethodConstraint.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/ConditionalClassInlinerMethodConstraint.java
new file mode 100644
index 0000000..5c384e8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/ConditionalClassInlinerMethodConstraint.java
@@ -0,0 +1,81 @@
+// 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;
+import com.android.tools.r8.ir.optimize.classinliner.analysis.AnalysisContext;
+import com.android.tools.r8.ir.optimize.classinliner.analysis.AnalysisState;
+import com.android.tools.r8.ir.optimize.classinliner.analysis.NonEmptyParameterUsage;
+import com.android.tools.r8.ir.optimize.classinliner.analysis.ParameterUsage;
+import com.android.tools.r8.ir.optimize.classinliner.analysis.ParameterUsagePerContext;
+import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
+import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
+
+public class ConditionalClassInlinerMethodConstraint implements ClassInlinerMethodConstraint {
+
+  private final AnalysisState usages;
+
+  public ConditionalClassInlinerMethodConstraint(AnalysisState usages) {
+    this.usages = usages;
+  }
+
+  @Override
+  public ClassInlinerMethodConstraint fixupAfterRemovingThisParameter() {
+    // TODO(b/181746071): Introduce a 'TOP' variant to reduce memory usage and add check here.
+    if (usages.isBottom()) {
+      return this;
+    }
+    Int2ObjectMap<ParameterUsagePerContext> backing = new Int2ObjectOpenHashMap<>();
+    usages.forEach(
+        (parameter, usagePerContext) -> {
+          if (parameter > 0) {
+            backing.put(parameter - 1, usagePerContext);
+          }
+        });
+    return new ConditionalClassInlinerMethodConstraint(AnalysisState.create(backing));
+  }
+
+  @Override
+  public ParameterUsage getParameterUsage(int parameter) {
+    AnalysisContext defaultContext = AnalysisContext.getDefaultContext();
+    return usages.get(parameter).get(defaultContext);
+  }
+
+  @Override
+  public boolean isEligibleForNewInstanceClassInlining(ProgramMethod method, int parameter) {
+    AnalysisContext defaultContext = AnalysisContext.getDefaultContext();
+    ParameterUsage usage = usages.get(parameter).get(defaultContext);
+    return !usage.isTop();
+  }
+
+  @Override
+  public boolean isEligibleForStaticGetClassInlining(ProgramMethod method, int parameter) {
+    AnalysisContext defaultContext = AnalysisContext.getDefaultContext();
+    ParameterUsage usage = usages.get(parameter).get(defaultContext);
+    if (usage.isBottom()) {
+      return true;
+    }
+    if (usage.isTop()) {
+      return false;
+    }
+
+    NonEmptyParameterUsage knownUsage = usage.asNonEmpty();
+    if (knownUsage.isParameterMutated()) {
+      // 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;
+    }
+    if (knownUsage.isParameterUsedAsLock()) {
+      // We will not be able to remove the monitor instruction afterwards.
+      return false;
+    }
+    if (!knownUsage.getFieldsReadFromParameter().isEmpty()) {
+      // We don't know the value of the field.
+      return false;
+    }
+    return true;
+  }
+}
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
deleted file mode 100644
index 9bb1702..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/OnlyNewInstanceClassInlinerMethodConstraint.java
+++ /dev/null
@@ -1,29 +0,0 @@
-// 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() {}
-
-  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/ConcreteCallSiteOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java
index ded60a3..78c9f46 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java
@@ -15,7 +15,6 @@
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
 import com.android.tools.r8.ir.analysis.value.UnknownValue;
 import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.ir.optimize.info.ParameterUsagesInfo.ParameterUsage;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceArrayMap;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
@@ -94,11 +93,6 @@
   public boolean hasUsefulOptimizationInfo(AppView<?> appView, DexEncodedMethod method) {
     TypeElement[] staticTypes = getStaticTypes(appView, method);
     for (int i = 0; i < size; i++) {
-      ParameterUsage parameterUsage = method.getOptimizationInfo().getParameterUsages(i);
-      // If the parameter is not used, passing accurate argument info doesn't matter.
-      if (parameterUsage != null && parameterUsage.notUsed()) {
-        continue;
-      }
       AbstractValue abstractValue = getAbstractArgumentValue(i);
       if (abstractValue.isNonTrivial()) {
         assert appView.options().callSiteOptimizationOptions().isConstantPropagationEnabled();
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 46b5a70..d49189f 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
@@ -12,9 +12,7 @@
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
 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.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;
 import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
@@ -35,9 +33,7 @@
   static ClassTypeElement UNKNOWN_CLASS_TYPE = null;
   static boolean UNKNOWN_CHECKS_NULL_RECEIVER_BEFORE_ANY_SIDE_EFFECT = false;
   static boolean UNKNOWN_TRIGGERS_CLASS_INIT_BEFORE_ANY_SIDE_EFFECT = false;
-  static ClassInlinerEligibilityInfo UNKNOWN_CLASS_INLINER_ELIGIBILITY = null;
   static boolean UNKNOWN_INITIALIZER_ENABLING_JAVA_ASSERTIONS = false;
-  static ParameterUsagesInfo UNKNOWN_PARAMETER_USAGE_INFO = null;
   static boolean UNKNOWN_MAY_HAVE_SIDE_EFFECTS = true;
   static boolean UNKNOWN_RETURN_VALUE_ONLY_DEPENDS_ON_ARGUMENTS = false;
   static BitSet NO_NULL_PARAMETER_OR_THROW_FACTS = null;
@@ -105,12 +101,6 @@
   }
 
   @Override
-  public ParameterUsage getParameterUsages(int parameter) {
-    assert UNKNOWN_PARAMETER_USAGE_INFO == null;
-    return null;
-  }
-
-  @Override
   public BitSet getNonNullParamOrThrow() {
     return NO_NULL_PARAMETER_OR_THROW_FACTS;
   }
@@ -152,11 +142,6 @@
   }
 
   @Override
-  public ClassInlinerEligibilityInfo getClassInlinerEligibility() {
-    return UNKNOWN_CLASS_INLINER_ELIGIBILITY;
-  }
-
-  @Override
   public AbstractValue getAbstractReturnValue() {
     return UNKNOWN_ABSTRACT_RETURN_VALUE;
   }
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 7cced9d..bdc35f3 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
@@ -10,9 +10,7 @@
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 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;
 import java.util.BitSet;
@@ -47,8 +45,6 @@
 
   public abstract ClassTypeElement getDynamicLowerBoundType();
 
-  public abstract ParameterUsage getParameterUsages(int parameter);
-
   public final boolean hasNonNullParamOrThrow() {
     return getNonNullParamOrThrow() != null;
   }
@@ -73,8 +69,6 @@
 
   public abstract BridgeInfo getBridgeInfo();
 
-  public abstract ClassInlinerEligibilityInfo getClassInlinerEligibility();
-
   public abstract Set<DexType> getInitializedClassesOnNormalExit();
 
   public abstract InstanceInitializerInfo getContextInsensitiveInstanceInitializerInfo();
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 804b6b3..aa28923 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
@@ -29,7 +29,6 @@
 import static com.android.tools.r8.ir.code.Opcodes.INVOKE_NEW_ARRAY;
 import static com.android.tools.r8.ir.code.Opcodes.INVOKE_STATIC;
 import static com.android.tools.r8.ir.code.Opcodes.INVOKE_VIRTUAL;
-import static com.android.tools.r8.ir.code.Opcodes.MONITOR;
 import static com.android.tools.r8.ir.code.Opcodes.MUL;
 import static com.android.tools.r8.ir.code.Opcodes.NEW_ARRAY_EMPTY;
 import static com.android.tools.r8.ir.code.Opcodes.NEW_INSTANCE;
@@ -47,10 +46,8 @@
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexClassAndMethod;
 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;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
@@ -77,23 +74,17 @@
 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.InvokeStatic;
-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;
 import com.android.tools.r8.ir.conversion.IRConverter;
 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.analysis.ClassInlinerMethodConstraintAnalysis;
 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;
 import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoCollection;
 import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
@@ -103,19 +94,14 @@
 import com.android.tools.r8.kotlin.Kotlin;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.ListUtils;
-import com.android.tools.r8.utils.Pair;
 import com.android.tools.r8.utils.Timing;
-import com.android.tools.r8.utils.WorkList;
 import com.google.common.collect.Sets;
 import java.util.ArrayDeque;
-import java.util.ArrayList;
 import java.util.BitSet;
 import java.util.Deque;
 import java.util.List;
 import java.util.Set;
 import java.util.function.BiFunction;
-import java.util.function.Predicate;
 
 public class MethodOptimizationInfoCollector {
 
@@ -145,16 +131,11 @@
       Timing timing) {
     DexEncodedMethod definition = method.getDefinition();
     identifyBridgeInfo(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);
+    computeClassInlinerMethodConstraint(method, code, feedback, timing);
     computeSimpleInliningConstraint(method, code, feedback, timing);
     computeDynamicReturnType(dynamicTypeOptimization, feedback, definition, code, timing);
     computeInitializedClassesOnNormalExit(feedback, definition, code, timing);
@@ -173,201 +154,6 @@
     timing.end();
   }
 
-  private ClassInlinerEligibilityInfo identifyClassInlinerEligibility(
-      IRCode code, OptimizationFeedback feedback, Timing timing) {
-    timing.begin("Identify class inliner eligibility");
-    ClassInlinerEligibilityInfo classInlinerEligibilityInfo =
-        identifyClassInlinerEligibility(code, feedback);
-    timing.end();
-    return classInlinerEligibilityInfo;
-  }
-
-  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:
-    //
-    //  (1) as a receiver of reads/writes of instance fields of the holder class,
-    //  (2) as a return value,
-    //  (3) as a receiver of a call to the superclass initializer. Note that we don't
-    //      check what is passed to superclass initializer as arguments, only check
-    //      that it is not the instance being initialized,
-    //  (4) as argument to a monitor instruction.
-    //
-    // Note that (4) can safely be removed as the receiver is guaranteed not to escape when we class
-    // inline it, and hence any monitor instructions are no-ops.
-    ProgramMethod context = code.context();
-    DexEncodedMethod definition = context.getDefinition();
-    boolean instanceInitializer = definition.isInstanceInitializer();
-    if (definition.isNative()
-        || (!definition.isNonAbstractVirtualMethod() && !instanceInitializer)) {
-      return null;
-    }
-
-    feedback.setClassInlinerEligibility(definition, null); // To allow returns below.
-
-    Value receiver = code.getThis();
-    if (receiver.numberOfPhiUsers() > 0) {
-      return null;
-    }
-
-    List<Pair<Invoke.Type, DexMethod>> callsReceiver = new ArrayList<>();
-    boolean seenSuperInitCall = false;
-    boolean seenMonitor = false;
-    boolean modifiesInstanceFields = false;
-
-    AliasedValueConfiguration configuration =
-        AssumeAndCheckCastAliasedValueConfiguration.getInstance();
-    Predicate<Value> isReceiverAlias = value -> value.getAliasedValue(configuration) == receiver;
-    for (Instruction insn : receiver.aliasedUsers(configuration)) {
-      switch (insn.opcode()) {
-        case ASSUME:
-        case CHECK_CAST:
-        case RETURN:
-          break;
-
-        case MONITOR:
-          seenMonitor = true;
-          break;
-
-        case INSTANCE_GET:
-        case INSTANCE_PUT:
-          {
-            if (insn.isInstancePut()) {
-              InstancePut instancePutInstruction = insn.asInstancePut();
-              // Only allow field writes to the receiver.
-              if (!isReceiverAlias.test(instancePutInstruction.object())) {
-                return null;
-              }
-              // Do not allow the receiver to escape via a field write.
-              if (isReceiverAlias.test(instancePutInstruction.value())) {
-                return null;
-              }
-              modifiesInstanceFields = true;
-            }
-            DexField field = insn.asFieldInstruction().getField();
-            if (appView.appInfo().resolveField(field).isFailedOrUnknownResolution()) {
-              return null;
-            }
-            break;
-          }
-
-        case INVOKE_DIRECT:
-          {
-            InvokeDirect invoke = insn.asInvokeDirect();
-            DexMethod invokedMethod = invoke.getInvokedMethod();
-            if (dexItemFactory.isConstructor(invokedMethod)
-                && invokedMethod.holder == context.getHolder().superType
-                && ListUtils.lastIndexMatching(invoke.arguments(), isReceiverAlias) == 0
-                && !seenSuperInitCall
-                && instanceInitializer) {
-              seenSuperInitCall = true;
-              break;
-            }
-            // We don't support other direct calls yet.
-            return null;
-          }
-
-        case INVOKE_STATIC:
-          {
-            InvokeStatic invoke = insn.asInvokeStatic();
-            DexClassAndMethod singleTarget = invoke.lookupSingleTarget(appView, context);
-            if (singleTarget == null) {
-              return null; // Not allowed.
-            }
-            if (singleTarget.getReference() == dexItemFactory.objectsMethods.requireNonNull) {
-              if (!invoke.hasOutValue() || !invoke.outValue().hasAnyUsers()) {
-                continue;
-              }
-            }
-            return null;
-          }
-
-        case INVOKE_VIRTUAL:
-          {
-            InvokeVirtual invoke = insn.asInvokeVirtual();
-            if (ListUtils.lastIndexMatching(invoke.arguments(), isReceiverAlias) != 0) {
-              return null; // Not allowed.
-            }
-            DexMethod invokedMethod = invoke.getInvokedMethod();
-            DexType returnType = invokedMethod.proto.returnType;
-            if (returnType.isClassType()
-                && appView.appInfo().inSameHierarchy(returnType, context.getHolderType())) {
-              return null; // Not allowed, could introduce an alias of the receiver.
-            }
-            callsReceiver.add(new Pair<>(Invoke.Type.VIRTUAL, invokedMethod));
-          }
-          break;
-
-        default:
-          // Other receiver usages make the method not eligible.
-          return null;
-      }
-    }
-
-    if (instanceInitializer && !seenSuperInitCall) {
-      // Call to super constructor not found?
-      return null;
-    }
-
-    boolean synchronizedVirtualMethod = definition.isSynchronized() && definition.isVirtualMethod();
-    ClassInlinerEligibilityInfo classInlinerEligibilityInfo =
-        new ClassInlinerEligibilityInfo(
-            callsReceiver,
-            new ClassInlinerReceiverAnalysis(appView, definition, code).computeReturnsReceiver(),
-            seenMonitor || synchronizedVirtualMethod,
-            modifiesInstanceFields);
-    feedback.setClassInlinerEligibility(definition, classInlinerEligibilityInfo);
-    return classInlinerEligibilityInfo;
-  }
-
-  private ParameterUsagesInfo identifyParameterUsages(
-      DexEncodedMethod method, IRCode code, OptimizationFeedback feedback, Timing timing) {
-    timing.begin("Identify parameter usages");
-    ParameterUsagesInfo parameterUsagesInfo = identifyParameterUsages(method, code, feedback);
-    timing.end();
-    return parameterUsagesInfo;
-  }
-
-  private ParameterUsagesInfo identifyParameterUsages(
-      DexEncodedMethod method, IRCode code, OptimizationFeedback feedback) {
-    List<ParameterUsage> usages = new ArrayList<>();
-    List<Value> values = code.collectArguments();
-    for (int i = 0; i < values.size(); i++) {
-      Value value = values.get(i);
-      ParameterUsage usage = collectParameterUsages(i, value);
-      if (usage != null) {
-        usages.add(usage);
-      }
-    }
-    ParameterUsagesInfo parameterUsagesInfo =
-        !usages.isEmpty() ? new ParameterUsagesInfo(usages) : null;
-    feedback.setParameterUsages(method, parameterUsagesInfo);
-    return parameterUsagesInfo;
-  }
-
-  private ParameterUsage collectParameterUsages(int i, Value root) {
-    ParameterUsageBuilder builder = new ParameterUsageBuilder(root, i, dexItemFactory);
-    WorkList<Value> worklist = WorkList.newIdentityWorkList();
-    worklist.addIfNotSeen(root);
-    while (worklist.hasNext()) {
-      Value value = worklist.next();
-      if (value.hasPhiUsers()) {
-        return null;
-      }
-      for (Instruction user : value.uniqueUsers()) {
-        if (!builder.note(user)) {
-          return null;
-        }
-        if (user.isAssume()) {
-          worklist.addIfNotSeen(user.outValue());
-        }
-      }
-    }
-    return builder.build();
-  }
-
   private void analyzeReturns(IRCode code, OptimizationFeedback feedback, Timing timing) {
     timing.begin("Identify returns argument");
     analyzeReturns(code, feedback);
@@ -982,24 +768,16 @@
       ProgramMethod method,
       IRCode code,
       OptimizationFeedback feedback,
-      ClassInlinerEligibilityInfo classInlinerEligibilityInfo,
-      ParameterUsagesInfo parameterUsagesInfo,
       Timing timing) {
     timing.begin("Compute class inlining constraint");
-    computeClassInlinerMethodConstraint(
-        method, code, feedback, classInlinerEligibilityInfo, parameterUsagesInfo);
+    computeClassInlinerMethodConstraint(method, code, feedback);
     timing.end();
   }
 
   private void computeClassInlinerMethodConstraint(
-      ProgramMethod method,
-      IRCode code,
-      OptimizationFeedback feedback,
-      ClassInlinerEligibilityInfo classInlinerEligibilityInfo,
-      ParameterUsagesInfo parameterUsagesInfo) {
+      ProgramMethod method, IRCode code, OptimizationFeedback feedback) {
     ClassInlinerMethodConstraint classInlinerMethodConstraint =
-        ClassInlinerMethodConstraintAnalysis.analyze(
-            classInlinerEligibilityInfo, parameterUsagesInfo);
+        ClassInlinerMethodConstraintAnalysis.analyze(appView, method, code);
     feedback.setClassInlinerMethodConstraint(method, classInlinerMethodConstraint);
   }
 
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 39f725d..df06045 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
@@ -14,7 +14,6 @@
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 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;
@@ -263,12 +262,6 @@
   }
 
   @Override
-  public synchronized void setClassInlinerEligibility(
-      DexEncodedMethod method, ClassInlinerEligibilityInfo eligibility) {
-    getMethodOptimizationInfoForUpdating(method).setClassInlinerEligibility(eligibility);
-  }
-
-  @Override
   public synchronized void setInstanceInitializerInfoCollection(
       DexEncodedMethod method,
       InstanceInitializerInfoCollection instanceInitializerInfoCollection) {
@@ -282,12 +275,6 @@
   }
 
   @Override
-  public synchronized void setParameterUsages(
-      DexEncodedMethod method, ParameterUsagesInfo parameterUsagesInfo) {
-    getMethodOptimizationInfoForUpdating(method).setParameterUsages(parameterUsagesInfo);
-  }
-
-  @Override
   public synchronized void setNonNullParamOrThrow(DexEncodedMethod method, BitSet facts) {
     getMethodOptimizationInfoForUpdating(method).setNonNullParamOrThrow(facts);
   }
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 3b46f4e..e862824 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
@@ -14,7 +14,6 @@
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 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;
@@ -118,10 +117,6 @@
       ProgramMethod method, ClassInlinerMethodConstraint classInlinerConstraint) {}
 
   @Override
-  public void setClassInlinerEligibility(
-      DexEncodedMethod method, ClassInlinerEligibilityInfo eligibility) {}
-
-  @Override
   public void setInstanceInitializerInfoCollection(
       DexEncodedMethod method,
       InstanceInitializerInfoCollection instanceInitializerInfoCollection) {}
@@ -130,10 +125,6 @@
   public void setInitializerEnablingJavaVmAssertions(DexEncodedMethod method) {}
 
   @Override
-  public void setParameterUsages(DexEncodedMethod method, ParameterUsagesInfo parameterUsagesInfo) {
-  }
-
-  @Override
   public void setNonNullParamOrThrow(DexEncodedMethod method, BitSet facts) {}
 
   @Override
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 2624486..3fb77d9 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
@@ -14,7 +14,6 @@
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 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;
@@ -170,12 +169,6 @@
   }
 
   @Override
-  public void setClassInlinerEligibility(
-      DexEncodedMethod method, ClassInlinerEligibilityInfo eligibility) {
-    // Ignored.
-  }
-
-  @Override
   public void setInstanceInitializerInfoCollection(
       DexEncodedMethod method,
       InstanceInitializerInfoCollection instanceInitializerInfoCollection) {
@@ -190,11 +183,6 @@
   }
 
   @Override
-  public void setParameterUsages(DexEncodedMethod method, ParameterUsagesInfo parameterUsagesInfo) {
-    // Ignored.
-  }
-
-  @Override
   public void setNonNullParamOrThrow(DexEncodedMethod method, BitSet facts) {
     method.getMutableOptimizationInfo().setNonNullParamOrThrow(facts);
   }
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
deleted file mode 100644
index 4389193..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/ParameterUsagesInfo.java
+++ /dev/null
@@ -1,273 +0,0 @@
-// Copyright (c) 2019, 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.info;
-
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.ir.code.If;
-import com.android.tools.r8.ir.code.If.Type;
-import com.android.tools.r8.ir.code.InstanceGet;
-import com.android.tools.r8.ir.code.InstancePut;
-import com.android.tools.r8.ir.code.Instruction;
-import com.android.tools.r8.ir.code.Invoke;
-import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
-import com.android.tools.r8.ir.code.InvokeStatic;
-import com.android.tools.r8.ir.code.Monitor;
-import com.android.tools.r8.ir.code.Return;
-import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.utils.ListUtils;
-import com.android.tools.r8.utils.Pair;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-public final class ParameterUsagesInfo {
-
-  private ImmutableList<ParameterUsage> parametersUsages;
-
-  public ParameterUsagesInfo(List<ParameterUsage> usages) {
-    assert !usages.isEmpty();
-    parametersUsages = ImmutableList.copyOf(usages);
-    assert parametersUsages.size() ==
-        parametersUsages.stream().map(usage -> usage.index).collect(Collectors.toSet()).size();
-  }
-
-  public ParameterUsage getParameterUsage(int index) {
-    for (ParameterUsage usage : parametersUsages) {
-      if (usage.index == index) {
-        return usage;
-      }
-    }
-    return null;
-  }
-
-  ParameterUsagesInfo remove(int index) {
-    assert parametersUsages.size() > 0;
-    assert 0 <= index && index <= ListUtils.last(parametersUsages).index;
-    ImmutableList.Builder<ParameterUsage> builder = ImmutableList.builder();
-    for (ParameterUsage usage : parametersUsages) {
-      // Once we remove or pass the designated index, copy-n-shift the remaining usages.
-      if (index < usage.index) {
-        builder.add(ParameterUsage.copyAndShift(usage, 1));
-      } else if (index == usage.index) {
-        // Do not add the parameter usage with the matched index.
-      } else {
-        // Until we meet the `parameter` of interest, keep copying.
-        assert usage.index < index;
-        builder.add(usage);
-      }
-    }
-    ImmutableList<ParameterUsage> adjustedUsages = builder.build();
-    if (adjustedUsages.isEmpty()) {
-      return DefaultMethodOptimizationInfo.UNKNOWN_PARAMETER_USAGE_INFO;
-    }
-    return new ParameterUsagesInfo(adjustedUsages);
-  }
-
-  public final static class ParameterUsage {
-
-    public final int index;
-    public final Set<Type> ifZeroTest;
-    public final List<Pair<Invoke.Type, DexMethod>> callsReceiver;
-
-    // If a field of this argument is assigned: arg.f = x.
-    public final boolean hasFieldAssignment;
-
-    // If a field of this argument is read: x = arg.f.
-    public final boolean hasFieldRead;
-
-    // If this argument is assigned to a field: x.f = arg.
-    public final boolean isAssignedToField;
-
-    // If this argument is returned: return arg.
-    public final boolean isReturned;
-
-    // If this argument is used in a monitor instruction.
-    public final boolean isUsedInMonitor;
-
-    ParameterUsage(
-        int index,
-        Set<Type> ifZeroTest,
-        List<Pair<Invoke.Type, DexMethod>> callsReceiver,
-        boolean hasFieldAssignment,
-        boolean hasFieldRead,
-        boolean isAssignedToField,
-        boolean isReturned,
-        boolean isUsedInMonitor) {
-      this.index = index;
-      this.ifZeroTest =
-          ifZeroTest.isEmpty() ? Collections.emptySet() : ImmutableSet.copyOf(ifZeroTest);
-      this.callsReceiver =
-          callsReceiver.isEmpty() ? Collections.emptyList() : ImmutableList.copyOf(callsReceiver);
-      this.hasFieldAssignment = hasFieldAssignment;
-      this.hasFieldRead = hasFieldRead;
-      this.isAssignedToField = isAssignedToField;
-      this.isReturned = isReturned;
-      this.isUsedInMonitor = isUsedInMonitor;
-    }
-
-    static ParameterUsage copyAndShift(ParameterUsage original, int shift) {
-      assert original.index >= shift;
-      return new ParameterUsage(
-          original.index - shift,
-          original.ifZeroTest,
-          original.callsReceiver,
-          original.hasFieldAssignment,
-          original.hasFieldRead,
-          original.isAssignedToField,
-          original.isReturned,
-          original.isUsedInMonitor);
-    }
-
-    public boolean notUsed() {
-      return (ifZeroTest == null || ifZeroTest.isEmpty())
-          && (callsReceiver == null || callsReceiver.isEmpty())
-          && !hasFieldAssignment
-          && !hasFieldRead
-          && !isAssignedToField
-          && !isReturned
-          && !isUsedInMonitor;
-    }
-  }
-
-  public static class ParameterUsageBuilder {
-
-    private final int index;
-    private final Value arg;
-    private final DexItemFactory dexItemFactory;
-
-    private final Set<Type> ifZeroTestTypes = new HashSet<>();
-    private final List<Pair<Invoke.Type, DexMethod>> callsOnReceiver = new ArrayList<>();
-
-    private boolean hasFieldAssignment = false;
-    private boolean hasFieldRead = false;
-    private boolean isAssignedToField = false;
-    private boolean isReturned = false;
-    private boolean isUsedInMonitor = false;
-
-    ParameterUsageBuilder(Value arg, int index, DexItemFactory dexItemFactory) {
-      this.arg = arg;
-      this.index = index;
-      this.dexItemFactory = dexItemFactory;
-    }
-
-    // Returns false if the instruction is not supported.
-    public boolean note(Instruction instruction) {
-      if (instruction.isAssume()) {
-        // Keep examining other users if there are no phi users, but the param usage builder should
-        // consider aliased users.
-        return !instruction.outValue().hasPhiUsers();
-      }
-      if (instruction.isIf()) {
-        return note(instruction.asIf());
-      }
-      if (instruction.isInstanceGet()) {
-        return note(instruction.asInstanceGet());
-      }
-      if (instruction.isInstancePut()) {
-        return note(instruction.asInstancePut());
-      }
-      if (instruction.isInvokeMethodWithReceiver()) {
-        return note(instruction.asInvokeMethodWithReceiver());
-      }
-      if (instruction.isInvokeStatic()) {
-        return note(instruction.asInvokeStatic());
-      }
-      if (instruction.isReturn()) {
-        return note(instruction.asReturn());
-      }
-      if (instruction.isMonitor()) {
-        return note(instruction.asMonitor());
-      }
-      return false;
-    }
-
-    public ParameterUsage build() {
-      return new ParameterUsage(
-          index,
-          ifZeroTestTypes,
-          callsOnReceiver,
-          hasFieldAssignment,
-          hasFieldRead,
-          isAssignedToField,
-          isReturned,
-          isUsedInMonitor);
-    }
-
-    private boolean note(If ifInstruction) {
-      if (ifInstruction.asIf().isZeroTest()) {
-        assert ifInstruction.inValues().size() == 1
-            && ifInstruction.inValues().get(0).getAliasedValue() == arg;
-        ifZeroTestTypes.add(ifInstruction.asIf().getType());
-        return true;
-      }
-      return false;
-    }
-
-    private boolean note(InstanceGet instanceGetInstruction) {
-      assert arg != instanceGetInstruction.outValue();
-      if (instanceGetInstruction.object().getAliasedValue() == arg) {
-        hasFieldRead = true;
-        return true;
-      }
-      return false;
-    }
-
-    private boolean note(InstancePut instancePutInstruction) {
-      assert arg != instancePutInstruction.outValue();
-      if (instancePutInstruction.object().getAliasedValue() == arg) {
-        hasFieldAssignment = true;
-        isAssignedToField |= instancePutInstruction.value().getAliasedValue() == arg;
-        return true;
-      }
-      if (instancePutInstruction.value().getAliasedValue() == arg) {
-        isAssignedToField = true;
-        return true;
-      }
-      return false;
-    }
-
-    private boolean note(InvokeMethodWithReceiver invokeInstruction) {
-      if (ListUtils.lastIndexMatching(
-          invokeInstruction.inValues(), v -> v.getAliasedValue() == arg) == 0) {
-        callsOnReceiver.add(
-            new Pair<>(
-                invokeInstruction.asInvokeMethodWithReceiver().getType(),
-                invokeInstruction.asInvokeMethodWithReceiver().getInvokedMethod()));
-        return true;
-      }
-      return false;
-    }
-
-    private boolean note(InvokeStatic invokeInstruction) {
-      if (invokeInstruction.getInvokedMethod() == dexItemFactory.objectsMethods.requireNonNull) {
-        if (!invokeInstruction.hasOutValue() || !invokeInstruction.outValue().hasAnyUsers()) {
-          ifZeroTestTypes.add(Type.EQ);
-          return true;
-        }
-      }
-      return false;
-    }
-
-    private boolean note(Return returnInstruction) {
-      assert returnInstruction.inValues().size() == 1
-          && returnInstruction.inValues().get(0).getAliasedValue() == arg;
-      isReturned = true;
-      return true;
-    }
-
-    private boolean note(Monitor monitorInstruction) {
-      assert monitorInstruction.inValues().size() == 1;
-      assert monitorInstruction.inValues().get(0).getAliasedValue() == arg;
-      isUsedInMonitor = true;
-      return true;
-    }
-  }
-}
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 bdcaae4..d6bdd6b 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
@@ -15,9 +15,7 @@
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
 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.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;
 import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfoCollection;
@@ -43,12 +41,8 @@
   // Stores information about instance methods and constructors for
   // class inliner, null value indicates that the method is not eligible.
   private BridgeInfo bridgeInfo = null;
-  private ClassInlinerEligibilityInfo classInlinerEligibility =
-      DefaultMethodOptimizationInfo.UNKNOWN_CLASS_INLINER_ELIGIBILITY;
   private InstanceInitializerInfoCollection instanceInitializerInfoCollection =
       InstanceInitializerInfoCollection.empty();
-  private ParameterUsagesInfo parametersUsages =
-      DefaultMethodOptimizationInfo.UNKNOWN_PARAMETER_USAGE_INFO;
   // Stores information about nullability hint per parameter. If set, that means, the method
   // somehow (e.g., null check, such as arg != null, or using checkParameterIsNotNull) ensures
   // the corresponding parameter is not null, or throws NPE before any other side effects.
@@ -147,9 +141,7 @@
     inlining = template.inlining;
     simpleInliningConstraint = template.simpleInliningConstraint;
     bridgeInfo = template.bridgeInfo;
-    classInlinerEligibility = template.classInlinerEligibility;
     instanceInitializerInfoCollection = template.instanceInitializerInfoCollection;
-    parametersUsages = template.parametersUsages;
     nonNullParamOrThrow = template.nonNullParamOrThrow;
     nonNullParamOnNormalExits = template.nonNullParamOnNormalExits;
   }
@@ -276,11 +268,6 @@
   }
 
   @Override
-  public ParameterUsage getParameterUsages(int parameter) {
-    return parametersUsages == null ? null : parametersUsages.getParameterUsage(parameter);
-  }
-
-  @Override
   public BitSet getNonNullParamOrThrow() {
     return nonNullParamOrThrow;
   }
@@ -330,11 +317,6 @@
   }
 
   @Override
-  public ClassInlinerEligibilityInfo getClassInlinerEligibility() {
-    return classInlinerEligibility;
-  }
-
-  @Override
   public AbstractValue getAbstractReturnValue() {
     return abstractReturnValue;
   }
@@ -379,10 +361,6 @@
     return isFlagSet(RETURN_VALUE_ONLY_DEPENDS_ON_ARGUMENTS_FLAG);
   }
 
-  void setParameterUsages(ParameterUsagesInfo parametersUsages) {
-    this.parametersUsages = parametersUsages;
-  }
-
   void setNonNullParamOrThrow(BitSet facts) {
     this.nonNullParamOrThrow = facts;
   }
@@ -399,10 +377,6 @@
     this.simpleInliningConstraint = constraint;
   }
 
-  void setClassInlinerEligibility(ClassInlinerEligibilityInfo eligibility) {
-    this.classInlinerEligibility = eligibility;
-  }
-
   void setInstanceInitializerInfoCollection(
       InstanceInitializerInfoCollection instanceInitializerInfoCollection) {
     this.instanceInitializerInfoCollection = instanceInitializerInfoCollection;
@@ -517,6 +491,7 @@
   }
 
   public void adjustOptimizationInfoAfterRemovingThisParameter() {
+    classInlinerConstraint = classInlinerConstraint.fixupAfterRemovingThisParameter();
     // cannotBeKept: doesn't depend on `this`
     // classInitializerMayBePostponed: `this` could trigger <clinit> of the previous holder.
     clearFlag(CLASS_INITIALIZER_MAY_BE_POSTPONED_FLAG);
@@ -549,18 +524,12 @@
     // triggersClassInitBeforeAnySideEffect: code is not changed.
     markTriggerClassInitBeforeAnySideEffect(
         DefaultMethodOptimizationInfo.UNKNOWN_TRIGGERS_CLASS_INIT_BEFORE_ANY_SIDE_EFFECT);
-    // classInlinerEligibility: chances are the method is not an instance method anymore.
-    classInlinerEligibility = DefaultMethodOptimizationInfo.UNKNOWN_CLASS_INLINER_ELIGIBILITY;
     // initializerInfo: the computed initializer info may become invalid.
     instanceInitializerInfoCollection = InstanceInitializerInfoCollection.empty();
     // initializerEnablingJavaAssertions: `this` could trigger <clinit> of the previous holder.
     setFlag(
         INITIALIZER_ENABLING_JAVA_ASSERTIONS_FLAG,
         DefaultMethodOptimizationInfo.UNKNOWN_INITIALIZER_ENABLING_JAVA_ASSERTIONS);
-    parametersUsages =
-        parametersUsages == DefaultMethodOptimizationInfo.UNKNOWN_PARAMETER_USAGE_INFO
-            ? DefaultMethodOptimizationInfo.UNKNOWN_PARAMETER_USAGE_INFO
-            : parametersUsages.remove(0);
     nonNullParamOrThrow =
         nonNullParamOrThrow == DefaultMethodOptimizationInfo.NO_NULL_PARAMETER_OR_THROW_FACTS
             ? DefaultMethodOptimizationInfo.NO_NULL_PARAMETER_OR_THROW_FACTS
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
index e169df0..3ba2c7b 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
@@ -12,6 +12,7 @@
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.OffOrAuto;
+import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
@@ -204,14 +205,14 @@
   }
 
   private void checkLambdaCount(CodeInspector inspector, int maxExpectedCount, String prefix) {
-    int count = 0;
+    List<String> found = new ArrayList<>();
     for (FoundClassSubject clazz : inspector.allClasses()) {
       if (clazz.isSynthesizedJavaLambdaClass() &&
           clazz.getOriginalName().startsWith(prefix)) {
-        count++;
+        found.add(clazz.getOriginalName());
       }
     }
-    assertEquals(maxExpectedCount, count);
+    assertEquals(StringUtils.lines(found), maxExpectedCount, found.size());
   }
 
   private void checkTestMultipleInterfacesCheckCastCount(
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/SyntheticDistributionTest.java b/src/test/java/com/android/tools/r8/dexsplitter/SyntheticDistributionTest.java
index b5ecddc..61b0fd7 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/SyntheticDistributionTest.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/SyntheticDistributionTest.java
@@ -15,10 +15,8 @@
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.ThrowableConsumer;
-import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
 import com.google.common.collect.ImmutableSet;
@@ -50,12 +48,6 @@
   @Test
   public void testDistribution() throws Exception {
     assumeTrue(parameters.isDexRuntime());
-    ThrowableConsumer<R8FullTestBuilder> configurator =
-        r8FullTestBuilder ->
-            r8FullTestBuilder
-                .noMinification()
-                .enableInliningAnnotations()
-                .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.O));
     ThrowableConsumer<R8TestCompileResult> ensureLambdaNotInBase =
         r8TestCompileResult ->
             r8TestCompileResult.inspect(
@@ -72,7 +64,7 @@
             ImmutableSet.of(FeatureClass.class),
             FeatureClass.class,
             ensureLambdaNotInBase,
-            configurator);
+            this::configure);
     assertEquals(0, processResult.exitCode);
     assertEquals(StringUtils.lines("42foobar"), processResult.stdout);
   }
@@ -86,13 +78,7 @@
             .addFeatureSplit(FeatureClass.class)
             .addFeatureSplit(Feature2Class.class)
             .addKeepFeatureMainRules(BaseSuperClass.class, FeatureClass.class, Feature2Class.class)
-            .addKeepMethodRules(
-                Reference.methodFromMethod(
-                    BaseSuperClass.class.getDeclaredMethod(
-                        "keptApplyLambda", MyFunction.class, String.class)))
-            .noMinification()
-            .enableInliningAnnotations()
-            .setMinApi(parameters.getApiLevel())
+            .apply(this::configure)
             .compile();
 
     compileResult
@@ -104,6 +90,17 @@
         .assertSuccessWithOutputLines("43barfoo");
   }
 
+  private void configure(R8FullTestBuilder testBuilder) throws NoSuchMethodException {
+    testBuilder
+        .addKeepMethodRules(
+            Reference.methodFromMethod(
+                BaseSuperClass.class.getDeclaredMethod(
+                    "keptApplyLambda", MyFunction.class, String.class)))
+        .enableInliningAnnotations()
+        .noMinification()
+        .setMinApi(parameters.getApiLevel());
+  }
+
   public abstract static class BaseSuperClass implements RunInterface {
     @Override
     public void run() {
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV9TreeShakeJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV9TreeShakeJarVerificationTest.java
index 3a09469..a9e28c9 100644
--- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV9TreeShakeJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV9TreeShakeJarVerificationTest.java
@@ -5,7 +5,6 @@
 
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.StringConsumer.FileConsumer;
-import com.android.tools.r8.utils.AndroidApp;
 import java.io.File;
 import java.nio.file.Path;
 import org.junit.Test;
@@ -15,12 +14,11 @@
   @Test
   public void buildAndTreeShakeFromDeployJar() throws Exception {
     Path proguardMapPath = File.createTempFile("mapping", ".txt", temp.getRoot()).toPath();
-    AndroidApp app =
-        buildAndTreeShakeFromDeployJar(
-            CompilationMode.RELEASE,
-            GMSCORE_V9_DIR,
-            true,
-            GMSCORE_V9_MAX_SIZE,
-            options -> options.proguardMapConsumer = new FileConsumer(proguardMapPath));
+    buildAndTreeShakeFromDeployJar(
+        CompilationMode.RELEASE,
+        GMSCORE_V9_DIR,
+        true,
+        GMSCORE_V9_MAX_SIZE,
+        options -> options.proguardMapConsumer = new FileConsumer(proguardMapPath));
   }
 }
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
index 16413e0..e6258e9 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
@@ -20,7 +20,6 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.naming.MemberNaming.MethodSignature;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.InstructionSubject;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
 import com.google.common.collect.Lists;
@@ -63,15 +62,6 @@
         && clazz.getInterfaces().size() == 1;
   }
 
-  private static Predicate<DexType> createLambdaCheck(CodeInspector inspector) {
-    Set<DexType> lambdaClasses =
-        inspector.allClasses().stream()
-            .filter(clazz -> isLambda(clazz.getDexProgramClass()))
-            .map(clazz -> clazz.getDexProgramClass().type)
-            .collect(Collectors.toSet());
-    return lambdaClasses::contains;
-  }
-
   @Test
   public void testJStyleLambdas() throws Exception {
     String mainClassName = "class_inliner_lambda_j_style.MainKt";