Merge commit 'e6f2ba20fe5c7e7ef5d3b6ca6b4872b9e2f87cf1' into dev-release
diff --git a/src/main/java/com/android/tools/r8/graph/CfCode.java b/src/main/java/com/android/tools/r8/graph/CfCode.java
index 0a7c0e5..fd550c1 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -551,12 +551,21 @@
 
   @Override
   public void registerCodeReferences(ProgramMethod method, UseRegistry registry) {
+    assert registry.getTraversalContinuation().shouldContinue();
     ListIterator<CfInstruction> iterator = instructions.listIterator();
     while (iterator.hasNext()) {
       CfInstruction instruction = iterator.next();
       instruction.registerUse(registry, method, iterator);
+      if (registry.getTraversalContinuation().shouldBreak()) {
+        return;
+      }
     }
-    tryCatchRanges.forEach(tryCatch -> tryCatch.internalRegisterUse(registry, method));
+    for (CfTryCatch tryCatch : tryCatchRanges) {
+      tryCatch.internalRegisterUse(registry, method);
+      if (registry.getTraversalContinuation().shouldBreak()) {
+        return;
+      }
+    }
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/graph/DexCode.java b/src/main/java/com/android/tools/r8/graph/DexCode.java
index 589ae3a..2c36882 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCode.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCode.java
@@ -301,12 +301,19 @@
   }
 
   private void internalRegisterCodeReferences(DexClassAndMethod method, UseRegistry registry) {
+    assert registry.getTraversalContinuation().shouldContinue();
     for (Instruction insn : instructions) {
       insn.registerUse(registry);
+      if (registry.getTraversalContinuation().shouldBreak()) {
+        return;
+      }
     }
     for (TryHandler handler : handlers) {
       for (TypeAddrPair pair : handler.pairs) {
         registry.registerExceptionGuard(pair.type);
+        if (registry.getTraversalContinuation().shouldBreak()) {
+          return;
+        }
       }
     }
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index c68adac..b6c7d02 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -88,6 +88,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.function.BiConsumer;
 import java.util.function.BiFunction;
 import java.util.function.Consumer;
 import java.util.function.Function;
@@ -1627,12 +1628,19 @@
 
     public Builder adjustOptimizationInfoAfterRemovingThisParameter(
         AppView<AppInfoWithLiveness> appView) {
-      if (optimizationInfo.isMutableOptimizationInfo()) {
-        optimizationInfo
-            .asMutableMethodOptimizationInfo()
-            .adjustOptimizationInfoAfterRemovingThisParameter(appView);
-      }
-      return this;
+      return modifyOptimizationInfo(
+          (newMethod, optimizationInfo) ->
+              optimizationInfo.adjustOptimizationInfoAfterRemovingThisParameter(appView));
+    }
+
+    public Builder modifyOptimizationInfo(
+        BiConsumer<DexEncodedMethod, MutableMethodOptimizationInfo> consumer) {
+      return addBuildConsumer(
+          newMethod -> {
+            if (optimizationInfo.isMutableOptimizationInfo()) {
+              consumer.accept(newMethod, optimizationInfo.asMutableMethodOptimizationInfo());
+            }
+          });
     }
 
     public Builder setCode(Code code) {
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramMethod.java b/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
index 5d5a755..4d1790e 100644
--- a/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
@@ -62,6 +62,11 @@
     }
   }
 
+  public <T> T registerCodeReferencesWithResult(UseRegistryWithResult<T> registry) {
+    registerCodeReferences(registry);
+    return registry.getResult();
+  }
+
   @Override
   public ProgramMethod getContext() {
     return this;
diff --git a/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java b/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java
index 4304fdd..98bf1c3 100644
--- a/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java
+++ b/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java
@@ -12,15 +12,16 @@
 import com.android.tools.r8.ir.conversion.ExtraParameter;
 import com.android.tools.r8.ir.conversion.ExtraUnusedNullParameter;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.utils.BooleanUtils;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Ordering;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceMap.Entry;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceRBTreeMap;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceSortedMap;
 import it.unimi.dsi.fastutil.ints.IntBidirectionalIterator;
 import it.unimi.dsi.fastutil.ints.IntSortedSet;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Iterator;
 import java.util.List;
 import java.util.function.Consumer;
 
@@ -222,6 +223,10 @@
       return this == EMPTY;
     }
 
+    public Iterator<Entry<ArgumentInfo>> iterator() {
+      return argumentInfos.int2ReferenceEntrySet().iterator();
+    }
+
     public boolean hasRemovedArguments() {
       for (ArgumentInfo value : argumentInfos.values()) {
         if (value.isRemovedArgumentInfo()) {
@@ -300,7 +305,7 @@
         return params;
       }
       DexType[] newParams = new DexType[params.length - numberOfRemovedArguments()];
-      int offset = encodedMethod.isStatic() ? 0 : 1;
+      int offset = encodedMethod.getFirstNonReceiverArgumentIndex();
       int newParamIndex = 0;
       for (int oldParamIndex = 0; oldParamIndex < params.length; oldParamIndex++) {
         ArgumentInfo argInfo = argumentInfos.get(oldParamIndex + offset);
@@ -363,7 +368,7 @@
         DexEncodedMethod method) {
       if (numberOfRemovedArguments() > 0 && !method.parameterAnnotationsList.isEmpty()) {
         return builder -> {
-          int firstArgumentIndex = BooleanUtils.intValue(!method.isStatic());
+          int firstArgumentIndex = method.getFirstNonReceiverArgumentIndex();
           builder.removeParameterAnnotations(
               oldIndex -> getArgumentInfo(oldIndex + firstArgumentIndex).isRemovedArgumentInfo());
         };
diff --git a/src/main/java/com/android/tools/r8/graph/UseRegistry.java b/src/main/java/com/android/tools/r8/graph/UseRegistry.java
index c9c0bc9..3913383 100644
--- a/src/main/java/com/android/tools/r8/graph/UseRegistry.java
+++ b/src/main/java/com/android/tools/r8/graph/UseRegistry.java
@@ -4,11 +4,13 @@
 package com.android.tools.r8.graph;
 
 import com.android.tools.r8.code.CfOrDexInstruction;
+import com.android.tools.r8.utils.TraversalContinuation;
 import java.util.ListIterator;
 
 public abstract class UseRegistry {
 
   private DexItemFactory factory;
+  private TraversalContinuation continuation = TraversalContinuation.CONTINUE;
 
   public enum MethodHandleUse {
     ARGUMENT_TO_LAMBDA_METAFACTORY,
@@ -23,6 +25,15 @@
     method.registerCodeReferences(this);
   }
 
+  public void doBreak() {
+    assert continuation.shouldContinue();
+    continuation = TraversalContinuation.BREAK;
+  }
+
+  public TraversalContinuation getTraversalContinuation() {
+    return continuation;
+  }
+
   public abstract void registerInitClass(DexType type);
 
   public abstract void registerInvokeVirtual(DexMethod method);
diff --git a/src/main/java/com/android/tools/r8/graph/UseRegistryWithResult.java b/src/main/java/com/android/tools/r8/graph/UseRegistryWithResult.java
new file mode 100644
index 0000000..916a2f9
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/UseRegistryWithResult.java
@@ -0,0 +1,28 @@
+// 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.graph;
+
+public abstract class UseRegistryWithResult<T> extends UseRegistry {
+
+  private T result;
+
+  public UseRegistryWithResult(DexItemFactory factory) {
+    super(factory);
+  }
+
+  public UseRegistryWithResult(DexItemFactory factory, T defaultResult) {
+    super(factory);
+    this.result = defaultResult;
+  }
+
+  public T getResult() {
+    return result;
+  }
+
+  public void setResult(T result) {
+    this.result = result;
+    doBreak();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ConstructorEntryPointSynthesizedCode.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ConstructorEntryPointSynthesizedCode.java
index da12b74..036a381 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ConstructorEntryPointSynthesizedCode.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ConstructorEntryPointSynthesizedCode.java
@@ -42,8 +42,12 @@
   }
 
   private void registerReachableDefinitions(UseRegistry registry) {
+    assert registry.getTraversalContinuation().shouldContinue();
     for (DexMethod typeConstructor : typeConstructors.values()) {
       registry.registerInvokeDirect(typeConstructor);
+      if (registry.getTraversalContinuation().shouldBreak()) {
+        return;
+      }
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/VirtualMethodEntryPointSynthesizedCode.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/VirtualMethodEntryPointSynthesizedCode.java
index 4402a83..c258558 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/VirtualMethodEntryPointSynthesizedCode.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/VirtualMethodEntryPointSynthesizedCode.java
@@ -17,8 +17,6 @@
 public class VirtualMethodEntryPointSynthesizedCode extends SynthesizedCode {
   private final Int2ReferenceSortedMap<DexMethod> mappedMethods;
 
-  private final DexItemFactory factory;
-
   public VirtualMethodEntryPointSynthesizedCode(
       Int2ReferenceSortedMap<DexMethod> mappedMethods,
       DexField classIdField,
@@ -35,7 +33,6 @@
                 method,
                 position,
                 originalMethod));
-    this.factory = factory;
     this.mappedMethods = mappedMethods;
   }
 
@@ -55,8 +52,12 @@
   }
 
   private void registerReachableDefinitions(UseRegistry registry) {
+    assert registry.getTraversalContinuation().shouldContinue();
     for (DexMethod mappedMethod : mappedMethods.values()) {
       registry.registerInvokeDirect(mappedMethod);
+      if (registry.getTraversalContinuation().shouldBreak()) {
+        return;
+      }
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilderBase.java b/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilderBase.java
index 2be47ca..1e5a344 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilderBase.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilderBase.java
@@ -144,11 +144,10 @@
         // We don't care about calls to native methods.
         return;
       }
-      if (!appView.getKeepInfo(callee).isInliningAllowed(appView.options())) {
-        // Since the callee is kept and optimizations are disallowed, we cannot inline it into the
-        // caller, and we also cannot collect any optimization info for the method. Therefore, we
-        // drop the call edge to reduce the total number of call graph edges, which should lead to
-        // fewer call graph cycles.
+      if (appView.appInfo().isPinned(callee.getReference())) {
+        // Since the callee is kept, we cannot inline it into the caller, and we also cannot collect
+        // any optimization info for the method. Therefore, we drop the call edge to reduce the
+        // total number of call graph edges, which should lead to fewer call graph cycles.
         return;
       }
       getOrCreateNode(callee).addCallerConcurrently(currentMethod, likelySpuriousCallEdge);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index b35e2ca..b6ffb43 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -542,7 +542,7 @@
     int originalNumberOfArguments =
         method.getReference().proto.parameters.values.length
             + argumentsInfo.numberOfRemovedArguments()
-            + (method.isStatic() ? 0 : 1)
+            + method.getFirstNonReceiverArgumentIndex()
             - prototypeChanges.numberOfExtraParameters();
 
     int usedArgumentIndex = 0;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index e1c1ba6..60ecf33 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -1313,7 +1313,6 @@
           .libraryMethodOptimizer()
           .optimize(code, feedback, methodProcessor, methodProcessingContext);
       timing.end();
-      previous = printMethod(code, "IR after class library method optimizer (SSA)", previous);
       assert code.isConsistentSSA();
     }
 
@@ -1324,7 +1323,6 @@
       timing.begin("Devirtualize invoke interface");
       devirtualizer.devirtualizeInvokeInterface(code);
       timing.end();
-      previous = printMethod(code, "IR after devirtualizer (SSA)", previous);
     }
 
     assert code.verifyTypes(appView);
@@ -1401,14 +1399,12 @@
       timing.begin("Rewrite throw NPE");
       codeRewriter.rewriteThrowNullPointerException(code);
       timing.end();
-      previous = printMethod(code, "IR after rewrite throw null (SSA)", previous);
     }
 
     timing.begin("Optimize class initializers");
     ClassInitializerDefaultsResult classInitializerDefaultsResult =
         classInitializerDefaultsOptimization.optimize(code, feedback);
     timing.end();
-    previous = printMethod(code, "IR after class initializer optimisation (SSA)", previous);
 
     if (Log.ENABLED) {
       Log.debug(getClass(), "Intermediate (SSA) flow graph for %s:\n%s",
@@ -1420,7 +1416,7 @@
     deadCodeRemover.run(code, timing);
     assert code.isConsistentSSA();
 
-    previous = printMethod(code, "IR after dead code removal (SSA)", previous);
+    previous = printMethod(code, "IR after lambda desugaring (SSA)", previous);
 
     assert code.verifyTypes(appView);
 
@@ -1606,7 +1602,7 @@
       timing.end();
     }
 
-    if (appView.getKeepInfo(code.context()).isPinned(options)) {
+    if (appView.getKeepInfo().getMethodInfo(code.context()).isPinned(options)) {
       return;
     }
 
@@ -1728,7 +1724,8 @@
         || definition.getOptimizationInfo().isReachabilitySensitive()) {
       return false;
     }
-    if (!appView.getKeepInfo(method).isInliningAllowed(options)) {
+    if (appView.appInfo().hasLiveness()
+        && appView.appInfo().withLiveness().isPinned(method.getReference())) {
       return false;
     }
     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 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/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index 7bf0e38..0ddc915 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -118,7 +118,7 @@
       WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
     AppInfoWithLiveness appInfo = appView.appInfo();
     DexMethod singleTargetReference = singleTarget.getReference();
-    if (!appView.getKeepInfo(singleTarget).isInliningAllowed(appView.options())) {
+    if (appInfo.isPinned(singleTargetReference)) {
       whyAreYouNotInliningReporter.reportPinned();
       return true;
     }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java b/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
index b2e8366..df7eeb3 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
@@ -305,7 +305,7 @@
 
     ArgumentInfoCollection.Builder argInfosBuilder = ArgumentInfoCollection.builder();
     DexProto proto = encodedMethod.getReference().proto;
-    int offset = encodedMethod.isStatic() ? 0 : 1;
+    int offset = encodedMethod.getFirstNonReceiverArgumentIndex();
     for (int i = 0; i < proto.parameters.size(); ++i) {
       DexType type = proto.parameters.values[i];
       if (type.isAlwaysNull(appView)) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java
index cc8f5b1..052e196 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java
@@ -282,7 +282,7 @@
         return null;
       }
     }
-    int offset = method.accessFlags.isStatic() ? 0 : 1;
+    int offset = method.getFirstNonReceiverArgumentIndex();
     int argumentCount = method.getReference().proto.parameters.size() + offset;
     CollectUsedArguments collector = new CollectUsedArguments();
     if (!method.accessFlags.isStatic()) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/classification/CheckNotNullEnumUnboxerMethodClassification.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/classification/CheckNotNullEnumUnboxerMethodClassification.java
index fd892c1..77d6178 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/classification/CheckNotNullEnumUnboxerMethodClassification.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/classification/CheckNotNullEnumUnboxerMethodClassification.java
@@ -4,8 +4,13 @@
 
 package com.android.tools.r8.ir.optimize.enums.classification;
 
+import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfo;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
 import com.android.tools.r8.ir.code.InvokeStatic;
 import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.utils.IteratorUtils;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceMap.Entry;
+import java.util.Iterator;
 
 public final class CheckNotNullEnumUnboxerMethodClassification
     extends EnumUnboxerMethodClassification {
@@ -31,6 +36,47 @@
   }
 
   @Override
+  public EnumUnboxerMethodClassification fixupAfterParameterRemoval(
+      ArgumentInfoCollection removedParameters) {
+    if (removedParameters.getArgumentInfo(argumentIndex).isRemovedArgumentInfo()) {
+      // If the null-checked argument is removed from the parameters of the method, then we can no
+      // longer classify this method as a check-not-null method. This is OK in terms of enum
+      // unboxing, since after the parameter removal enums at the call site will no longer have the
+      // check-not-null invoke instruction as a user.
+      //
+      // Note that when we materialize the enum instance in the check-not-null method, it is
+      // important that this method is reprocessed by enum unboxing (or that materialized instance
+      // would not be unboxed). This is guaranteed by argument removal: Since we have removed a
+      // parameter from the method, we will need to reprocess its code in the second optimization
+      // pass.
+      return unknown();
+    }
+
+    int numberOfArgumentsRemovedBeforeThis = 0;
+
+    Iterator<Entry<ArgumentInfo>> iterator = removedParameters.iterator();
+    while (iterator.hasNext()) {
+      Entry<ArgumentInfo> entry = iterator.next();
+      int argumentIndexForInfo = entry.getIntKey();
+      if (argumentIndexForInfo >= getArgumentIndex()) {
+        break;
+      }
+      ArgumentInfo argumentInfo = entry.getValue();
+      if (argumentInfo.isRemovedArgumentInfo()) {
+        numberOfArgumentsRemovedBeforeThis++;
+      }
+    }
+
+    assert IteratorUtils.allRemainingMatchDestructive(
+        iterator, entry -> entry.getIntKey() >= getArgumentIndex());
+
+    return numberOfArgumentsRemovedBeforeThis > 0
+        ? new CheckNotNullEnumUnboxerMethodClassification(
+            getArgumentIndex() - numberOfArgumentsRemovedBeforeThis)
+        : this;
+  }
+
+  @Override
   public boolean isCheckNotNullClassification() {
     return true;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/classification/EnumUnboxerMethodClassification.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/classification/EnumUnboxerMethodClassification.java
index 2b34291..5c93c36 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/classification/EnumUnboxerMethodClassification.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/classification/EnumUnboxerMethodClassification.java
@@ -4,12 +4,17 @@
 
 package com.android.tools.r8.ir.optimize.enums.classification;
 
+import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
+
 public abstract class EnumUnboxerMethodClassification {
 
   public static UnknownEnumUnboxerMethodClassification unknown() {
     return UnknownEnumUnboxerMethodClassification.getInstance();
   }
 
+  public abstract EnumUnboxerMethodClassification fixupAfterParameterRemoval(
+      ArgumentInfoCollection removedParameters);
+
   public EnumUnboxerMethodClassification fixupAfterRemovingThisParameter() {
     // Only static methods are currently classified by the enum unboxer.
     assert isUnknownClassification();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/classification/UnknownEnumUnboxerMethodClassification.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/classification/UnknownEnumUnboxerMethodClassification.java
index 9f51d0a..e88239b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/classification/UnknownEnumUnboxerMethodClassification.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/classification/UnknownEnumUnboxerMethodClassification.java
@@ -4,6 +4,8 @@
 
 package com.android.tools.r8.ir.optimize.enums.classification;
 
+import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
+
 public final class UnknownEnumUnboxerMethodClassification extends EnumUnboxerMethodClassification {
 
   private static final UnknownEnumUnboxerMethodClassification INSTANCE =
@@ -16,6 +18,12 @@
   }
 
   @Override
+  public EnumUnboxerMethodClassification fixupAfterParameterRemoval(
+      ArgumentInfoCollection removedParameters) {
+    return this;
+  }
+
+  @Override
   public boolean isUnknownClassification() {
     return true;
   }
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 d0c82cb..986ba65 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
@@ -85,7 +85,7 @@
   }
 
   private TypeElement[] getStaticTypes(AppView<?> appView, DexEncodedMethod method) {
-    int argOffset = method.isStatic() ? 0 : 1;
+    int argOffset = method.getFirstNonReceiverArgumentIndex();
     int size = method.getReference().getArity() + argOffset;
     TypeElement[] staticTypes = new TypeElement[size];
     if (!method.isStatic()) {
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 3efe7f6..4263b35 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
@@ -261,6 +261,10 @@
     this.enumUnboxerMethodClassification = enumUnboxerMethodClassification;
   }
 
+  void unsetEnumUnboxerMethodClassification() {
+    this.enumUnboxerMethodClassification = EnumUnboxerMethodClassification.unknown();
+  }
+
   @Override
   public TypeElement getDynamicUpperBoundType() {
     return returnsObjectWithUpperBoundType;
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..bd275db 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 synchronized 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 fe00440..10649fb 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
@@ -187,7 +187,15 @@
   @Override
   public void setEnumUnboxerMethodClassification(
       ProgramMethod method, EnumUnboxerMethodClassification enumUnboxerMethodClassification) {
-    // Ignored.
+    method
+        .getDefinition()
+        .getMutableOptimizationInfo()
+        .setEnumUnboxerMethodClassification(enumUnboxerMethodClassification);
+  }
+
+  @Override
+  public void unsetEnumUnboxerMethodClassification(ProgramMethod method) {
+    method.getDefinition().getMutableOptimizationInfo().unsetEnumUnboxerMethodClassification();
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/SynthesizedCode.java b/src/main/java/com/android/tools/r8/ir/synthetic/SynthesizedCode.java
index eb56742..6bd2080 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/SynthesizedCode.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/SynthesizedCode.java
@@ -4,22 +4,15 @@
 
 package com.android.tools.r8.ir.synthetic;
 
-import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.UseRegistry;
 import java.util.function.Consumer;
 
-public class SynthesizedCode extends AbstractSynthesizedCode {
+public abstract class SynthesizedCode extends AbstractSynthesizedCode {
 
   private final SourceCodeProvider sourceCodeProvider;
-  private final Consumer<UseRegistry> registryCallback;
 
   public SynthesizedCode(SourceCodeProvider sourceCodeProvider) {
-    this(sourceCodeProvider, SynthesizedCode::registerReachableDefinitionsDefault);
-  }
-
-  private SynthesizedCode(SourceCodeProvider sourceCodeProvider, Consumer<UseRegistry> callback) {
     this.sourceCodeProvider = sourceCodeProvider;
-    this.registryCallback = callback;
   }
 
   @Override
@@ -28,11 +21,5 @@
   }
 
   @Override
-  public Consumer<UseRegistry> getRegistryCallback() {
-    return registryCallback;
-  }
-
-  private static void registerReachableDefinitionsDefault(UseRegistry registry) {
-    throw new Unreachable();
-  }
+  public abstract Consumer<UseRegistry> getRegistryCallback();
 }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java
index f81b135..45420b6 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java
@@ -21,11 +21,13 @@
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Timing;
+import com.google.common.collect.Sets;
 import java.util.Collection;
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
+import java.util.function.Consumer;
 
 /** Optimization that propagates information about arguments from call sites to method entries. */
 public class ArgumentPropagator {
@@ -139,8 +141,10 @@
 
     // Using the computed optimization info, build a graph lens that describes the mapping from
     // methods with constant parameters to methods with the constant parameters removed.
+    Set<DexProgramClass> affectedClasses = Sets.newConcurrentHashSet();
     ArgumentPropagatorGraphLens graphLens =
-        optimizeMethodParameters(stronglyConnectedProgramComponents, executorService);
+        optimizeMethodParameters(
+            stronglyConnectedProgramComponents, affectedClasses::add, executorService);
 
     // Find all the code objects that need reprocessing.
     new ArgumentPropagatorMethodReprocessingEnqueuer(appView)
@@ -148,7 +152,8 @@
 
     // Finally, apply the graph lens to the program (i.e., remove constant parameters from method
     // definitions).
-    new ArgumentPropagatorApplicationFixer(appView, graphLens).fixupApplication(executorService);
+    new ArgumentPropagatorApplicationFixer(appView, graphLens)
+        .fixupApplication(affectedClasses, executorService);
 
     timing.end();
   }
@@ -184,12 +189,15 @@
   /** Called by {@link IRConverter} to optimize method definitions. */
   private ArgumentPropagatorGraphLens optimizeMethodParameters(
       List<Set<DexProgramClass>> stronglyConnectedProgramComponents,
+      Consumer<DexProgramClass> affectedClassConsumer,
       ExecutorService executorService)
       throws ExecutionException {
     Collection<ArgumentPropagatorGraphLens.Builder> partialGraphLensBuilders =
         ThreadUtils.processItemsWithResults(
             stronglyConnectedProgramComponents,
-            classes -> new ArgumentPropagatorProgramOptimizer(appView).optimize(classes),
+            classes ->
+                new ArgumentPropagatorProgramOptimizer(appView)
+                    .optimize(classes, affectedClassConsumer),
             executorService);
 
     // Merge all the partial, disjoint graph lens builders into a single graph lens.
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorApplicationFixer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorApplicationFixer.java
index 346e3fc..71e4970 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorApplicationFixer.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorApplicationFixer.java
@@ -8,8 +8,14 @@
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.MethodCollection;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
+import com.android.tools.r8.ir.optimize.enums.classification.EnumUnboxerMethodClassification;
+import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
+import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.ThreadUtils;
+import java.util.Set;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 
@@ -28,15 +34,19 @@
     this.graphLens = graphLens;
   }
 
-  public void fixupApplication(ExecutorService executorService) throws ExecutionException {
+  public void fixupApplication(
+      Set<DexProgramClass> affectedClasses, ExecutorService executorService)
+      throws ExecutionException {
     // If the graph lens is null, argument propagation did not lead to any parameter removals. In
     // this case there is no needed to fixup the program.
     if (graphLens == null) {
+      assert affectedClasses.isEmpty();
       return;
     }
 
-    // TODO(b/190154391): Do not naively visit all classes, when only few require changes.
-    ThreadUtils.processItems(appView.appInfo().classes(), this::fixupClass, executorService);
+    assert !affectedClasses.isEmpty();
+
+    ThreadUtils.processItems(affectedClasses, this::fixupClass, executorService);
     appView.setGraphLens(graphLens);
   }
 
@@ -55,6 +65,25 @@
               methodReferenceAfterParameterRemoval,
               builder -> {
                 // TODO(b/190154391): fixup parameter annotations, if any.
+                ArgumentInfoCollection removedParameters =
+                    graphLens.getRemovedParameters(methodReferenceAfterParameterRemoval);
+                builder.modifyOptimizationInfo(
+                    (newMethod, optimizationInfo) -> {
+                      OptimizationFeedback feedback = OptimizationFeedbackSimple.getInstance();
+                      ProgramMethod programMethod = new ProgramMethod(clazz, newMethod);
+                      // TODO(b/190154391): test this.
+                      EnumUnboxerMethodClassification rewrittenEnumUnboxerMethodClassification =
+                          optimizationInfo
+                              .getEnumUnboxerMethodClassification()
+                              .fixupAfterParameterRemoval(removedParameters);
+                      if (rewrittenEnumUnboxerMethodClassification.isCheckNotNullClassification()) {
+                        feedback.setEnumUnboxerMethodClassification(
+                            programMethod, rewrittenEnumUnboxerMethodClassification);
+                      } else {
+                        // Bypass monotonicity check.
+                        feedback.unsetEnumUnboxerMethodClassification(programMethod);
+                      }
+                    });
               });
         });
   }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java
index c60640c..a00c016 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java
@@ -32,6 +32,11 @@
     return new Builder(appView);
   }
 
+  public ArgumentInfoCollection getRemovedParameters(DexMethod method) {
+    assert method != internalGetPreviousMethodSignature(method);
+    return removedParameters.getOrDefault(method, ArgumentInfoCollection.empty());
+  }
+
   @Override
   protected RewrittenPrototypeDescription internalDescribePrototypeChanges(
       RewrittenPrototypeDescription prototypeChanges, DexMethod method) {
@@ -40,8 +45,7 @@
       assert !removedParameters.containsKey(method);
       return prototypeChanges;
     }
-    return prototypeChanges.withRemovedArguments(
-        removedParameters.getOrDefault(method, ArgumentInfoCollection.empty()));
+    return prototypeChanges.withRemovedArguments(getRemovedParameters(method));
   }
 
   @Override
@@ -79,13 +83,10 @@
 
     public Builder recordMove(
         DexMethod from, DexMethod to, ArgumentInfoCollection removedParametersForMethod) {
-      if (from != to) {
-        newMethodSignatures.put(from, to);
-        if (!removedParametersForMethod.isEmpty()) {
-          removedParameters.put(to, removedParametersForMethod);
-        }
-      } else {
-        assert removedParametersForMethod.isEmpty();
+      assert from != to;
+      newMethodSignatures.put(from, to);
+      if (!removedParametersForMethod.isEmpty()) {
+        removedParameters.put(to, removedParametersForMethod);
       }
       return this;
     }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java
index 3c732df..37adcfa 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java
@@ -13,7 +13,7 @@
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
 import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.graph.UseRegistryWithResult;
 import com.android.tools.r8.ir.conversion.IRConverter;
 import com.android.tools.r8.ir.conversion.PostMethodProcessor;
 import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo;
@@ -93,8 +93,7 @@
                   method -> {
                     AffectedMethodUseRegistry registry =
                         new AffectedMethodUseRegistry(appView, graphLens);
-                    method.registerCodeReferences(registry);
-                    if (registry.isAffected()) {
+                    if (method.registerCodeReferencesWithResult(registry)) {
                       methodsToReprocessInClass.add(method);
                     }
                   });
@@ -107,24 +106,20 @@
             methodsToReprocessBuilder.addAll(methodsToReprocessForClass, currentGraphLens));
   }
 
-  static class AffectedMethodUseRegistry extends UseRegistry {
+  static class AffectedMethodUseRegistry extends UseRegistryWithResult<Boolean> {
 
     private final AppView<AppInfoWithLiveness> appView;
     private final ArgumentPropagatorGraphLens graphLens;
 
-    // Set to true if the given piece of code resolves to a method that needs rewriting according to
-    // the graph lens.
-    private boolean affected;
-
     AffectedMethodUseRegistry(
         AppView<AppInfoWithLiveness> appView, ArgumentPropagatorGraphLens graphLens) {
-      super(appView.dexItemFactory());
+      super(appView.dexItemFactory(), false);
       this.appView = appView;
       this.graphLens = graphLens;
     }
 
-    boolean isAffected() {
-      return affected;
+    private void markAffected() {
+      setResult(Boolean.TRUE);
     }
 
     @Override
@@ -163,8 +158,7 @@
       DexMethod rewrittenMethodReference =
           graphLens.internalGetNextMethodSignature(resolvedMethod.getReference());
       if (rewrittenMethodReference != resolvedMethod.getReference()) {
-        affected = true;
-        // TODO(b/150583533): break/abort!
+        markAffected();
       }
     }
 
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 b10ff8d..f469ddc 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
@@ -19,6 +19,7 @@
 import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo;
 import com.android.tools.r8.ir.optimize.info.ConcreteCallSiteOptimizationInfo;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.BooleanBox;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.Pair;
 import com.android.tools.r8.utils.collections.DexMethodSignatureSet;
@@ -31,6 +32,7 @@
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
+import java.util.function.Consumer;
 import java.util.function.IntPredicate;
 
 public class ArgumentPropagatorProgramOptimizer {
@@ -68,7 +70,8 @@
   //  also enqueue the caller's callers for reprocessing. This would propagate the throwing
   //  information to all call sites.
   public ArgumentPropagatorGraphLens.Builder optimize(
-      Set<DexProgramClass> stronglyConnectedProgramClasses) {
+      Set<DexProgramClass> stronglyConnectedProgramClasses,
+      Consumer<DexProgramClass> affectedClassConsumer) {
     // First reserve pinned method signatures.
     reservePinnedMethodSignatures(stronglyConnectedProgramClasses);
 
@@ -83,7 +86,9 @@
     ArgumentPropagatorGraphLens.Builder partialGraphLensBuilder =
         ArgumentPropagatorGraphLens.builder(appView);
     for (DexProgramClass clazz : stronglyConnectedProgramClasses) {
-      visitClass(clazz, partialGraphLensBuilder);
+      if (visitClass(clazz, partialGraphLensBuilder)) {
+        affectedClassConsumer.accept(clazz);
+      }
     }
     return partialGraphLensBuilder;
   }
@@ -200,8 +205,10 @@
     return true;
   }
 
-  private void visitClass(
+  // Returns true if the class was changed as a result of argument propagation.
+  private boolean visitClass(
       DexProgramClass clazz, ArgumentPropagatorGraphLens.Builder partialGraphLensBuilder) {
+    BooleanBox affected = new BooleanBox();
     clazz.forEachProgramMethod(
         method -> {
           ArgumentInfoCollection removableParameters =
@@ -209,9 +216,13 @@
                   ? computeRemovableParametersFromDirectMethod(method)
                   : computeRemovableParametersFromVirtualMethod(method);
           DexMethod newMethodSignature = getNewMethodSignature(method, removableParameters);
-          partialGraphLensBuilder.recordMove(
-              method.getReference(), newMethodSignature, removableParameters);
+          if (newMethodSignature != method.getReference()) {
+            partialGraphLensBuilder.recordMove(
+                method.getReference(), newMethodSignature, removableParameters);
+            affected.set();
+          }
         });
+    return affected.get();
   }
 
   private DexMethod getNewMethodSignature(
diff --git a/src/main/java/com/android/tools/r8/shaking/AbstractMethodRemover.java b/src/main/java/com/android/tools/r8/shaking/AbstractMethodRemover.java
index 4123c10..e0c7f83 100644
--- a/src/main/java/com/android/tools/r8/shaking/AbstractMethodRemover.java
+++ b/src/main/java/com/android/tools/r8/shaking/AbstractMethodRemover.java
@@ -11,7 +11,7 @@
 import com.android.tools.r8.logging.Log;
 import com.android.tools.r8.shaking.ScopedDexMethodSet.AddMethodIfMoreVisibleResult;
 import com.android.tools.r8.utils.IterableUtils;
-import java.util.ArrayList;
+import com.android.tools.r8.utils.ListUtils;
 import java.util.List;
 
 /**
@@ -58,32 +58,31 @@
     if (virtualMethods == null) {
       return null;
     }
-    // Removal of abstract methods is rare, so avoid copying the array until we find one.
-    List<DexEncodedMethod> methods = null;
-    for (int i = 0; i < virtualMethods.size(); i++) {
-      DexEncodedMethod method = virtualMethods.get(i);
-      if (scope.addMethodIfMoreVisible(method) != AddMethodIfMoreVisibleResult.NOT_ADDED
-          || !method.accessFlags.isAbstract()
-          || appView.appInfo().isPinned(method.getReference())) {
-        if (methods != null) {
-          methods.add(method);
-        }
-      } else {
-        if (methods == null) {
-          methods = new ArrayList<>(virtualMethods.size() - 1);
-          for (int j = 0; j < i; j++) {
-            methods.add(virtualMethods.get(j));
-          }
-        }
-        if (Log.ENABLED) {
-          Log.debug(getClass(), "Removing abstract method %s.", method.getReference());
-        }
-      }
+    // Removal of abstract methods is rare, ListUtils.filterOrElse does no copying if nothing is
+    // filtered out.
+    List<DexEncodedMethod> filteredMethods =
+        ListUtils.filterOrElse(virtualMethods, this::isNonAbstractPinnedOrWideningVisibility);
+    return filteredMethods == virtualMethods
+        ? null
+        : filteredMethods.toArray(DexEncodedMethod.EMPTY_ARRAY);
+  }
+
+  private boolean isNonAbstractPinnedOrWideningVisibility(DexEncodedMethod method) {
+    if (!method.accessFlags.isAbstract()) {
+      return true;
     }
-    if (methods != null) {
-      return methods.toArray(DexEncodedMethod.EMPTY_ARRAY);
+    // Check if the method widens visibility. Adding to the scope mutates it.
+    if (scope.addMethodIfMoreVisible(method) != AddMethodIfMoreVisibleResult.NOT_ADDED) {
+      return true;
     }
-    return null;
+    if (appView.appInfo().isPinned(method.getReference())) {
+      return true;
+    }
+    // We will filter the method out since it is not pinned.
+    if (Log.ENABLED) {
+      Log.debug(getClass(), "Removing abstract method %s.", method.getReference());
+    }
+    return false;
   }
 
 }
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index 55278ec..acc8410 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -933,8 +933,7 @@
     if (!options().enableValuePropagation || neverPropagateValue.contains(method)) {
       return false;
     }
-    if (!method.getReturnType().isAlwaysNull(this)
-        && !getKeepInfo().getMethodInfo(method, this).isInliningAllowed(options())) {
+    if (isPinned(method) && !method.getReturnType().isAlwaysNull(this)) {
       return false;
     }
     return true;
@@ -1316,11 +1315,7 @@
       if (refinedReceiverClass.isProgramClass()) {
         DexClassAndMethod clazzAndMethod =
             resolution.lookupVirtualDispatchTarget(refinedReceiverClass.asProgramClass(), this);
-        if (clazzAndMethod == null
-            || (clazzAndMethod.isProgramMethod()
-                && !getKeepInfo()
-                    .getMethodInfo(clazzAndMethod.asProgramMethod())
-                    .isOptimizationAllowed(options()))) {
+        if (clazzAndMethod == null || isPinned(clazzAndMethod.getDefinition().getReference())) {
           // TODO(b/150640456): We should maybe only consider program methods.
           return DexEncodedMethod.SENTINEL;
         }
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java
index 79fac0a..dd4226c 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java
@@ -54,10 +54,6 @@
     return this.equals(bottom());
   }
 
-  public boolean isInliningAllowed(GlobalKeepInfoConfiguration configuration) {
-    return isOptimizationAllowed(configuration);
-  }
-
   public static class Builder extends KeepInfo.Builder<Builder, KeepMethodInfo> {
 
     private Builder() {
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index 6a89e9b..6e06948 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -2186,6 +2186,7 @@
     @Override
     public Consumer<UseRegistry> getRegistryCallback() {
       return registry -> {
+        assert registry.getTraversalContinuation().shouldContinue();
         switch (type) {
           case DIRECT:
             registry.registerInvokeDirect(invocationTarget);
diff --git a/src/main/java/com/android/tools/r8/utils/IntObjToObjFunction.java b/src/main/java/com/android/tools/r8/utils/IntObjToObjFunction.java
index 9c07bed..b0ed4ee 100644
--- a/src/main/java/com/android/tools/r8/utils/IntObjToObjFunction.java
+++ b/src/main/java/com/android/tools/r8/utils/IntObjToObjFunction.java
@@ -6,5 +6,5 @@
 
 public interface IntObjToObjFunction<S, T> {
 
-  S apply(int i, T obj);
+  T apply(int i, S obj);
 }
diff --git a/src/main/java/com/android/tools/r8/utils/IteratorUtils.java b/src/main/java/com/android/tools/r8/utils/IteratorUtils.java
index 9420440..a34ec96 100644
--- a/src/main/java/com/android/tools/r8/utils/IteratorUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/IteratorUtils.java
@@ -165,6 +165,16 @@
     return !anyRemainingMatch(iterator, remaining -> !predicate.test(remaining));
   }
 
+  public static <T> boolean allRemainingMatchDestructive(
+      Iterator<T> iterator, Predicate<T> predicate) {
+    while (iterator.hasNext()) {
+      if (!predicate.test(iterator.next())) {
+        return false;
+      }
+    }
+    return true;
+  }
+
   public static <T> boolean anyRemainingMatch(ListIterator<T> iterator, Predicate<T> predicate) {
     T state = peekNext(iterator);
     boolean result = false;
diff --git a/src/main/java/com/android/tools/r8/utils/ListUtils.java b/src/main/java/com/android/tools/r8/utils/ListUtils.java
index 891f1c2..8fd84fc 100644
--- a/src/main/java/com/android/tools/r8/utils/ListUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ListUtils.java
@@ -159,6 +159,14 @@
     return mapOrElse(list, fn, list);
   }
 
+  /**
+   * Takes elements from the input list depending on the predicate being true. Returns the filtered
+   * list otherwise returns the original list of none were removed.
+   */
+  public static <T> List<T> filterOrElse(List<T> list, Predicate<T> predicate) {
+    return mapOrElse(list, element -> predicate.test(element) ? element : null, list);
+  }
+
   public static <T> ArrayList<T> newArrayList(T element) {
     ArrayList<T> list = new ArrayList<>(1);
     list.add(element);
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/KeepAbstractMethodShadowingTest.java b/src/test/java/com/android/tools/r8/bridgeremoval/KeepAbstractMethodShadowingTest.java
new file mode 100644
index 0000000..04cacdf
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/KeepAbstractMethodShadowingTest.java
@@ -0,0 +1,98 @@
+// 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.bridgeremoval;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.transformers.ClassFileTransformer.MethodPredicate;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class KeepAbstractMethodShadowingTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  public KeepAbstractMethodShadowingTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testJvm() throws Exception {
+    Assume.assumeTrue(parameters.isCfRuntime());
+    testForJvm()
+        .addProgramClasses(A.class, C.class, Main.class)
+        .addProgramClassFileData(getBWithAbstractFoo())
+        .run(parameters.getRuntime(), Main.class)
+        .assertFailureWithErrorThatThrows(AbstractMethodError.class);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClasses(A.class, C.class, Main.class)
+        .addProgramClassFileData(getBWithAbstractFoo())
+        .setMinApi(parameters.getApiLevel())
+        .addKeepMainRule(Main.class)
+        .addKeepClassAndMembersRules(A.class)
+        .run(parameters.getRuntime(), Main.class)
+        .assertFailureWithErrorThatThrows(AbstractMethodError.class)
+        .inspectFailure(
+            inspector -> {
+              ClassSubject clazz = inspector.clazz(B.class);
+              assertThat(clazz, isPresent());
+              MethodSubject foo = clazz.uniqueMethodWithName("foo");
+              assertThat(foo, isPresent());
+            });
+  }
+
+  private byte[] getBWithAbstractFoo() throws Exception {
+    return transformer(B.class)
+        .renameMethod(MethodPredicate.onName("will_be_foo"), "foo")
+        .transform();
+  }
+
+  public static class A {
+    public void foo() {
+      System.out.println("Hello World");
+    }
+  }
+
+  public abstract static class B extends A {
+    public abstract void /* foo */ will_be_foo();
+  }
+
+  public static class C extends B {
+
+    @Override
+    public void foo() {
+      super.foo();
+    }
+
+    @Override
+    public void will_be_foo() {}
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) {
+      new C().foo();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaInStacktraceTest.java b/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaInStacktraceTest.java
index 31f87d9..640796d 100644
--- a/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaInStacktraceTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaInStacktraceTest.java
@@ -102,7 +102,6 @@
             .addKeepAttributeSourceFile()
             .addKeepRules("-renamesourcefileattribute SourceFile")
             .noTreeShaking()
-            .addDontOptimize()
             .run(parameters.getRuntime(), TestRunner.class, Boolean.toString(isDalvik))
             .assertSuccess()
             .getStdOut();
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InliningOptimize.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InliningOptimize.java
deleted file mode 100644
index 0c0bac6..0000000
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InliningOptimize.java
+++ /dev/null
@@ -1,47 +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.inliner;
-
-import static org.junit.Assert.assertFalse;
-
-import com.android.tools.r8.TestBase;
-import com.android.tools.r8.utils.codeinspector.InstructionSubject;
-import org.junit.Test;
-
-class Foobar {
-  public static void main(String[] args) {
-    System.out.println("Value: " + (new Bar()).returnPlusConstant(42));
-  }
-}
-
-class Bar {
-  public int returnPlusConstant(int value) {
-    return value + 42;
-  }
-}
-
-public class InliningOptimize extends TestBase {
-
-  @Test
-  public void test() throws Exception {
-    testForR8(Backend.DEX)
-        .addProgramClasses(Bar.class, Foobar.class)
-        .addKeepRules("-keep,allowoptimization class ** {\n" + "*;\n" + "}")
-        .compile()
-        .inspect(
-            inspector -> {
-              inspector
-                  .clazz(Foobar.class)
-                  .mainMethod()
-                  .iterateInstructions(InstructionSubject::isInvoke)
-                  .forEachRemaining(
-                      invoke -> {
-                        assertFalse(
-                            invoke.getMethod().name.toString().contains("returnPlusConstant"));
-                      });
-            })
-        .run(Foobar.class)
-        .assertSuccessWithOutputLines("Value: 84");
-  }
-}
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
index c6182b5..be4f6de 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -51,6 +51,7 @@
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.code.CatchHandlers;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.Phi.RegisterReadType;
@@ -852,7 +853,12 @@
                 DexString.EMPTY_ARRAY);
         Code code =
             new SynthesizedCode(
-                (ignored, callerPosition) -> new ReturnVoidCode(voidReturnMethod, callerPosition));
+                (ignored, callerPosition) -> new ReturnVoidCode(voidReturnMethod, callerPosition)) {
+              @Override
+              public Consumer<UseRegistry> getRegistryCallback() {
+                throw new Unreachable();
+              }
+            };
         DexEncodedMethod method =
             DexEncodedMethod.builder()
                 .setMethod(voidReturnMethod)
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
index 42cd647..bcea2fe 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
@@ -391,7 +391,6 @@
         .setMinApi(minSdk)
         .noMinification()
         .noTreeShaking()
-        .addDontOptimize()
         .setMainDexListConsumer(ToolHelper.consumeString(r8MainDexListOutput::set))
         .allowDiagnosticMessages()
         .compileWithExpectedDiagnostics(
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
new file mode 100644
index 0000000..ad4c08a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/MixedArgumentRemovalAndEnumUnboxingTest.java
@@ -0,0 +1,99 @@
+// 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 static org.junit.Assert.assertTrue;
+
+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.InstructionSubject;
+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 MixedArgumentRemovalAndEnumUnboxingTest 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)
+        .addOptionsModification(
+            options ->
+                options
+                    .callSiteOptimizationOptions()
+                    .setEnableExperimentalArgumentPropagation(true))
+        .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 mainMethodSubject = mainClassSubject.mainMethod();
+              assertThat(mainMethodSubject, isPresent());
+              assertTrue(
+                  mainMethodSubject
+                      .streamInstructions()
+                      .noneMatch(InstructionSubject::isConstNull));
+
+              MethodSubject testMethodSubject = mainClassSubject.uniqueMethodWithName("test");
+              assertThat(testMethodSubject, isPresent());
+              assertEquals(2, testMethodSubject.getProgramMethod().getReference().getArity());
+              assertEquals(
+                  "int", testMethodSubject.getProgramMethod().getParameter(0).getTypeName());
+              assertEquals(
+                  "int", testMethodSubject.getProgramMethod().getParameter(1).getTypeName());
+              assertTrue(
+                  testMethodSubject.streamInstructions().noneMatch(InstructionSubject::isIf));
+            })
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("A", "B");
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      MyEnum alwaysA = System.currentTimeMillis() >= 1 ? MyEnum.A : MyEnum.B;
+      MyEnum alwaysB = System.currentTimeMillis() >= 1 ? MyEnum.B : MyEnum.A;
+      test(null, alwaysA, null, alwaysB);
+    }
+
+    @NeverInline
+    static void test(Object alwaysNull, MyEnum alwaysA, Object alsoAlwaysNull, MyEnum alwaysB) {
+      if (alwaysNull == null && alsoAlwaysNull == null) {
+        System.out.println(alwaysA.name());
+        System.out.println(alwaysB.name());
+      }
+    }
+  }
+
+  enum MyEnum {
+    A,
+    B
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceLambdaTest.java b/src/test/java/com/android/tools/r8/retrace/RetraceLambdaTest.java
index d68447d..252dcc5 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceLambdaTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceLambdaTest.java
@@ -105,7 +105,6 @@
         .addKeepMainRule(Main.class)
         .addKeepPackageNamesRule(getClass().getPackage())
         .noTreeShaking()
-        .addDontOptimize()
         .addKeepAttributeSourceFile()
         .addKeepAttributeLineNumberTable()
         .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/smali/OutlineTest.java b/src/test/java/com/android/tools/r8/smali/OutlineTest.java
index c9d30fd..21501d0 100644
--- a/src/test/java/com/android/tools/r8/smali/OutlineTest.java
+++ b/src/test/java/com/android/tools/r8/smali/OutlineTest.java
@@ -85,8 +85,6 @@
     return options -> {
       // Disable inlining to make sure that code looks as expected.
       options.enableInlining = false;
-      // Disable the devirtulizer to not remove the super calls
-      options.enableDevirtualization = false;
       // Disable string concatenation optimization to not bother outlining of StringBuilder usage.
       options.enableStringConcatenationOptimization = false;
       // Also apply outline options.