Disable argument propagation for check-not-null classified methods

This also transfers the CheckNotNullEnumUnboxerClassification from each checkNotNull() method to the synthesized checkNotZero() methods, such that the only methods with a CheckNotNullEnumUnboxerClassification after enum unboxing is the checkNotZero() methods.

Change-Id: Ifbd863249c83693ff64b93e97fb1e714e57688a9
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index 72cddc2..0225c1d 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -97,7 +97,7 @@
   private InitializedClassesInInstanceMethods initializedClassesInInstanceMethods;
   private HorizontallyMergedClasses horizontallyMergedClasses = HorizontallyMergedClasses.empty();
   private VerticallyMergedClasses verticallyMergedClasses;
-  private EnumDataMap unboxedEnums = EnumDataMap.empty();
+  private EnumDataMap unboxedEnums = null;
   // TODO(b/169115389): Remove
   private Set<DexMethod> cfByteCodePassThrough = ImmutableSet.of();
   private Map<DexType, DexValueString> sourceDebugExtensions = new IdentityHashMap<>();
@@ -575,12 +575,16 @@
     testing().verticallyMergedClassesConsumer.accept(dexItemFactory(), verticallyMergedClasses);
   }
 
+  public boolean hasUnboxedEnums() {
+    return unboxedEnums != null;
+  }
+
   public EnumDataMap unboxedEnums() {
-    return unboxedEnums;
+    return hasUnboxedEnums() ? unboxedEnums : EnumDataMap.empty();
   }
 
   public void setUnboxedEnums(EnumDataMap unboxedEnums) {
-    assert this.unboxedEnums.isEmpty();
+    assert !hasUnboxedEnums();
     this.unboxedEnums = unboxedEnums;
     testing().unboxedEnumsConsumer.accept(dexItemFactory(), unboxedEnums);
   }
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 13b7140..53bb277 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
@@ -66,6 +66,8 @@
   void setEnumUnboxerMethodClassification(
       ProgramMethod method, EnumUnboxerMethodClassification enumUnboxerMethodClassification);
 
+  void unsetEnumUnboxerMethodClassification(ProgramMethod method);
+
   void setInstanceInitializerInfoCollection(
       DexEncodedMethod method, InstanceInitializerInfoCollection instanceInitializerInfoCollection);
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
index 46eec15..e4229c2 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
@@ -10,15 +10,18 @@
 import static com.android.tools.r8.ir.code.Opcodes.ARRAY_PUT;
 import static com.android.tools.r8.ir.code.Opcodes.ASSUME;
 import static com.android.tools.r8.ir.code.Opcodes.CHECK_CAST;
+import static com.android.tools.r8.ir.code.Opcodes.CONST_CLASS;
 import static com.android.tools.r8.ir.code.Opcodes.IF;
 import static com.android.tools.r8.ir.code.Opcodes.INSTANCE_GET;
 import static com.android.tools.r8.ir.code.Opcodes.INSTANCE_PUT;
+import static com.android.tools.r8.ir.code.Opcodes.INVOKE_CUSTOM;
 import static com.android.tools.r8.ir.code.Opcodes.INVOKE_DIRECT;
 import static com.android.tools.r8.ir.code.Opcodes.INVOKE_INTERFACE;
 import static com.android.tools.r8.ir.code.Opcodes.INVOKE_STATIC;
 import static com.android.tools.r8.ir.code.Opcodes.INVOKE_SUPER;
 import static com.android.tools.r8.ir.code.Opcodes.INVOKE_VIRTUAL;
 import static com.android.tools.r8.ir.code.Opcodes.RETURN;
+import static com.android.tools.r8.ir.code.Opcodes.STATIC_GET;
 import static com.android.tools.r8.ir.code.Opcodes.STATIC_PUT;
 import static com.android.tools.r8.utils.MapUtils.ignoreKey;
 
@@ -42,7 +45,6 @@
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.MethodResolutionResult;
 import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.PrunedItems;
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.analysis.fieldvalueanalysis.StaticFieldValues;
 import com.android.tools.r8.ir.analysis.fieldvalueanalysis.StaticFieldValues.EnumStaticFieldValues;
@@ -69,7 +71,6 @@
 import com.android.tools.r8.ir.code.InvokeStatic;
 import com.android.tools.r8.ir.code.InvokeVirtual;
 import com.android.tools.r8.ir.code.MemberType;
-import com.android.tools.r8.ir.code.Opcodes;
 import com.android.tools.r8.ir.code.Phi;
 import com.android.tools.r8.ir.code.Return;
 import com.android.tools.r8.ir.code.Value;
@@ -102,6 +103,8 @@
 import com.android.tools.r8.utils.Reporter;
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.android.tools.r8.utils.collections.ImmutableInt2ReferenceSortedMap;
+import com.android.tools.r8.utils.collections.LongLivedClassSetBuilder;
+import com.android.tools.r8.utils.collections.LongLivedProgramMethodMapBuilder;
 import com.android.tools.r8.utils.collections.LongLivedProgramMethodSetBuilder;
 import com.android.tools.r8.utils.collections.ProgramMethodMap;
 import com.android.tools.r8.utils.collections.ProgramMethodSet;
@@ -147,8 +150,8 @@
   private LongLivedProgramMethodSetBuilder<ProgramMethodSet> methodsDependingOnLibraryModelisation;
 
   // Map from checkNotNull() methods to the enums that use the given method.
-  private final ProgramMethodMap<Set<DexProgramClass>> checkNotNullMethods =
-      ProgramMethodMap.createConcurrent();
+  private LongLivedProgramMethodMapBuilder<LongLivedClassSetBuilder<DexProgramClass>>
+      checkNotNullMethodsBuilder;
 
   private final DexClassAndField ordinalField;
 
@@ -261,21 +264,21 @@
           }
         }
         switch (instruction.opcode()) {
-          case Opcodes.CONST_CLASS:
+          case CONST_CLASS:
             analyzeConstClass(instruction.asConstClass(), eligibleEnums, code.context());
             break;
-          case Opcodes.CHECK_CAST:
+          case CHECK_CAST:
             analyzeCheckCast(instruction.asCheckCast(), eligibleEnums);
             break;
-          case Opcodes.INVOKE_CUSTOM:
+          case INVOKE_CUSTOM:
             analyzeInvokeCustom(instruction.asInvokeCustom(), eligibleEnums);
             break;
           case INVOKE_STATIC:
             analyzeInvokeStatic(instruction.asInvokeStatic(), eligibleEnums, code.context());
             break;
-          case Opcodes.STATIC_GET:
-          case Opcodes.INSTANCE_GET:
-          case Opcodes.STATIC_PUT:
+          case STATIC_GET:
+          case INSTANCE_GET:
+          case STATIC_PUT:
           case INSTANCE_PUT:
             analyzeFieldInstruction(
                 instruction.asFieldInstruction(), eligibleEnums, code.context());
@@ -560,9 +563,18 @@
 
   @Override
   public void prepareForPrimaryOptimizationPass(GraphLens graphLensForPrimaryOptimizationPass) {
+    assert appView.graphLens() == graphLensForPrimaryOptimizationPass;
+    initializeCheckNotNullMethods(graphLensForPrimaryOptimizationPass);
     initializeEnumUnboxingCandidates(graphLensForPrimaryOptimizationPass);
   }
 
+  private void initializeCheckNotNullMethods(GraphLens graphLensForPrimaryOptimizationPass) {
+    assert checkNotNullMethodsBuilder == null;
+    checkNotNullMethodsBuilder =
+        LongLivedProgramMethodMapBuilder.createConcurrentBuilderForNonConcurrentMap(
+            graphLensForPrimaryOptimizationPass);
+  }
+
   private void initializeEnumUnboxingCandidates(GraphLens graphLensForPrimaryOptimizationPass) {
     assert enumUnboxingCandidatesInfo == null;
     enumUnboxingCandidatesInfo =
@@ -577,10 +589,12 @@
   public void unboxEnums(
       AppView<AppInfoWithLiveness> appView,
       IRConverter converter,
-      Builder postBuilder,
+      Builder postMethodProcessorBuilder,
       ExecutorService executorService,
       OptimizationFeedbackDelayed feedback)
       throws ExecutionException {
+    assert feedback.noUpdatesLeft();
+
     assert candidatesToRemoveInWave.isEmpty();
     EnumDataMap enumDataMap = finishAnalysis();
     assert candidatesToRemoveInWave.isEmpty();
@@ -588,9 +602,10 @@
     // At this point the enum unboxing candidates are no longer candidates, they will all be
     // unboxed. We extract the now immutable enums to unbox information and clear the candidate
     // info.
+    appView.setUnboxedEnums(enumDataMap);
+
     if (enumUnboxingCandidatesInfo.isEmpty()) {
       assert enumDataMap.isEmpty();
-      appView.setUnboxedEnums(enumDataMap);
       return;
     }
 
@@ -609,12 +624,15 @@
             .build(converter, executorService);
 
     // Fixup the application.
+    ProgramMethodMap<Set<DexProgramClass>> checkNotNullMethods =
+        checkNotNullMethodsBuilder
+            .rewrittenWithLens(appView, (enumClasses, appliedGraphLens) -> enumClasses)
+            .build(appView, builder -> builder.build(appView));
     EnumUnboxingTreeFixer.Result treeFixerResult =
         new EnumUnboxingTreeFixer(
                 appView, checkNotNullMethods, enumDataMap, enumClassesToUnbox, utilityClasses)
             .fixupTypeReferences(converter, executorService);
     EnumUnboxingLens enumUnboxingLens = treeFixerResult.getLens();
-    appView.setUnboxedEnums(enumDataMap);
 
     // Update the graph lens.
     appView.rewriteWithLens(enumUnboxingLens);
@@ -624,7 +642,7 @@
     // Note that the reprocessing set must be rewritten to the new enum unboxing lens before pruning
     // the builders with the methods removed by the tree fixer (since these methods references are
     // already fully lens rewritten).
-    postBuilder
+    postMethodProcessorBuilder
         .getMethodsToReprocessBuilder()
         .rewrittenWithLens(appView)
         .merge(dependencies)
@@ -632,7 +650,7 @@
         .removeAll(treeFixerResult.getPrunedItems().getRemovedMethods());
     methodsDependingOnLibraryModelisation.clear();
 
-    updateOptimizationInfos(executorService, feedback, treeFixerResult.getPrunedItems());
+    updateOptimizationInfos(executorService, feedback, treeFixerResult);
 
     enumUnboxerRewriter =
         new EnumUnboxingRewriter(
@@ -647,7 +665,7 @@
   private void updateOptimizationInfos(
       ExecutorService executorService,
       OptimizationFeedbackDelayed feedback,
-      PrunedItems prunedItems)
+      EnumUnboxingTreeFixer.Result treeFixerResult)
       throws ExecutionException {
     feedback.fixupOptimizationInfos(
         appView,
@@ -666,7 +684,16 @@
             optimizationInfo
                 .fixupClassTypeReferences(appView, appView.graphLens())
                 .fixupAbstractReturnValue(appView, appView.graphLens())
-                .fixupInstanceInitializerInfo(appView, appView.graphLens(), prunedItems);
+                .fixupInstanceInitializerInfo(
+                    appView, appView.graphLens(), treeFixerResult.getPrunedItems());
+
+            // Clear the enum unboxer method classification for check-not-null methods (these
+            // classifications are transferred to the synthesized check-not-zero methods by now).
+            if (!treeFixerResult
+                .getCheckNotNullToCheckNotZeroMapping()
+                .containsValue(method.getReference())) {
+              optimizationInfo.unsetEnumUnboxerMethodClassification();
+            }
           }
         });
   }
@@ -1316,10 +1343,16 @@
             classification.asCheckNotNullClassification();
         if (checkNotNullClassification.isUseEligibleForUnboxing(
             invoke.asInvokeStatic(), enumValue)) {
-          checkNotNullMethods
+          GraphLens graphLens = appView.graphLens();
+          checkNotNullMethodsBuilder
               .computeIfAbsent(
-                  singleTarget.asProgramMethod(), ignoreKey(Sets::newConcurrentHashSet))
-              .add(enumClass);
+                  singleTarget.asProgramMethod(),
+                  ignoreKey(
+                      () ->
+                          LongLivedClassSetBuilder.createConcurrentBuilderForIdentitySet(
+                              graphLens)),
+                  graphLens)
+              .add(enumClass, graphLens);
           return Reason.ELIGIBLE;
         }
       }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
index 02d8cc1..5fc743e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
@@ -398,26 +398,38 @@
       return;
     }
 
-    if (singleTarget.isProgramMethod()) {
-      EnumUnboxerMethodClassification classification =
-          singleTarget.getOptimizationInfo().getEnumUnboxerMethodClassification();
-      if (classification.isCheckNotNullClassification()) {
-        CheckNotNullEnumUnboxerMethodClassification checkNotNullClassification =
-            classification.asCheckNotNullClassification();
-        Value argument = invoke.getArgument(checkNotNullClassification.getArgumentIndex());
-        DexType enumType = getEnumTypeOrNull(argument, convertedEnums);
-        if (enumType != null) {
-          InvokeStatic replacement =
-              InvokeStatic.builder()
-                  .setMethod(checkNotNullToCheckNotZeroMapping.get(singleTarget.getReference()))
-                  .setArguments(invoke.arguments())
-                  .setPosition(invoke.getPosition())
-                  .build();
-          instructionIterator.replaceCurrentInstruction(replacement);
-          convertedEnums.put(replacement, enumType);
+    if (singleTarget.isProgramMethod()
+        && checkNotNullToCheckNotZeroMapping.containsKey(singleTarget.getReference())) {
+      DexMethod checkNotZeroMethodReference =
+          checkNotNullToCheckNotZeroMapping.get(singleTarget.getReference());
+      ProgramMethod checkNotZeroMethod =
+          appView
+              .appInfo()
+              .resolveMethodOnClass(checkNotZeroMethodReference)
+              .getResolvedProgramMethod();
+      if (checkNotZeroMethod != null) {
+        EnumUnboxerMethodClassification classification =
+            checkNotZeroMethod.getOptimizationInfo().getEnumUnboxerMethodClassification();
+        if (classification.isCheckNotNullClassification()) {
+          CheckNotNullEnumUnboxerMethodClassification checkNotNullClassification =
+              classification.asCheckNotNullClassification();
+          Value argument = invoke.getArgument(checkNotNullClassification.getArgumentIndex());
+          DexType enumType = getEnumTypeOrNull(argument, convertedEnums);
+          if (enumType != null) {
+            InvokeStatic replacement =
+                InvokeStatic.builder()
+                    .setMethod(checkNotZeroMethod)
+                    .setArguments(invoke.arguments())
+                    .setPosition(invoke.getPosition())
+                    .build();
+            instructionIterator.replaceCurrentInstruction(replacement);
+            convertedEnums.put(replacement, enumType);
+          }
+        } else {
+          assert false;
         }
       } else {
-        assert !checkNotNullToCheckNotZeroMapping.containsKey(singleTarget.getReference());
+        assert false;
       }
     }
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
index c7bcc37..8a97aef 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
@@ -54,10 +54,11 @@
 import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Timing;
 import com.android.tools.r8.utils.collections.ProgramMethodMap;
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
 import com.google.common.collect.Sets;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.IdentityHashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.ListIterator;
@@ -130,16 +131,16 @@
     }
 
     // Create mapping from checkNotNull() to checkNotZero() methods.
-    Map<DexMethod, DexMethod> checkNotNullToCheckNotZeroMapping =
+    BiMap<DexMethod, DexMethod> checkNotNullToCheckNotZeroMapping =
         duplicateCheckNotNullMethods(converter, executorService);
 
     return new Result(
         checkNotNullToCheckNotZeroMapping, lensBuilder.build(appView), prunedItemsBuilder.build());
   }
 
-  private Map<DexMethod, DexMethod> duplicateCheckNotNullMethods(
+  private BiMap<DexMethod, DexMethod> duplicateCheckNotNullMethods(
       IRConverter converter, ExecutorService executorService) throws ExecutionException {
-    Map<DexMethod, DexMethod> checkNotNullToCheckNotZeroMapping = new IdentityHashMap<>();
+    BiMap<DexMethod, DexMethod> checkNotNullToCheckNotZeroMapping = HashBiMap.create();
     ProcessorContext processorContext = appView.createProcessorContext();
     OneTimeMethodProcessor.Builder methodProcessorBuilder =
         OneTimeMethodProcessor.builder(processorContext);
@@ -186,6 +187,11 @@
                               .setApiLevelForDefinition(minApiLevelIfEnabledOrUnknown(appView))
                               .setApiLevelForCode(minApiLevelIfEnabledOrUnknown(appView))
                               .setCode(method -> new CheckNotZeroCode(checkNotNullMethod))
+                              .setOptimizationInfo(
+                                  checkNotNullMethod
+                                      .getOptimizationInfo()
+                                      .asMutableMethodOptimizationInfo()
+                                      .mutableCopy())
                               .setProto(newProto));
           checkNotNullToCheckNotZeroMapping.put(
               checkNotNullMethod.getReference(), checkNotZeroMethod.getReference());
@@ -603,12 +609,12 @@
 
   public static class Result {
 
-    private final Map<DexMethod, DexMethod> checkNotNullToCheckNotZeroMapping;
+    private final BiMap<DexMethod, DexMethod> checkNotNullToCheckNotZeroMapping;
     private final EnumUnboxingLens lens;
     private final PrunedItems prunedItems;
 
     Result(
-        Map<DexMethod, DexMethod> checkNotNullToCheckNotZeroMapping,
+        BiMap<DexMethod, DexMethod> checkNotNullToCheckNotZeroMapping,
         EnumUnboxingLens lens,
         PrunedItems prunedItems) {
       this.checkNotNullToCheckNotZeroMapping = checkNotNullToCheckNotZeroMapping;
@@ -616,7 +622,7 @@
       this.prunedItems = prunedItems;
     }
 
-    Map<DexMethod, DexMethod> getCheckNotNullToCheckNotZeroMapping() {
+    BiMap<DexMethod, DexMethod> getCheckNotNullToCheckNotZeroMapping() {
       return checkNotNullToCheckNotZeroMapping;
     }
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/classification/EnumUnboxerMethodClassificationAnalysis.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/classification/EnumUnboxerMethodClassificationAnalysis.java
index f01abfc..3d8bccc 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/classification/EnumUnboxerMethodClassificationAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/classification/EnumUnboxerMethodClassificationAnalysis.java
@@ -46,13 +46,25 @@
     }
 
     // Look for an argument with a single if-zero user.
+    EnumUnboxerMethodClassification currentClassification =
+        method.getOptimizationInfo().getEnumUnboxerMethodClassification();
     DexItemFactory dexItemFactory = appView.dexItemFactory();
     InstructionIterator entryIterator = code.entryBlock().iterator();
     for (int index = 0; index < method.getParameters().size(); index++) {
       Argument argument = entryIterator.next().asArgument();
       DexType parameter = method.getParameter(index);
-      if (parameter != dexItemFactory.objectType) {
-        continue;
+      // Before enum unboxing, we classify methods with `object != null` as check-not-null methods.
+      // After enum unboxing, we check correctness of the classification for check-not-zero methods.
+      if (appView.hasUnboxedEnums()) {
+        if (parameter != dexItemFactory.intType
+            || !currentClassification.isCheckNotNullClassification()
+            || currentClassification.asCheckNotNullClassification().getArgumentIndex() != index) {
+          continue;
+        }
+      } else {
+        if (parameter != dexItemFactory.objectType) {
+          continue;
+        }
       }
 
       if (onlyHasCheckNotNullUsers(argument, methodProcessor)) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/code/CheckNotZeroCode.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/code/CheckNotZeroCode.java
index 38ad5cb..c2cbec9 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/code/CheckNotZeroCode.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/code/CheckNotZeroCode.java
@@ -49,7 +49,7 @@
     // Start iterating at the argument instruction for the checked argument.
     IteratorUtils.skip(
         instructionIterator,
-        checkNotNullMethod
+        checkNotZeroMethod
             .getOptimizationInfo()
             .getEnumUnboxerMethodClassification()
             .asCheckNotNullClassification()
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 6abfb55..db7c3d0 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
@@ -808,9 +808,33 @@
       IRCode code,
       OptimizationFeedback feedback,
       MethodProcessor methodProcessor) {
-    EnumUnboxerMethodClassification enumUnboxerMethodClassification =
-        EnumUnboxerMethodClassificationAnalysis.analyze(appView, method, code, methodProcessor);
-    feedback.setEnumUnboxerMethodClassification(method, enumUnboxerMethodClassification);
+    if (appView.hasUnboxedEnums()) {
+      if (appView.unboxedEnums().isEmpty()) {
+        feedback.unsetEnumUnboxerMethodClassification(method);
+      } else {
+        assert verifyEnumUnboxerMethodClassificationCorrect(method, code, methodProcessor);
+      }
+    } else {
+      EnumUnboxerMethodClassification enumUnboxerMethodClassification =
+          EnumUnboxerMethodClassificationAnalysis.analyze(appView, method, code, methodProcessor);
+      feedback.setEnumUnboxerMethodClassification(method, enumUnboxerMethodClassification);
+    }
+  }
+
+  private boolean verifyEnumUnboxerMethodClassificationCorrect(
+      ProgramMethod method, IRCode code, MethodProcessor methodProcessor) {
+    EnumUnboxerMethodClassification existingClassification =
+        method.getOptimizationInfo().getEnumUnboxerMethodClassification();
+    if (existingClassification.isCheckNotNullClassification()) {
+      EnumUnboxerMethodClassification computedClassification =
+          EnumUnboxerMethodClassificationAnalysis.analyze(appView, method, code, methodProcessor);
+      assert computedClassification.isCheckNotNullClassification();
+      assert computedClassification.asCheckNotNullClassification().getArgumentIndex()
+          == existingClassification.asCheckNotNullClassification().getArgumentIndex();
+    } else {
+      assert existingClassification.isUnknownClassification();
+    }
+    return true;
   }
 
   private void computeSimpleInliningConstraint(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
index c4a08f4..d8bbe6b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
@@ -280,6 +280,10 @@
     this.enumUnboxerMethodClassification = enumUnboxerMethodClassification;
   }
 
+  public void unsetEnumUnboxerMethodClassification() {
+    this.enumUnboxerMethodClassification = EnumUnboxerMethodClassification.unknown();
+  }
+
   public MutableMethodOptimizationInfo fixupEnumUnboxerMethodClassification(
       MethodOptimizationInfoFixer fixer) {
     enumUnboxerMethodClassification =
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 c9390d7..067407d 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
@@ -272,6 +272,11 @@
   }
 
   @Override
+  public void unsetEnumUnboxerMethodClassification(ProgramMethod method) {
+    getMethodOptimizationInfoForUpdating(method).unsetEnumUnboxerMethodClassification();
+  }
+
+  @Override
   public synchronized void setInstanceInitializerInfoCollection(
       DexEncodedMethod method,
       InstanceInitializerInfoCollection instanceInitializerInfoCollection) {
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 623d8d2..1894532 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
@@ -122,6 +122,9 @@
       ProgramMethod method, EnumUnboxerMethodClassification enumUnboxerMethodClassification) {}
 
   @Override
+  public void unsetEnumUnboxerMethodClassification(ProgramMethod method) {}
+
+  @Override
   public void setInstanceInitializerInfoCollection(
       DexEncodedMethod method,
       InstanceInitializerInfoCollection instanceInitializerInfoCollection) {}
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 47f1b8e..d5859c1 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
@@ -194,6 +194,16 @@
   }
 
   @Override
+  public void unsetEnumUnboxerMethodClassification(ProgramMethod method) {
+    if (method.getOptimizationInfo().isMutableOptimizationInfo()) {
+      method
+          .getOptimizationInfo()
+          .asMutableMethodOptimizationInfo()
+          .unsetEnumUnboxerMethodClassification();
+    }
+  }
+
+  @Override
   public void setInstanceInitializerInfoCollection(
       DexEncodedMethod method,
       InstanceInitializerInfoCollection instanceInitializerInfoCollection) {
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
index 76845cc..a7a68ff 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
@@ -394,6 +394,13 @@
           || method.getDefinition().isInstanceInitializer()) {
         return ArgumentInfoCollection.empty();
       }
+      // TODO(b/199864962): Allow parameter removal from check-not-null classified methods.
+      if (method
+          .getOptimizationInfo()
+          .getEnumUnboxerMethodClassification()
+          .isCheckNotNullClassification()) {
+        return ArgumentInfoCollection.empty();
+      }
       return computeRemovableParametersFromMethod(method);
     }
 
diff --git a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/CheckNotZeroMethodWithArgumentRemovalTest.java b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/CheckNotZeroMethodWithArgumentRemovalTest.java
new file mode 100644
index 0000000..be25553
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/CheckNotZeroMethodWithArgumentRemovalTest.java
@@ -0,0 +1,79 @@
+// 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.optimize.argumentpropagation;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class CheckNotZeroMethodWithArgumentRemovalTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection parameters() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(Main.class)
+        .addEnumUnboxingInspector(inspector -> inspector.assertUnboxed(MyEnum.class))
+        .enableInliningAnnotations()
+        // TODO(b/173398086): uniqueMethodWithName() does not work with argument removal.
+        .noMinification()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .inspect(
+            inspector -> {
+              ClassSubject mainClassSubject = inspector.clazz(Main.class);
+              assertThat(mainClassSubject, isPresent());
+
+              MethodSubject checkNotNullSubject =
+                  mainClassSubject.uniqueMethodWithName("checkNotNull");
+              assertThat(checkNotNullSubject, isPresent());
+              // TODO(b/199864962): Allow parameter removal from check-not-null classified methods.
+              assertEquals(2, checkNotNullSubject.getProgramMethod().getReference().getArity());
+            })
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithEmptyOutput();
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      checkNotNull(System.currentTimeMillis() > 0 ? MyEnum.A : null, "x");
+      checkNotNull(System.currentTimeMillis() > 0 ? new Object() : null, "x");
+    }
+
+    @NeverInline
+    static void checkNotNull(Object o, String name) {
+      if (o == null) {
+        throw new NullPointerException("Expected not null, but " + name + " was null");
+      }
+    }
+  }
+
+  enum MyEnum {
+    A,
+    B
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/MixedArgumentRemovalAndEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/MixedArgumentRemovalAndEnumUnboxingTest.java
index ad4c08a..58f2423 100644
--- a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/MixedArgumentRemovalAndEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/MixedArgumentRemovalAndEnumUnboxingTest.java
@@ -84,7 +84,7 @@
     }
 
     @NeverInline
-    static void test(Object alwaysNull, MyEnum alwaysA, Object alsoAlwaysNull, MyEnum alwaysB) {
+    static void test(Main alwaysNull, MyEnum alwaysA, Main alsoAlwaysNull, MyEnum alwaysB) {
       if (alwaysNull == null && alsoAlwaysNull == null) {
         System.out.println(alwaysA.name());
         System.out.println(alwaysB.name());