Introduce a 'top' element for the class inliner analysis

Bug: 181746071
Change-Id: Ia62a189777d6bd0c8a03c8f1cb29f1575f2473d0
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/AnalysisContext.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/AnalysisContext.java
index 8c05bf0..5f63f4c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/AnalysisContext.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/AnalysisContext.java
@@ -5,7 +5,7 @@
 package com.android.tools.r8.ir.optimize.classinliner.analysis;
 
 /**
- * The context used for the {@link AnalysisState} lattice.
+ * The context used for the {@link ParameterUsages} lattice.
  *
  * <p>By only using {@link DefaultAnalysisContext} a context-insensitive result is achieved.
  * Context-sensitive results can be achieved by forking different analysis contexts during the class
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/BottomParameterUsages.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/BottomParameterUsages.java
new file mode 100644
index 0000000..84bae0e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/BottomParameterUsages.java
@@ -0,0 +1,50 @@
+// 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 it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
+
+public class BottomParameterUsages extends ParameterUsages {
+
+  private static final BottomParameterUsages INSTANCE = new BottomParameterUsages();
+
+  private BottomParameterUsages() {}
+
+  static BottomParameterUsages getInstance() {
+    return INSTANCE;
+  }
+
+  @Override
+  ParameterUsages externalize() {
+    return this;
+  }
+
+  @Override
+  public ParameterUsagePerContext get(int parameter) {
+    return ParameterUsagePerContext.bottom();
+  }
+
+  @Override
+  public boolean isBottom() {
+    return true;
+  }
+
+  @Override
+  ParameterUsages put(int parameter, ParameterUsagePerContext usagePerContext) {
+    Int2ObjectOpenHashMap<ParameterUsagePerContext> backing = new Int2ObjectOpenHashMap<>();
+    backing.put(parameter, usagePerContext);
+    return NonEmptyParameterUsages.create(backing);
+  }
+
+  @Override
+  public boolean equals(Object other) {
+    return other == INSTANCE;
+  }
+
+  @Override
+  public int hashCode() {
+    return System.identityHashCode(this);
+  }
+}
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
index fee1048..677518c 100644
--- 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
@@ -17,20 +17,30 @@
 
   public static ClassInlinerMethodConstraint analyze(
       AppView<AppInfoWithLiveness> appView, ProgramMethod method, IRCode code) {
-    if (method.getDefinition().isClassInitializer()) {
+    if (method.getDefinition().isClassInitializer()
+        || method.getDefinition().getNumberOfArguments() == 0) {
       return ClassInlinerMethodConstraint.alwaysFalse();
     }
 
     // Analyze code.
-    IntraproceduralDataflowAnalysis<AnalysisState> analysis =
+    IntraproceduralDataflowAnalysis<ParameterUsages> analysis =
         new IntraproceduralDataflowAnalysis<>(
-            AnalysisState.bottom(), new TransferFunction(appView, method));
-    SuccessfulDataflowAnalysisResult<AnalysisState> result =
+            ParameterUsages.bottom(), new TransferFunction(appView, method));
+    SuccessfulDataflowAnalysisResult<ParameterUsages> result =
         analysis.run(code.entryBlock()).asSuccessfulAnalysisResult();
     if (result == null) {
       return ClassInlinerMethodConstraint.alwaysFalse();
     }
 
-    return new ConditionalClassInlinerMethodConstraint(result.join().externalize());
+    // TODO(b/181746071): This always returns a non-empty result, that should be transformed into
+    //  bottom or top.
+    ParameterUsages usages = result.join();
+    if (usages.isBottom()) {
+      return ClassInlinerMethodConstraint.alwaysTrue();
+    }
+    if (usages.isTop()) {
+      return ClassInlinerMethodConstraint.alwaysFalse();
+    }
+    return new ConditionalClassInlinerMethodConstraint(usages.externalize());
   }
 }
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/NonEmptyParameterUsages.java
similarity index 80%
rename from src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/AnalysisState.java
rename to src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/NonEmptyParameterUsages.java
index a09ada2..2fb4a19 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/NonEmptyParameterUsages.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.ir.optimize.classinliner.analysis;
 
-import com.android.tools.r8.ir.analysis.framework.intraprocedural.AbstractState;
 import com.android.tools.r8.ir.code.AssumeAndCheckCastAliasedValueConfiguration;
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.utils.ArrayUtils;
@@ -12,7 +11,6 @@
 import com.android.tools.r8.utils.IntObjConsumer;
 import com.android.tools.r8.utils.IntObjToObjFunction;
 import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
-import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
 import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -63,43 +61,34 @@
  * <p>Finally, to provide the information for each method parameter, this class provides a mapping
  * from parameters to {@link ParameterUsagePerContext}.
  */
-public class AnalysisState extends AbstractState<AnalysisState> {
+public class NonEmptyParameterUsages extends ParameterUsages {
 
-  private static final AnalysisState BOTTOM = new AnalysisState();
   private static final AssumeAndCheckCastAliasedValueConfiguration aliasedValueConfiguration =
       AssumeAndCheckCastAliasedValueConfiguration.getInstance();
 
   private final Int2ObjectMap<ParameterUsagePerContext> backing;
 
-  private AnalysisState() {
-    this.backing = Int2ObjectMaps.emptyMap();
-  }
-
-  private AnalysisState(Int2ObjectMap<ParameterUsagePerContext> backing) {
+  private NonEmptyParameterUsages(Int2ObjectMap<ParameterUsagePerContext> backing) {
     assert !backing.isEmpty() : "Should use bottom() instead";
     this.backing = backing;
   }
 
-  static AnalysisState bottom() {
-    return BOTTOM;
+  public static ParameterUsages create(Int2ObjectMap<ParameterUsagePerContext> backing) {
+    return backing.isEmpty() ? bottom() : new NonEmptyParameterUsages(backing);
   }
 
-  public static AnalysisState create(Int2ObjectMap<ParameterUsagePerContext> backing) {
-    return backing.isEmpty() ? bottom() : new AnalysisState(backing);
+  @Override
+  public NonEmptyParameterUsages asNonEmpty() {
+    return this;
   }
 
-  /**
-   * 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() {
+  @Override
+  NonEmptyParameterUsages externalize() {
     return rebuildParameters((parameter, usagePerContext) -> usagePerContext.externalize());
   }
 
-  AnalysisState put(int parameter, ParameterUsagePerContext parameterUsagePerContext) {
+  @Override
+  ParameterUsages put(int parameter, ParameterUsagePerContext parameterUsagePerContext) {
     Int2ObjectOpenHashMap<ParameterUsagePerContext> newBacking =
         new Int2ObjectOpenHashMap<>(backing);
     newBacking.put(parameter, parameterUsagePerContext);
@@ -110,22 +99,18 @@
     Int2ObjectMapUtils.forEach(backing, consumer);
   }
 
+  @Override
   public ParameterUsagePerContext get(int parameter) {
     assert backing.containsKey(parameter);
     ParameterUsagePerContext value = backing.get(parameter);
     return value != null ? value : ParameterUsagePerContext.bottom();
   }
 
-  public boolean isBottom() {
-    assert !backing.isEmpty() || this == bottom();
-    return backing.isEmpty();
-  }
-
-  AnalysisState abandonClassInliningInCurrentContexts(Value value) {
+  NonEmptyParameterUsages abandonClassInliningInCurrentContexts(Value value) {
     return rebuildParameter(value, (context, usage) -> ParameterUsage.top());
   }
 
-  AnalysisState abandonClassInliningInCurrentContexts(Collection<Value> values) {
+  NonEmptyParameterUsages abandonClassInliningInCurrentContexts(Collection<Value> values) {
     if (values.isEmpty()) {
       return this;
     }
@@ -141,7 +126,7 @@
                 : usagePerContext);
   }
 
-  AnalysisState abandonClassInliningInCurrentContexts(
+  NonEmptyParameterUsages abandonClassInliningInCurrentContexts(
       Iterable<Value> values, Predicate<Value> predicate) {
     List<Value> filtered = new ArrayList<>();
     for (Value value : values) {
@@ -153,7 +138,7 @@
     return abandonClassInliningInCurrentContexts(filtered);
   }
 
-  AnalysisState rebuildParameter(
+  NonEmptyParameterUsages rebuildParameter(
       Value value, BiFunction<AnalysisContext, ParameterUsage, ParameterUsage> transformation) {
     Value valueRoot = value.getAliasedValue(aliasedValueConfiguration);
     assert valueRoot.isArgument();
@@ -165,7 +150,7 @@
                 : usagePerContext);
   }
 
-  AnalysisState rebuildParameters(
+  NonEmptyParameterUsages rebuildParameters(
       IntObjToObjFunction<ParameterUsagePerContext, ParameterUsagePerContext> transformation) {
     Int2ObjectMap<ParameterUsagePerContext> rebuiltBacking = null;
     for (Int2ObjectMap.Entry<ParameterUsagePerContext> entry : backing.int2ObjectEntrySet()) {
@@ -190,16 +175,10 @@
         rebuiltBacking.put(parameter, newUsagePerContext);
       }
     }
-    return rebuiltBacking != null ? create(rebuiltBacking) : this;
+    return rebuiltBacking != null ? new NonEmptyParameterUsages(rebuiltBacking) : this;
   }
 
-  @Override
-  public AnalysisState asAbstractState() {
-    return this;
-  }
-
-  @Override
-  public AnalysisState join(AnalysisState otherAnalysisState) {
+  public NonEmptyParameterUsages join(NonEmptyParameterUsages otherAnalysisState) {
     if (isBottom()) {
       return otherAnalysisState;
     }
@@ -214,7 +193,7 @@
                 parameterUsagePerContext.join(
                     Int2ObjectMapUtils.getOrDefault(
                         newBacking, parameter, ParameterUsagePerContext.bottom()))));
-    return AnalysisState.create(newBacking);
+    return new NonEmptyParameterUsages(newBacking);
   }
 
   @Override
@@ -222,7 +201,7 @@
     if (other == null || getClass() != other.getClass()) {
       return false;
     }
-    AnalysisState analysisState = (AnalysisState) other;
+    NonEmptyParameterUsages analysisState = (NonEmptyParameterUsages) other;
     return backing.equals(analysisState.backing);
   }
 
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 4c5c2c2..e2ad8da 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
@@ -80,7 +80,7 @@
 
   abstract ParameterUsage setParameterUsedAsLock();
 
-  static BottomParameterUsage bottom() {
+  public static BottomParameterUsage bottom() {
     return BottomParameterUsage.getInstance();
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/ParameterUsages.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/ParameterUsages.java
new file mode 100644
index 0000000..6b4cffb
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/ParameterUsages.java
@@ -0,0 +1,65 @@
+// 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.ir.analysis.framework.intraprocedural.AbstractState;
+
+public abstract class ParameterUsages extends AbstractState<ParameterUsages> {
+
+  @Override
+  public ParameterUsages asAbstractState() {
+    return this;
+  }
+
+  public NonEmptyParameterUsages asNonEmpty() {
+    return null;
+  }
+
+  /**
+   * This converts instances inside this {@link ParameterUsages} instance that are not suitable for
+   * being stored in optimization info into instances that can be stored in the optimization info.
+   *
+   * <p>For example, converts instances of {@link InternalNonEmptyParameterUsage} to {@link
+   * NonEmptyParameterUsage}. 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.
+   */
+  abstract ParameterUsages externalize();
+
+  public abstract ParameterUsagePerContext get(int parameter);
+
+  public boolean isBottom() {
+    return false;
+  }
+
+  public boolean isTop() {
+    return false;
+  }
+
+  @Override
+  public ParameterUsages join(ParameterUsages state) {
+    if (isBottom()) {
+      return state;
+    }
+    if (state.isBottom()) {
+      return this;
+    }
+    if (isTop() || state.isTop()) {
+      return top();
+    }
+    return asNonEmpty().join(state.asNonEmpty());
+  }
+
+  abstract ParameterUsages put(int parameter, ParameterUsagePerContext usagePerContext);
+
+  public static BottomParameterUsages bottom() {
+    return BottomParameterUsages.getInstance();
+  }
+
+  public static UnknownParameterUsages top() {
+    return UnknownParameterUsages.getInstance();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/TransferFunction.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/TransferFunction.java
index c501bbe..740a8cd 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/TransferFunction.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/TransferFunction.java
@@ -22,7 +22,6 @@
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
 import com.android.tools.r8.ir.analysis.framework.intraprocedural.AbstractTransferFunction;
-import com.android.tools.r8.ir.analysis.framework.intraprocedural.TransferFunctionResult;
 import com.android.tools.r8.ir.code.AliasedValueConfiguration;
 import com.android.tools.r8.ir.code.Argument;
 import com.android.tools.r8.ir.code.Assume;
@@ -45,7 +44,7 @@
 import com.google.common.collect.Sets;
 import java.util.Set;
 
-class TransferFunction implements AbstractTransferFunction<AnalysisState> {
+class TransferFunction implements AbstractTransferFunction<ParameterUsages> {
 
   private static final AliasedValueConfiguration aliasedValueConfiguration =
       AssumeAndCheckCastAliasedValueConfiguration.getInstance();
@@ -73,7 +72,7 @@
   }
 
   @Override
-  public TransferFunctionResult<AnalysisState> apply(Instruction instruction, AnalysisState state) {
+  public ParameterUsages apply(Instruction instruction, ParameterUsages state) {
     if (instruction.isArgument()) {
       return analyzeArgument(instruction.asArgument(), state);
     }
@@ -82,6 +81,12 @@
       // purpose of class inlining we can ignore this instruction.
       return state;
     }
+    assert !state.isBottom();
+    assert !state.isTop();
+    return apply(instruction, state.asNonEmpty());
+  }
+
+  private ParameterUsages apply(Instruction instruction, NonEmptyParameterUsages state) {
     switch (instruction.opcode()) {
       case ASSUME:
         return analyzeAssume(instruction.asAssume(), state);
@@ -111,8 +116,8 @@
   }
 
   @Override
-  public AnalysisState computeBlockEntryState(
-      BasicBlock block, BasicBlock predecessor, AnalysisState predecessorExitState) {
+  public ParameterUsages computeBlockEntryState(
+      BasicBlock block, BasicBlock predecessor, ParameterUsages predecessorExitState) {
     // TODO(b/173337498): Fork a new `FIELD=x` analysis context for the successor block if the
     //  predecessor ends with an if or switch instruction, and the successor block is the
     //  `FIELD=x` target of the predecessor. To avoid an excessive number of contexts being
@@ -121,8 +126,7 @@
     return predecessorExitState;
   }
 
-  private TransferFunctionResult<AnalysisState> analyzeArgument(
-      Argument argument, AnalysisState state) {
+  private ParameterUsages analyzeArgument(Argument argument, ParameterUsages state) {
     // Only consider arguments that could store an instance eligible for class inlining. Note that
     // we can't ignore parameters with a library type, since instances of program classes could
     // still flow into such parameters.
@@ -138,18 +142,17 @@
     return state.put(argument.getIndex(), NonEmptyParameterUsagePerContext.createInitial());
   }
 
-  private TransferFunctionResult<AnalysisState> analyzeAssume(Assume assume, AnalysisState state) {
+  private ParameterUsages analyzeAssume(Assume assume, NonEmptyParameterUsages state) {
     // Mark the value as ineligible for class inlining if it has phi users.
     return assume.outValue().hasPhiUsers() ? fail(assume, state) : state;
   }
 
-  private TransferFunctionResult<AnalysisState> analyzeCheckCast(
-      CheckCast checkCast, AnalysisState state) {
+  private ParameterUsages analyzeCheckCast(CheckCast checkCast, NonEmptyParameterUsages state) {
     // Mark the value as ineligible for class inlining if it has phi users.
     return checkCast.outValue().hasPhiUsers() ? fail(checkCast, state) : state;
   }
 
-  private TransferFunctionResult<AnalysisState> analyzeIf(If theIf, AnalysisState state) {
+  private ParameterUsages analyzeIf(If theIf, NonEmptyParameterUsages state) {
     // Null/not-null tests are ok.
     if (theIf.isZeroTest()) {
       assert argumentsOfInterest.contains(theIf.lhs().getAliasedValue(aliasedValueConfiguration));
@@ -160,8 +163,8 @@
     return fail(theIf, state);
   }
 
-  private TransferFunctionResult<AnalysisState> analyzeInstanceGet(
-      InstanceGet instanceGet, AnalysisState state) {
+  private ParameterUsages analyzeInstanceGet(
+      InstanceGet instanceGet, NonEmptyParameterUsages state) {
     // Instance field reads are OK, as long as the field resolves, since the class inliner will
     // just replace the field read by the value of the field.
     FieldResolutionResult resolutionResult = appView.appInfo().resolveField(instanceGet.getField());
@@ -176,8 +179,8 @@
     return fail(instanceGet, state);
   }
 
-  private TransferFunctionResult<AnalysisState> analyzeInstancePut(
-      InstancePut instancePut, AnalysisState state) {
+  private ParameterUsages analyzeInstancePut(
+      InstancePut instancePut, NonEmptyParameterUsages state) {
     // Instance field writes are OK, as long as the field resolves and the receiver is not being
     // assigned (in that case the receiver escapes, and thus it is not eligible for class
     // inlining).
@@ -199,8 +202,7 @@
     }
   }
 
-  private TransferFunctionResult<AnalysisState> analyzeInvokeDirect(
-      InvokeDirect invoke, AnalysisState state) {
+  private ParameterUsages analyzeInvokeDirect(InvokeDirect invoke, NonEmptyParameterUsages state) {
     // We generally don't class inline instances that escape through invoke-direct calls, but we
     // make an exception for forwarding/parent constructor calls that does not leak the receiver.
     state =
@@ -242,8 +244,8 @@
     return state;
   }
 
-  private TransferFunctionResult<AnalysisState> analyzeInvokeInterface(
-      InvokeInterface invoke, AnalysisState state) {
+  private ParameterUsages analyzeInvokeInterface(
+      InvokeInterface invoke, NonEmptyParameterUsages state) {
     // We only allow invoke-interface instructions where the parameter is in the receiver position.
     state =
         state.abandonClassInliningInCurrentContexts(
@@ -264,8 +266,7 @@
         receiverRoot, (context, usage) -> usage.addMethodCallWithParameterAsReceiver(invoke));
   }
 
-  private TransferFunctionResult<AnalysisState> analyzeInvokeStatic(
-      InvokeStatic invoke, AnalysisState state) {
+  private ParameterUsages analyzeInvokeStatic(InvokeStatic invoke, NonEmptyParameterUsages state) {
     // We generally don't class inline instances that escape through invoke-static calls, but we
     // make an exception for calls to Objects.requireNonNull().
     SingleResolutionResult resolutionResult =
@@ -282,8 +283,8 @@
     return fail(invoke, state);
   }
 
-  private TransferFunctionResult<AnalysisState> analyzeInvokeVirtual(
-      InvokeVirtual invoke, AnalysisState state) {
+  private ParameterUsages analyzeInvokeVirtual(
+      InvokeVirtual invoke, NonEmptyParameterUsages state) {
     // We only allow invoke-virtual instructions where the parameter is in the receiver position.
     state =
         state.abandonClassInliningInCurrentContexts(
@@ -304,21 +305,19 @@
         receiverRoot, (context, usage) -> usage.addMethodCallWithParameterAsReceiver(invoke));
   }
 
-  private TransferFunctionResult<AnalysisState> analyzeMonitor(
-      Monitor monitor, AnalysisState state) {
+  private ParameterUsages analyzeMonitor(Monitor monitor, NonEmptyParameterUsages state) {
     // Record that the receiver is used as a lock in each context that may reach this monitor
     // instruction.
     return state.rebuildParameter(
         monitor.object(), (context, usage) -> usage.setParameterUsedAsLock());
   }
 
-  private TransferFunctionResult<AnalysisState> analyzeReturn(
-      Return theReturn, AnalysisState state) {
+  private ParameterUsages analyzeReturn(Return theReturn, NonEmptyParameterUsages state) {
     return state.rebuildParameter(
         theReturn.returnValue(), (context, usage) -> usage.setParameterReturned());
   }
 
-  private TransferFunctionResult<AnalysisState> fail(Instruction instruction, AnalysisState state) {
+  private ParameterUsages fail(Instruction instruction, NonEmptyParameterUsages state) {
     return state.abandonClassInliningInCurrentContexts(
         instruction.inValues(), this::isArgumentOfInterest);
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/UnknownParameterUsages.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/UnknownParameterUsages.java
new file mode 100644
index 0000000..13e32a7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/UnknownParameterUsages.java
@@ -0,0 +1,46 @@
+// 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;
+
+public class UnknownParameterUsages extends ParameterUsages {
+
+  private static final UnknownParameterUsages INSTANCE = new UnknownParameterUsages();
+
+  private UnknownParameterUsages() {}
+
+  static UnknownParameterUsages getInstance() {
+    return INSTANCE;
+  }
+
+  @Override
+  ParameterUsages externalize() {
+    return this;
+  }
+
+  @Override
+  public ParameterUsagePerContext get(int parameter) {
+    return ParameterUsagePerContext.top();
+  }
+
+  @Override
+  public boolean isTop() {
+    return true;
+  }
+
+  @Override
+  ParameterUsages put(int parameter, ParameterUsagePerContext usagePerContext) {
+    return this;
+  }
+
+  @Override
+  public boolean equals(Object other) {
+    return other == INSTANCE;
+  }
+
+  @Override
+  public int hashCode() {
+    return System.identityHashCode(this);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/AlwaysTrueClassInlinerMethodConstraint.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/AlwaysTrueClassInlinerMethodConstraint.java
new file mode 100644
index 0000000..30ae12b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/AlwaysTrueClassInlinerMethodConstraint.java
@@ -0,0 +1,40 @@
+// 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.ParameterUsage;
+
+public class AlwaysTrueClassInlinerMethodConstraint implements ClassInlinerMethodConstraint {
+
+  private static final AlwaysTrueClassInlinerMethodConstraint INSTANCE =
+      new AlwaysTrueClassInlinerMethodConstraint();
+
+  private AlwaysTrueClassInlinerMethodConstraint() {}
+
+  static AlwaysTrueClassInlinerMethodConstraint getInstance() {
+    return INSTANCE;
+  }
+
+  @Override
+  public ClassInlinerMethodConstraint fixupAfterRemovingThisParameter() {
+    return this;
+  }
+
+  @Override
+  public ParameterUsage getParameterUsage(int parameter) {
+    return ParameterUsage.bottom();
+  }
+
+  @Override
+  public boolean isEligibleForNewInstanceClassInlining(ProgramMethod method, int parameter) {
+    return true;
+  }
+
+  @Override
+  public boolean isEligibleForStaticGetClassInlining(ProgramMethod method, int parameter) {
+    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 c86f373..9b04087 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
@@ -20,4 +20,8 @@
   static AlwaysFalseClassInlinerMethodConstraint alwaysFalse() {
     return AlwaysFalseClassInlinerMethodConstraint.getInstance();
   }
+
+  static AlwaysTrueClassInlinerMethodConstraint alwaysTrue() {
+    return AlwaysTrueClassInlinerMethodConstraint.getInstance();
+  }
 }
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
index 5c384e8..be9d99a 100644
--- 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
@@ -6,35 +6,38 @@
 
 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.NonEmptyParameterUsages;
 import com.android.tools.r8.ir.optimize.classinliner.analysis.ParameterUsage;
 import com.android.tools.r8.ir.optimize.classinliner.analysis.ParameterUsagePerContext;
+import com.android.tools.r8.ir.optimize.classinliner.analysis.ParameterUsages;
 import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
 import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
 
 public class ConditionalClassInlinerMethodConstraint implements ClassInlinerMethodConstraint {
 
-  private final AnalysisState usages;
+  private final ParameterUsages usages;
 
-  public ConditionalClassInlinerMethodConstraint(AnalysisState usages) {
+  public ConditionalClassInlinerMethodConstraint(ParameterUsages usages) {
+    assert !usages.isTop();
     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));
+    usages
+        .asNonEmpty()
+        .forEach(
+            (parameter, usagePerContext) -> {
+              if (parameter > 0) {
+                backing.put(parameter - 1, usagePerContext);
+              }
+            });
+    return new ConditionalClassInlinerMethodConstraint(NonEmptyParameterUsages.create(backing));
   }
 
   @Override