Merge commit 'c471bd7f6f7b3d20d030df56df1fe84a237d82f4' into dev-release
diff --git a/src/main/java/com/android/tools/r8/graph/DexApplication.java b/src/main/java/com/android/tools/r8/graph/DexApplication.java
index d0a336a..67f8be4 100644
--- a/src/main/java/com/android/tools/r8/graph/DexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/DexApplication.java
@@ -131,6 +131,7 @@
     return flags;
   }
 
+  @Override
   public abstract DexClass definitionFor(DexType type);
 
   public abstract DexProgramClass programDefinitionFor(DexType type);
diff --git a/src/main/java/com/android/tools/r8/graph/DexDefinitionSupplier.java b/src/main/java/com/android/tools/r8/graph/DexDefinitionSupplier.java
index d18db79..57212d4 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDefinitionSupplier.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDefinitionSupplier.java
@@ -76,6 +76,11 @@
   @Deprecated
   DexClass definitionFor(DexType type);
 
+  default DexClassAndMethod definitionFor(DexMethod method) {
+    DexClass holder = definitionFor(method.getHolderType());
+    return holder != null ? holder.lookupClassMethod(method) : null;
+  }
+
   // Use programDefinitionFor with a context.
   @Deprecated
   default DexProgramClass definitionForProgramType(DexType type) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethod.java b/src/main/java/com/android/tools/r8/graph/DexMethod.java
index 4f23975..0cd5a14 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethod.java
@@ -145,7 +145,7 @@
 
   @Override
   public String toString() {
-    return "Method " + holder + "." + name + " " + proto.toString();
+    return toSourceString();
   }
 
   public MethodReference asMethodReference() {
diff --git a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
index 6d3e71c..e69e74b 100644
--- a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
+++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -330,7 +330,11 @@
 
     @Override
     public void visitPermittedSubclass(String permittedSubclass) {
-      throw new CompilationError("Sealed classes are not supported", origin);
+      if (classKind == ClassKind.PROGRAM) {
+        throw new CompilationError("Sealed classes are not supported as program classes", origin);
+      }
+      // For library and classpath just ignore the permitted subclasses, as the compiler is not
+      // validating the code with respect to sealed classes.
     }
 
     @Override
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 0a60b01..dda7746 100644
--- a/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java
+++ b/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java
@@ -204,21 +204,28 @@
 
   public static class RewrittenTypeInfo extends ArgumentInfo {
 
+    private final DexType castType;
     private final DexType oldType;
     private final DexType newType;
     private final SingleValue singleValue;
 
+    public static Builder builder() {
+      return new Builder();
+    }
+
     public static RewrittenTypeInfo toVoid(
         DexType oldReturnType, DexItemFactory dexItemFactory, SingleValue singleValue) {
       assert singleValue != null;
-      return new RewrittenTypeInfo(oldReturnType, dexItemFactory.voidType, singleValue);
+      return new RewrittenTypeInfo(oldReturnType, dexItemFactory.voidType, null, singleValue);
     }
 
     public RewrittenTypeInfo(DexType oldType, DexType newType) {
-      this(oldType, newType, null);
+      this(oldType, newType, null, null);
     }
 
-    public RewrittenTypeInfo(DexType oldType, DexType newType, SingleValue singleValue) {
+    public RewrittenTypeInfo(
+        DexType oldType, DexType newType, DexType castType, SingleValue singleValue) {
+      this.castType = castType;
       this.oldType = oldType;
       this.newType = newType;
       this.singleValue = singleValue;
@@ -229,6 +236,10 @@
       return other.hasRewrittenReturnInfo() ? combine(other.getRewrittenReturnInfo()) : this;
     }
 
+    public DexType getCastType() {
+      return castType;
+    }
+
     public DexType getNewType() {
       return newType;
     }
@@ -245,6 +256,10 @@
       return newType.isVoidType();
     }
 
+    public boolean hasCastType() {
+      return castType != null;
+    }
+
     public boolean hasSingleValue() {
       return singleValue != null;
     }
@@ -271,18 +286,23 @@
     public RewrittenTypeInfo combine(RewrittenTypeInfo other) {
       assert !getNewType().isVoidType();
       assert getNewType() == other.getOldType();
-      return new RewrittenTypeInfo(getOldType(), other.getNewType(), other.getSingleValue());
+      return new RewrittenTypeInfo(
+          getOldType(), other.getNewType(), getCastType(), other.getSingleValue());
     }
 
     @Override
     public RewrittenTypeInfo rewrittenWithLens(
         AppView<AppInfoWithLiveness> appView, GraphLens graphLens) {
+      DexType rewrittenCastType = castType != null ? graphLens.lookupType(castType) : null;
       DexType rewrittenNewType = graphLens.lookupType(newType);
       SingleValue rewrittenSingleValue =
           hasSingleValue() ? getSingleValue().rewrittenWithLens(appView, graphLens) : null;
-      if (rewrittenNewType != newType || rewrittenSingleValue != singleValue) {
+      if (rewrittenCastType != castType
+          || rewrittenNewType != newType
+          || rewrittenSingleValue != singleValue) {
         // The old type is intentionally not rewritten.
-        return new RewrittenTypeInfo(oldType, rewrittenNewType, rewrittenSingleValue);
+        return new RewrittenTypeInfo(
+            oldType, rewrittenNewType, rewrittenCastType, rewrittenSingleValue);
       }
       return this;
     }
@@ -308,6 +328,45 @@
       assert newType.toBaseType(dexItemFactory).isIntType();
       return true;
     }
+
+    public static class Builder {
+
+      private DexType castType;
+      private DexType oldType;
+      private DexType newType;
+      private SingleValue singleValue;
+
+      public Builder applyIf(boolean condition, Consumer<Builder> consumer) {
+        if (condition) {
+          consumer.accept(this);
+        }
+        return this;
+      }
+
+      public Builder setCastType(DexType castType) {
+        this.castType = castType;
+        return this;
+      }
+
+      public Builder setOldType(DexType oldType) {
+        this.oldType = oldType;
+        return this;
+      }
+
+      public Builder setNewType(DexType newType) {
+        this.newType = newType;
+        return this;
+      }
+
+      public Builder setSingleValue(SingleValue singleValue) {
+        this.singleValue = singleValue;
+        return this;
+      }
+
+      public RewrittenTypeInfo build() {
+        return new RewrittenTypeInfo(oldType, newType, castType, singleValue);
+      }
+    }
   }
 
   public static class ArgumentInfoCollection {
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassInstanceFieldsMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassInstanceFieldsMerger.java
index a8b8d30..682da64 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassInstanceFieldsMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassInstanceFieldsMerger.java
@@ -8,6 +8,8 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexTypeUtils;
 import com.android.tools.r8.horizontalclassmerging.HorizontalClassMergerGraphLens.Builder;
 import com.android.tools.r8.horizontalclassmerging.policies.SameInstanceFields.InstanceFieldInfo;
 import com.android.tools.r8.utils.IterableUtils;
@@ -149,12 +151,15 @@
 
     DexEncodedField newField;
     if (needsRelaxedType(targetField, sourceFields)) {
+      DexType newFieldType =
+          DexTypeUtils.computeLeastUpperBound(
+              appView,
+              Iterables.transform(
+                  Iterables.concat(IterableUtils.singleton(targetField), sourceFields),
+                  DexEncodedField::getType));
       newField =
           targetField.toTypeSubstitutedField(
-              appView,
-              targetField
-                  .getReference()
-                  .withType(appView.dexItemFactory().objectType, appView.dexItemFactory()));
+              appView, targetField.getReference().withType(newFieldType, appView.dexItemFactory()));
     } else {
       newField = targetField;
     }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
index 202f78c..b14f07c 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
@@ -126,7 +126,7 @@
     KeepInfoCollection keepInfo = appView.getKeepInfo();
     keepInfo.mutate(
         mutator ->
-            mutator.removeKeepInfoForPrunedItems(
+            mutator.removeKeepInfoForMergedClasses(
                 PrunedItems.builder().setRemovedClasses(mergedClasses.getSources()).build()));
 
     // Must rewrite AppInfoWithLiveness before pruning the merged classes, to ensure that allocation
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoIllegalInlining.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoIllegalInlining.java
index 6e88f4d..0c19d53 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoIllegalInlining.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoIllegalInlining.java
@@ -29,7 +29,7 @@
   private boolean disallowInlining(ProgramMethod method) {
     Code code = method.getDefinition().getCode();
 
-    if (appView.appInfo().isNeverInlineMethod(method.getReference())) {
+    if (!appView.getKeepInfo(method).isInliningAllowed(appView.options())) {
       return true;
     }
 
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/inlining/NullSimpleInliningConstraint.java b/src/main/java/com/android/tools/r8/ir/analysis/inlining/NullSimpleInliningConstraint.java
index bb4f505..10af841 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/inlining/NullSimpleInliningConstraint.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/inlining/NullSimpleInliningConstraint.java
@@ -52,12 +52,13 @@
           : NeverSimpleInliningConstraint.getInstance();
     } else if (argumentInfo.isRewrittenTypeInfo()) {
       RewrittenTypeInfo rewrittenTypeInfo = argumentInfo.asRewrittenTypeInfo();
-      // We should only get here as a result of enum unboxing.
-      assert rewrittenTypeInfo.verifyIsDueToUnboxing(appView.dexItemFactory());
-      // Rewrite definitely-null constraints to definitely-zero constraints.
-      return nullability.isDefinitelyNull()
-          ? factory.createEqualToNumberConstraint(getArgumentIndex(), 0)
-          : factory.createNotEqualToNumberConstraint(getArgumentIndex(), 0);
+      if (rewrittenTypeInfo.getNewType().isIntType()) {
+        // Rewrite definitely-null constraints to definitely-zero constraints.
+        return nullability.isDefinitelyNull()
+            ? factory.createEqualToNumberConstraint(getArgumentIndex(), 0)
+            : factory.createNotEqualToNumberConstraint(getArgumentIndex(), 0);
+      }
+      return this;
     }
     return withArgumentIndex(changes.getNewArgumentIndex(getArgumentIndex()), factory);
   }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java
index 7596684..4d9b9a0 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java
@@ -4,38 +4,49 @@
 
 package com.android.tools.r8.ir.analysis.proto;
 
+import static com.android.tools.r8.graph.DexClassAndMethod.asProgramMethodOrNull;
 import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
 import static com.android.tools.r8.ir.analysis.proto.ProtoUtils.getInfoValueFromMessageInfoConstructionInvoke;
 import static com.android.tools.r8.ir.analysis.proto.ProtoUtils.getObjectsValueFromMessageInfoConstructionInvoke;
 import static com.android.tools.r8.ir.analysis.proto.ProtoUtils.setObjectsValueForMessageInfoConstructionInvoke;
+import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
 
+import com.android.tools.r8.graph.AccessControl;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.proto.schema.ProtoMessageInfo;
 import com.android.tools.r8.ir.analysis.proto.schema.ProtoObject;
-import com.android.tools.r8.ir.analysis.type.Nullability;
+import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.code.ArrayPut;
 import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.BasicBlockIterator;
 import com.android.tools.r8.ir.code.ConstString;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.IRCodeUtils;
 import com.android.tools.r8.ir.code.Instruction;
 import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeDirect;
 import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
 import com.android.tools.r8.ir.code.MemberType;
 import com.android.tools.r8.ir.code.NewArrayEmpty;
+import com.android.tools.r8.ir.code.NewInstance;
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.ir.conversion.IRConverter;
 import com.android.tools.r8.ir.conversion.OneTimeMethodProcessor;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.DependentMinimumKeepInfoCollection;
 import com.android.tools.r8.utils.Timing;
 import com.android.tools.r8.utils.collections.ProgramMethodSet;
+import com.google.common.collect.Sets;
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.function.Consumer;
@@ -60,8 +71,8 @@
     // Types.
     this.objectArrayType =
         TypeElement.fromDexType(
-            appView.dexItemFactory().objectArrayType, Nullability.definitelyNotNull(), appView);
-    this.stringType = TypeElement.stringClassType(appView, Nullability.definitelyNotNull());
+            appView.dexItemFactory().objectArrayType, definitelyNotNull(), appView);
+    this.stringType = TypeElement.stringClassType(appView, definitelyNotNull());
   }
 
   public void extendRootSet(DependentMinimumKeepInfoCollection dependentMinimumKeepInfo) {
@@ -81,25 +92,17 @@
             .disallowOptimization();
       }
 
-      ProgramMethod newRepeatedGeneratedExtensionMethod =
-          generatedMessageLiteClass.lookupProgramMethod(
-              references.generatedMessageLiteMethods.newRepeatedGeneratedExtension);
-      if (newRepeatedGeneratedExtensionMethod != null) {
-        dependentMinimumKeepInfo
-            .getOrCreateUnconditionalMinimumKeepInfoFor(
-                newRepeatedGeneratedExtensionMethod.getReference())
-            .disallowOptimization();
-      }
-
-      ProgramMethod newSingularGeneratedExtensionMethod =
-          generatedMessageLiteClass.lookupProgramMethod(
-              references.generatedMessageLiteMethods.newSingularGeneratedExtension);
-      if (newSingularGeneratedExtensionMethod != null) {
-        dependentMinimumKeepInfo
-            .getOrCreateUnconditionalMinimumKeepInfoFor(
-                newSingularGeneratedExtensionMethod.getReference())
-            .disallowOptimization();
-      }
+      references.forEachMethodReference(
+          reference -> {
+            DexProgramClass holder =
+                asProgramClassOrNull(appView.definitionFor(reference.getHolderType()));
+            ProgramMethod method = reference.lookupOnProgramClass(holder);
+            if (method != null) {
+              dependentMinimumKeepInfo
+                  .getOrCreateUnconditionalMinimumKeepInfoFor(method.getReference())
+                  .disallowOptimization();
+            }
+          });
     }
   }
 
@@ -107,9 +110,98 @@
     ProgramMethod method = code.context();
     if (references.isDynamicMethod(method.getReference())) {
       rewriteDynamicMethod(method, code);
+    } else if (appView.hasLiveness()) {
+      optimizeNewMutableInstance(appView.withLiveness(), code);
     }
   }
 
+  private void optimizeNewMutableInstance(AppView<AppInfoWithLiveness> appView, IRCode code) {
+    Set<Value> affectedValues = Sets.newIdentityHashSet();
+    BasicBlockIterator blockIterator = code.listIterator();
+    while (blockIterator.hasNext()) {
+      BasicBlock block = blockIterator.next();
+      InstructionListIterator instructionIterator = block.listIterator(code);
+      while (instructionIterator.hasNext()) {
+        Instruction instruction = instructionIterator.next();
+        DexType newMutableInstanceType = getNewMutableInstanceType(appView, instruction);
+        if (newMutableInstanceType == null) {
+          continue;
+        }
+
+        DexMethod instanceInitializerReference =
+            appView.dexItemFactory().createInstanceInitializer(newMutableInstanceType);
+        ProgramMethod instanceInitializer =
+            asProgramMethodOrNull(appView.definitionFor(instanceInitializerReference));
+        if (instanceInitializer == null
+            || AccessControl.isMemberAccessible(
+                    instanceInitializer, instanceInitializer.getHolder(), code.context(), appView)
+                .isPossiblyFalse()) {
+          continue;
+        }
+
+        NewInstance newInstance =
+            NewInstance.builder()
+                .setType(newMutableInstanceType)
+                .setFreshOutValue(
+                    code, newMutableInstanceType.toTypeElement(appView, definitelyNotNull()))
+                .setPosition(instruction)
+                .build();
+        instructionIterator.replaceCurrentInstruction(newInstance, affectedValues);
+
+        InvokeDirect constructorInvoke =
+            InvokeDirect.builder()
+                .setMethod(instanceInitializerReference)
+                .setSingleArgument(newInstance.outValue())
+                .setPosition(instruction)
+                .build();
+
+        if (block.hasCatchHandlers()) {
+          // Split the block after the new-instance instruction and insert the constructor call in
+          // the split block.
+          BasicBlock splitBlock =
+              instructionIterator.splitCopyCatchHandlers(code, blockIterator, appView.options());
+          instructionIterator = splitBlock.listIterator(code);
+          instructionIterator.add(constructorInvoke);
+          BasicBlock previousBlock =
+              blockIterator.previousUntil(previous -> previous == splitBlock);
+          assert previousBlock != null;
+          blockIterator.next();
+        } else {
+          instructionIterator.add(constructorInvoke);
+        }
+      }
+    }
+    if (!affectedValues.isEmpty()) {
+      new TypeAnalysis(appView).narrowing(affectedValues);
+    }
+  }
+
+  private DexType getNewMutableInstanceType(
+      AppView<AppInfoWithLiveness> appView, Instruction instruction) {
+    if (!instruction.isInvokeMethodWithReceiver()) {
+      return null;
+    }
+    InvokeMethodWithReceiver invoke = instruction.asInvokeMethodWithReceiver();
+    DexMethod invokedMethod = invoke.getInvokedMethod();
+    if (!references.isDynamicMethod(invokedMethod)
+        && !references.isDynamicMethodBridge(invokedMethod)) {
+      return null;
+    }
+    assert invokedMethod.getParameter(0) == references.methodToInvokeType;
+    if (!references.methodToInvokeMembers.isNewMutableInstanceEnum(
+        invoke.getFirstNonReceiverArgument())) {
+      return null;
+    }
+    TypeElement receiverType = invoke.getReceiver().getDynamicUpperBoundType(appView);
+    if (!receiverType.isClassType()) {
+      return null;
+    }
+    DexType rawReceiverType = receiverType.asClassType().getClassType();
+    return appView.appInfo().isStrictSubtypeOf(rawReceiverType, references.generatedMessageLiteType)
+        ? rawReceiverType
+        : null;
+  }
+
   public void postOptimizeDynamicMethods(
       IRConverter converter, ExecutorService executorService, Timing timing)
       throws ExecutionException {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoReferences.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoReferences.java
index 030fe3d..31633de 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoReferences.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoReferences.java
@@ -14,6 +14,7 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.code.Value;
+import java.util.function.Consumer;
 
 public class ProtoReferences {
 
@@ -129,6 +130,17 @@
     methodToInvokeMembers = new MethodToInvokeMembers(factory);
   }
 
+  public void forEachMethodReference(Consumer<DexMethod> consumer) {
+    generatedExtensionMethods.forEachMethodReference(consumer);
+    generatedMessageLiteMethods.forEachMethodReference(consumer);
+    generatedMessageLiteBuilderMethods.forEachMethodReference(consumer);
+    generatedMessageLiteExtendableBuilderMethods.forEachMethodReference(consumer);
+    methodToInvokeMembers.forEachMethodReference(consumer);
+    consumer.accept(dynamicMethod);
+    consumer.accept(newMessageInfoMethod);
+    consumer.accept(rawMessageInfoConstructor);
+  }
+
   public DexField getDefaultInstanceField(DexProgramClass holder) {
     return dexItemFactory.createField(holder.type, holder.type, defaultInstanceFieldName);
   }
@@ -220,6 +232,11 @@
               dexItemFactory.constructorMethodName);
     }
 
+    public void forEachMethodReference(Consumer<DexMethod> consumer) {
+      consumer.accept(constructor);
+      consumer.accept(constructorWithClass);
+    }
+
     public boolean isConstructor(DexMethod method) {
       return method == constructor || method == constructorWithClass;
     }
@@ -230,7 +247,6 @@
     public final DexMethod createBuilderMethod;
     public final DexMethod dynamicMethodBridgeMethod;
     public final DexMethod dynamicMethodBridgeMethodWithObject;
-    public final DexMethod isInitializedMethod;
     public final DexMethod newRepeatedGeneratedExtension;
     public final DexMethod newSingularGeneratedExtension;
 
@@ -251,11 +267,6 @@
               dexItemFactory.createProto(
                   dexItemFactory.objectType, methodToInvokeType, dexItemFactory.objectType),
               "dynamicMethod");
-      isInitializedMethod =
-          dexItemFactory.createMethod(
-              generatedMessageLiteType,
-              dexItemFactory.createProto(dexItemFactory.booleanType),
-              "isInitialized");
       newRepeatedGeneratedExtension =
           dexItemFactory.createMethod(
               generatedMessageLiteType,
@@ -283,25 +294,31 @@
                   dexItemFactory.classType),
               "newSingularGeneratedExtension");
     }
+
+    public void forEachMethodReference(Consumer<DexMethod> consumer) {
+      consumer.accept(createBuilderMethod);
+      consumer.accept(dynamicMethodBridgeMethod);
+      consumer.accept(dynamicMethodBridgeMethodWithObject);
+      consumer.accept(newRepeatedGeneratedExtension);
+      consumer.accept(newSingularGeneratedExtension);
+    }
   }
 
   public class GeneratedMessageLiteBuilderMethods {
 
-    public final DexMethod buildPartialMethod;
     public final DexMethod constructorMethod;
 
     private GeneratedMessageLiteBuilderMethods(DexItemFactory dexItemFactory) {
-      buildPartialMethod =
-          dexItemFactory.createMethod(
-              generatedMessageLiteBuilderType,
-              dexItemFactory.createProto(generatedMessageLiteType),
-              "buildPartial");
       constructorMethod =
           dexItemFactory.createMethod(
               generatedMessageLiteBuilderType,
               dexItemFactory.createProto(dexItemFactory.voidType, generatedMessageLiteType),
               dexItemFactory.constructorMethodName);
     }
+
+    public void forEachMethodReference(Consumer<DexMethod> consumer) {
+      consumer.accept(constructorMethod);
+    }
   }
 
   public class GeneratedMessageLiteExtendableBuilderMethods {
@@ -322,6 +339,11 @@
                   dexItemFactory.voidType, generatedMessageLiteExtendableMessageType),
               dexItemFactory.constructorMethodName);
     }
+
+    public void forEachMethodReference(Consumer<DexMethod> consumer) {
+      consumer.accept(buildPartialMethod);
+      consumer.accept(constructorMethod);
+    }
   }
 
   public class MethodToInvokeMembers {
@@ -355,6 +377,10 @@
               methodToInvokeType, methodToInvokeType, "SET_MEMOIZED_IS_INITIALIZED");
     }
 
+    public void forEachMethodReference(Consumer<DexMethod> consumer) {
+      // Intentionally empty.
+    }
+
     public boolean isNewMutableInstanceEnum(DexField field) {
       return field == newMutableInstanceField;
     }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java
index 65e9670..7e54571 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.analysis.type.InterfaceCollection.Builder;
 import com.android.tools.r8.utils.BooleanBox;
@@ -154,6 +155,16 @@
     return getOrCreateVariant(nullability().meet(nullability));
   }
 
+  public DexType toDexType(DexItemFactory dexItemFactory) {
+    if (type == dexItemFactory.objectType) {
+      DexType singleKnownInterface = getInterfaces().getSingleKnownInterface();
+      if (singleKnownInterface != null) {
+        return singleKnownInterface;
+      }
+    }
+    return type;
+  }
+
   @Override
   public String toString() {
     StringBuilder builder = new StringBuilder();
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeElement.java
index 25cb05d..e97006c 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeElement.java
@@ -236,6 +236,9 @@
     if (this == other) {
       return true;
     }
+    if (isBottom() != other.isBottom()) {
+      return false;
+    }
     if (isPrimitiveType() || other.isPrimitiveType()) {
       return false;
     }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeUtils.java b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeUtils.java
index 02459a7..6ded2e5 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeUtils.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeUtils.java
@@ -4,10 +4,188 @@
 
 package com.android.tools.r8.ir.analysis.type;
 
+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.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_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_PUT;
+
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.code.Assume;
+import com.android.tools.r8.ir.code.InstanceGet;
+import com.android.tools.r8.ir.code.InstancePut;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionOrPhi;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.Phi;
+import com.android.tools.r8.ir.code.StaticPut;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.WorkList;
+import java.util.Objects;
 
 public class TypeUtils {
 
+  private static class UserAndValuePair {
+
+    final InstructionOrPhi user;
+    final Value value;
+
+    UserAndValuePair(InstructionOrPhi user, Value value) {
+      this.user = user;
+      this.value = value;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+      if (this == obj) {
+        return true;
+      }
+      if (obj == null || getClass() != obj.getClass()) {
+        return false;
+      }
+      UserAndValuePair pair = (UserAndValuePair) obj;
+      return user == pair.user && value == pair.value;
+    }
+
+    @Override
+    public int hashCode() {
+      return Objects.hash(user, value);
+    }
+  }
+
+  /**
+   * Returns the "use type" of a given value {@link Value}, i.e., the weakest static type that this
+   * value must have in order for the program to type check.
+   */
+  public static TypeElement computeUseType(
+      AppView<AppInfoWithLiveness> appView, ProgramMethod method, Value value) {
+    TypeElement staticType = value.getType();
+    TypeElement useType = TypeElement.getBottom();
+    WorkList<UserAndValuePair> users = WorkList.newEqualityWorkList();
+    enqueueUsers(value, users);
+    while (users.hasNext()) {
+      UserAndValuePair item = users.next();
+      InstructionOrPhi user = item.user;
+      if (user.isPhi()) {
+        enqueueUsers(user.asPhi(), users);
+      } else {
+        Instruction instruction = user.asInstruction();
+        TypeElement instructionUseType =
+            computeUseTypeForInstruction(appView, method, instruction, item.value, users);
+        useType = useType.join(instructionUseType, appView);
+        if (useType.isTop() || useType.equalUpToNullability(staticType)) {
+          // Bail-out.
+          return staticType;
+        }
+      }
+    }
+    return useType;
+  }
+
+  private static void enqueueUsers(Value value, WorkList<UserAndValuePair> users) {
+    for (Instruction user : value.uniqueUsers()) {
+      users.addIfNotSeen(new UserAndValuePair(user, value));
+    }
+    for (Phi user : value.uniquePhiUsers()) {
+      users.addIfNotSeen(new UserAndValuePair(user, value));
+    }
+  }
+
+  private static TypeElement computeUseTypeForInstruction(
+      AppView<AppInfoWithLiveness> appView,
+      ProgramMethod method,
+      Instruction instruction,
+      Value value,
+      WorkList<UserAndValuePair> users) {
+    switch (instruction.opcode()) {
+      case ASSUME:
+        return computeUseTypeForAssume(instruction.asAssume(), users);
+      case CHECK_CAST:
+      case IF:
+        return TypeElement.getBottom();
+      case INSTANCE_GET:
+        return computeUseTypeForInstanceGet(appView, instruction.asInstanceGet());
+      case INSTANCE_PUT:
+        return computeUseTypeForInstancePut(appView, instruction.asInstancePut(), value);
+      case INVOKE_DIRECT:
+      case INVOKE_INTERFACE:
+      case INVOKE_STATIC:
+      case INVOKE_SUPER:
+      case INVOKE_VIRTUAL:
+        return computeUseTypeForInvoke(appView, instruction.asInvokeMethod(), value);
+      case RETURN:
+        return computeUseTypeForReturn(appView, method);
+      case STATIC_PUT:
+        return computeUseTypeForStaticPut(appView, instruction.asStaticPut());
+      default:
+        // Bail out for unhandled instructions.
+        return TypeElement.getTop();
+    }
+  }
+
+  private static TypeElement computeUseTypeForAssume(
+      Assume assume, WorkList<UserAndValuePair> users) {
+    enqueueUsers(assume.outValue(), users);
+    return TypeElement.getBottom();
+  }
+
+  private static TypeElement computeUseTypeForInstanceGet(
+      AppView<AppInfoWithLiveness> appView, InstanceGet instanceGet) {
+    return instanceGet.getField().getHolderType().toTypeElement(appView);
+  }
+
+  private static TypeElement computeUseTypeForInstancePut(
+      AppView<AppInfoWithLiveness> appView, InstancePut instancePut, Value value) {
+    DexField field = instancePut.getField();
+    TypeElement useType = TypeElement.getBottom();
+    if (instancePut.object() == value) {
+      useType = useType.join(field.getHolderType().toTypeElement(appView), appView);
+    }
+    if (instancePut.value() == value) {
+      useType = useType.join(field.getType().toTypeElement(appView), appView);
+    }
+    return useType;
+  }
+
+  private static TypeElement computeUseTypeForInvoke(
+      AppView<AppInfoWithLiveness> appView, InvokeMethod invoke, Value value) {
+    TypeElement useType = TypeElement.getBottom();
+    for (int argumentIndex = 0; argumentIndex < invoke.arguments().size(); argumentIndex++) {
+      Value argument = invoke.getArgument(argumentIndex);
+      if (argument != value) {
+        continue;
+      }
+      TypeElement useTypeForArgument =
+          invoke
+              .getInvokedMethod()
+              .getArgumentType(argumentIndex, invoke.isInvokeStatic())
+              .toTypeElement(appView);
+      useType = useType.join(useTypeForArgument, appView);
+    }
+    assert !useType.isBottom();
+    return useType;
+  }
+
+  private static TypeElement computeUseTypeForReturn(
+      AppView<AppInfoWithLiveness> appView, ProgramMethod method) {
+    return method.getReturnType().toTypeElement(appView);
+  }
+
+  private static TypeElement computeUseTypeForStaticPut(
+      AppView<AppInfoWithLiveness> appView, StaticPut staticPut) {
+    return staticPut.getField().getType().toTypeElement(appView);
+  }
+
   public static boolean isNullPointerException(TypeElement type, AppView<?> appView) {
     return type.isClassType()
         && type.asClassType().getClassType() == appView.dexItemFactory().npeType;
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlockIterator.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlockIterator.java
index 523ca73..4de1c80 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlockIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlockIterator.java
@@ -6,6 +6,7 @@
 
 import com.android.tools.r8.utils.IteratorUtils;
 import java.util.ListIterator;
+import java.util.function.Predicate;
 
 public class BasicBlockIterator implements ListIterator<BasicBlock> {
 
@@ -63,6 +64,10 @@
     return listIterator.previousIndex();
   }
 
+  public BasicBlock previousUntil(Predicate<BasicBlock> predicate) {
+    return IteratorUtils.previousUntil(this, predicate);
+  }
+
   @Override
   public void add(BasicBlock block) {
     listIterator.add(block);
diff --git a/src/main/java/com/android/tools/r8/ir/code/CheckCast.java b/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
index 764167c..e34ab51 100644
--- a/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
+++ b/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
@@ -232,7 +232,7 @@
       assert outType.equalUpToNullability(castType);
 
       // Check soundness of null information.
-      assert inType.nullability().lessThanOrEqual(outType.nullability());
+      assert inType.nullability() == outType.nullability();
 
       // Since we cannot remove the cast the in-value must be different from null.
       assert !inType.isNullType();
diff --git a/src/main/java/com/android/tools/r8/ir/code/Instruction.java b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
index 3409c01..30f9c5f 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
@@ -796,6 +796,14 @@
     return null;
   }
 
+  public boolean isSafeCheckCast() {
+    return false;
+  }
+
+  public SafeCheckCast asSafeCheckCast() {
+    return null;
+  }
+
   public boolean isConstNumber() {
     return false;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
index efbcd4c..06173d4 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
@@ -71,6 +71,10 @@
     }
   }
 
+  public Value getFirstNonReceiverArgument() {
+    return getArgument(getFirstNonReceiverArgumentIndex());
+  }
+
   public int getFirstNonReceiverArgumentIndex() {
     return BooleanUtils.intValue(isInvokeMethodWithReceiver());
   }
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewInstance.java b/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
index 05fba04..024f6f2 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
@@ -38,6 +38,10 @@
     this.clazz = clazz;
   }
 
+  public static Builder builder() {
+    return new Builder();
+  }
+
   public DexType getType() {
     return clazz;
   }
@@ -228,4 +232,24 @@
     assert type.isDefinitelyNotNull();
     return true;
   }
+
+  public static class Builder extends BuilderBase<Builder, NewInstance> {
+
+    private DexType type;
+
+    public Builder setType(DexType type) {
+      this.type = type;
+      return this;
+    }
+
+    @Override
+    public NewInstance build() {
+      return amend(new NewInstance(type, outValue));
+    }
+
+    @Override
+    public Builder self() {
+      return this;
+    }
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/SafeCheckCast.java b/src/main/java/com/android/tools/r8/ir/code/SafeCheckCast.java
index 8b36c90..72a62cd 100644
--- a/src/main/java/com/android/tools/r8/ir/code/SafeCheckCast.java
+++ b/src/main/java/com/android/tools/r8/ir/code/SafeCheckCast.java
@@ -35,6 +35,16 @@
     return false;
   }
 
+  @Override
+  public boolean isSafeCheckCast() {
+    return true;
+  }
+
+  @Override
+  public SafeCheckCast asSafeCheckCast() {
+    return this;
+  }
+
   public static class Builder extends CheckCast.Builder {
 
     @Override
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 c3fd49f..5f318cd 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
@@ -120,9 +120,7 @@
 import com.android.tools.r8.ir.code.ValueType;
 import com.android.tools.r8.ir.code.ValueTypeConstraint;
 import com.android.tools.r8.ir.code.Xor;
-import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo;
 import com.android.tools.r8.naming.dexitembasedstring.NameComputationInfo;
-import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagatorIROptimizer;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.Pair;
@@ -748,26 +746,6 @@
       new TypeAnalysis(appView).narrowing(ir);
     }
 
-    // Update the IR code if collected call site optimization info has something useful.
-    // While aggregation of parameter information at call sites would be more precise than static
-    // types, those could be still less precise at one single call site, where specific arguments
-    // will be passed during (double) inlining. Instead of adding assumptions and removing invalid
-    // ones, it's better not to insert assumptions for inlinee in the beginning.
-    CallSiteOptimizationInfo callSiteOptimizationInfo =
-        getMethod().getOptimizationInfo().getArgumentInfos();
-    if (callSiteOptimizationInfo.isConcreteCallSiteOptimizationInfo() && method == context) {
-      // TODO(b/190154391): Consider pruning all argument information from the optimization info
-      //  after the second optimization pass. That way we save memory and can assert here that
-      //  !appView.hasLiveness() (which currently may happen due to the reflective behavior
-      //  handling in the final round of tree shaking).
-      if (appView.hasLiveness()) {
-        ArgumentPropagatorIROptimizer.optimize(
-            appView.withLiveness(),
-            ir,
-            callSiteOptimizationInfo.asConcreteCallSiteOptimizationInfo());
-      }
-    }
-
     if (appView.options().isStringSwitchConversionEnabled()) {
       StringSwitchConverter.convertToStringSwitchInstructions(ir, appView.dexItemFactory());
     }
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 327326c..c4dba5e 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
@@ -81,6 +81,7 @@
 import com.android.tools.r8.ir.optimize.classinliner.ClassInliner;
 import com.android.tools.r8.ir.optimize.enums.EnumUnboxer;
 import com.android.tools.r8.ir.optimize.enums.EnumValueOptimizer;
+import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo;
 import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfoCollector;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackDelayed;
@@ -96,8 +97,10 @@
 import com.android.tools.r8.logging.Log;
 import com.android.tools.r8.naming.IdentifierNameStringMarker;
 import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagator;
+import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagatorIROptimizer;
 import com.android.tools.r8.position.MethodPosition;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.shaking.KeepMethodInfo;
 import com.android.tools.r8.shaking.LibraryMethodOverrideAnalysis;
 import com.android.tools.r8.utils.Action;
 import com.android.tools.r8.utils.CfgPrinter;
@@ -1212,6 +1215,20 @@
 
     previous = printMethod(code, "IR after disable assertions (SSA)", previous);
 
+    // Update the IR code if collected call site optimization info has something useful.
+    // While aggregation of parameter information at call sites would be more precise than static
+    // types, those could be still less precise at one single call site, where specific arguments
+    // will be passed during (double) inlining. Instead of adding assumptions and removing invalid
+    // ones, it's better not to insert assumptions for inlinee in the beginning.
+    CallSiteOptimizationInfo callSiteOptimizationInfo =
+        context.getOptimizationInfo().getArgumentInfos();
+    if (callSiteOptimizationInfo.isConcreteCallSiteOptimizationInfo() && appView.hasLiveness()) {
+      ArgumentPropagatorIROptimizer.optimize(
+          appView.withLiveness(),
+          code,
+          callSiteOptimizationInfo.asConcreteCallSiteOptimizationInfo());
+    }
+
     if (assumeInserter != null) {
       assumeInserter.insertAssumeInstructions(code, timing);
     }
@@ -1678,7 +1695,8 @@
         || definition.getOptimizationInfo().isReachabilitySensitive()) {
       return false;
     }
-    if (!appView.getKeepInfo(method).isInliningAllowed(options)) {
+    KeepMethodInfo keepInfo = appView.getKeepInfo(method);
+    if (!keepInfo.isInliningAllowed(options) && !keepInfo.isClassInliningAllowed(options)) {
       return false;
     }
     return true;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
index 5794f8d..f6c957c 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
@@ -6,6 +6,7 @@
 import static com.android.tools.r8.graph.UseRegistry.MethodHandleUse.NOT_ARGUMENT_TO_LAMBDA_METAFACTORY;
 import static com.android.tools.r8.ir.code.Invoke.Type.STATIC;
 import static com.android.tools.r8.ir.code.Invoke.Type.VIRTUAL;
+import static com.android.tools.r8.ir.code.Opcodes.ARGUMENT;
 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;
@@ -33,7 +34,6 @@
 import static com.android.tools.r8.utils.ObjectUtils.getBooleanOrElse;
 
 import com.android.tools.r8.errors.CompilationError;
-import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AccessControl;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
@@ -60,7 +60,7 @@
 import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.analysis.value.SingleNumberValue;
-import com.android.tools.r8.ir.code.Assume;
+import com.android.tools.r8.ir.code.Argument;
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.BasicBlockIterator;
 import com.android.tools.r8.ir.code.CatchHandlers;
@@ -105,7 +105,6 @@
 import java.util.HashSet;
 import java.util.IdentityHashMap;
 import java.util.List;
-import java.util.ListIterator;
 import java.util.Map;
 import java.util.Set;
 import java.util.function.BiFunction;
@@ -256,6 +255,9 @@
               DexMethod actualTarget = lensLookup.getReference();
               Invoke.Type actualInvokeType = lensLookup.getType();
 
+              iterator =
+                  insertCastsForInvokeArgumentsIfNeeded(code, blocks, iterator, invoke, lensLookup);
+
               RewrittenPrototypeDescription prototypeChanges = lensLookup.getPrototypeChanges();
               if (prototypeChanges.requiresRewritingAtCallSite()
                   || invoke.getType() != actualInvokeType
@@ -335,10 +337,22 @@
                   }
                 }
 
-                Value newOutValue =
-                    prototypeChanges.hasBeenChangedToReturnVoid()
-                        ? null
-                        : makeOutValue(invoke, code);
+                Value newOutValue;
+                if (prototypeChanges.hasRewrittenReturnInfo()) {
+                  if (invoke.hasOutValue() && !prototypeChanges.hasBeenChangedToReturnVoid()) {
+                    TypeElement newReturnType =
+                        prototypeChanges
+                            .getRewrittenReturnInfo()
+                            .getNewType()
+                            .toTypeElement(appView);
+                    newOutValue = code.createValue(newReturnType, invoke.getLocalInfo());
+                    affectedPhis.addAll(invoke.outValue().uniquePhiUsers());
+                  } else {
+                    newOutValue = null;
+                  }
+                } else {
+                  newOutValue = makeOutValue(invoke, code);
+                }
 
                 Map<SingleNumberValue, Map<DexType, Value>> parameterMap = new IdentityHashMap<>();
 
@@ -415,19 +429,6 @@
                     iterator.add(constantReturnMaterializingInstruction);
                   }
                 }
-
-                DexType actualReturnType = actualTarget.proto.returnType;
-                DexType expectedReturnType = graphLens.lookupType(invokedMethod.proto.returnType);
-                if (newInvoke.hasOutValue() && actualReturnType != expectedReturnType) {
-                  throw new Unreachable(
-                      "Unexpected need to insert a cast. Possibly related to resolving"
-                          + " b/79143143.\n"
-                          + invokedMethod
-                          + " type changed from "
-                          + expectedReturnType
-                          + " to "
-                          + actualReturnType);
-                }
               }
             }
             break;
@@ -660,6 +661,9 @@
               if (ret.isReturnVoid()) {
                 break;
               }
+
+              insertCastForReturnIfNeeded(code, blocks, iterator, ret);
+
               DexType returnType = code.context().getReturnType();
               Value retValue = ret.returnValue();
               DexType initialType =
@@ -683,49 +687,30 @@
             }
             break;
 
-          case ASSUME:
+          case ARGUMENT:
             {
-              // TODO(b/174543992): It's not clear we should rewrite the assumes here. The code
-              // present fixes the problem for enum unboxing, but not for lambda merging.
-              // The LensCodeRewriter is run before most assume instructions are inserted, however,
-              // the call site optimization may propagate assumptions at IR building time, and such
-              // assumes are already present.
-              // R8 clears the assumes if the type is rewritten to a primitive type.
-              Assume assume = current.asAssume();
-              if (assume.hasOutValue()) {
-                TypeElement type = assume.getOutType();
-                TypeElement substituted = type.rewrittenWithLens(appView, graphLens);
-                if (substituted != type) {
-                  assert type.isArrayType() || type.isClassType();
-                  affectedPhis.addAll(assume.outValue().uniquePhiUsers());
-                  if (substituted.isPrimitiveType()) {
-                    assert type.isClassType();
-                    assert appView.unboxedEnums().isUnboxedEnum(type.asClassType().getClassType());
-                    // Any assumption of a class type being converted to a primitive type is
-                    // invalid. Dynamic type is irrelevant and non null is incorrect.
-                    assume.outValue().replaceUsers(assume.src());
-                    iterator.removeOrReplaceByDebugLocalRead();
-                  } else if (substituted.isPrimitiveArrayType()) {
-                    assert type.isArrayType();
-                    // Non-null assumptions on a class array type being converted to a primitive
-                    // array type remains, but dynamic type becomes irrelevant.
-                    assume.unsetDynamicTypeAssumption();
-                    if (assume.hasNonNullAssumption()) {
-                      assume.outValue().setType(substituted);
-                    } else {
-                      assume.outValue().replaceUsers(assume.src());
-                      iterator.removeOrReplaceByDebugLocalRead();
-                    }
-                  } else {
-                    assert !substituted.isPrimitiveType();
-                    assert !substituted.isPrimitiveArrayType();
-                    current.outValue().setType(substituted);
-                  }
-                }
+              Argument argument = current.asArgument();
+              TypeElement currentArgumentType = argument.getOutType();
+              TypeElement newArgumentType =
+                  method
+                      .getArgumentType(argument.getIndex())
+                      .toTypeElement(appView, currentArgumentType.nullability());
+              if (!newArgumentType.equals(currentArgumentType)) {
+                affectedPhis.addAll(argument.outValue().uniquePhiUsers());
+                iterator.replaceCurrentInstruction(
+                    Argument.builder()
+                        .setIndex(argument.getIndex())
+                        .setFreshOutValue(code, newArgumentType)
+                        .setPosition(argument)
+                        .build());
               }
             }
             break;
 
+          case ASSUME:
+            assert false;
+            break;
+
           default:
             if (current.hasOutValue()) {
               // For all other instructions, substitute any changed type.
@@ -756,7 +741,7 @@
 
   private InstructionListIterator insertCastForFieldAssignmentIfNeeded(
       IRCode code,
-      ListIterator<BasicBlock> blocks,
+      BasicBlockIterator blocks,
       InstructionListIterator iterator,
       FieldPut fieldPut,
       FieldLookupResult lookup) {
@@ -770,15 +755,112 @@
               .setPosition(fieldPut.getPosition())
               .build();
       iterator.add(checkCast);
-      iterator =
-          iterator.splitCopyCatchHandlers(code, blocks, appView.options()).listIterator(code);
       fieldPut.setValue(checkCast.outValue());
+
+      if (checkCast.getBlock().hasCatchHandlers()) {
+        // Split the block and reset the block iterator.
+        BasicBlock splitBlock = iterator.splitCopyCatchHandlers(code, blocks, appView.options());
+        BasicBlock previousBlock = blocks.previousUntil(block -> block == splitBlock);
+        assert previousBlock == splitBlock;
+        blocks.next();
+        iterator = splitBlock.listIterator(code);
+      }
+
       Instruction next = iterator.next();
       assert next == fieldPut;
     }
     return iterator;
   }
 
+  private InstructionListIterator insertCastsForInvokeArgumentsIfNeeded(
+      IRCode code,
+      BasicBlockIterator blocks,
+      InstructionListIterator iterator,
+      InvokeMethod invoke,
+      MethodLookupResult lookup) {
+    RewrittenPrototypeDescription prototypeChanges = lookup.getPrototypeChanges();
+    if (prototypeChanges.isEmpty()) {
+      return iterator;
+    }
+    for (int argumentIndex = 0; argumentIndex < invoke.arguments().size(); argumentIndex++) {
+      RewrittenTypeInfo rewrittenTypeInfo =
+          prototypeChanges
+              .getArgumentInfoCollection()
+              .getArgumentInfo(argumentIndex)
+              .asRewrittenTypeInfo();
+      if (rewrittenTypeInfo != null && rewrittenTypeInfo.hasCastType()) {
+        iterator.previous();
+        Value object = invoke.getArgument(argumentIndex);
+        CheckCast checkCast =
+            SafeCheckCast.builder()
+                .setObject(object)
+                .setFreshOutValue(
+                    code,
+                    rewrittenTypeInfo
+                        .getCastType()
+                        .toTypeElement(appView, object.getType().nullability()))
+                .setCastType(rewrittenTypeInfo.getCastType())
+                .setPosition(invoke.getPosition())
+                .build();
+        iterator.add(checkCast);
+        invoke.replaceValue(argumentIndex, checkCast.outValue());
+
+        if (checkCast.getBlock().hasCatchHandlers()) {
+          // Split the block and reset the block iterator.
+          BasicBlock splitBlock = iterator.splitCopyCatchHandlers(code, blocks, appView.options());
+          BasicBlock previousBlock = blocks.previousUntil(block -> block == splitBlock);
+          assert previousBlock == splitBlock;
+          blocks.next();
+          iterator = splitBlock.listIterator(code);
+        }
+
+        Instruction next = iterator.next();
+        assert next == invoke;
+      }
+    }
+    return iterator;
+  }
+
+  private InstructionListIterator insertCastForReturnIfNeeded(
+      IRCode code, BasicBlockIterator blocks, InstructionListIterator iterator, Return ret) {
+    RewrittenPrototypeDescription prototypeChanges =
+        appView
+            .graphLens()
+            .lookupPrototypeChangesForMethodDefinition(code.context().getReference());
+    if (!prototypeChanges.hasRewrittenReturnInfo()
+        || !prototypeChanges.getRewrittenReturnInfo().hasCastType()) {
+      return iterator;
+    }
+
+    iterator.previous();
+
+    // Split the block and reset the block iterator.
+    if (ret.getBlock().hasCatchHandlers()) {
+      BasicBlock splitBlock = iterator.splitCopyCatchHandlers(code, blocks, options);
+      BasicBlock previousBlock = blocks.previousUntil(block -> block == splitBlock);
+      assert previousBlock != null;
+      blocks.next();
+      iterator = splitBlock.listIterator(code);
+    }
+
+    DexType castType = prototypeChanges.getRewrittenReturnInfo().getCastType();
+    Value returnValue = ret.returnValue();
+    CheckCast checkCast =
+        SafeCheckCast.builder()
+            .setObject(returnValue)
+            .setFreshOutValue(
+                code, castType.toTypeElement(appView, returnValue.getType().nullability()))
+            .setCastType(castType)
+            .setPosition(ret.getPosition())
+            .build();
+    iterator.add(checkCast);
+    ret.replaceValue(0, checkCast.outValue());
+
+    Instruction next = iterator.next();
+    assert next == ret;
+    return iterator;
+  }
+
   private DexField rewriteFieldReference(FieldLookupResult lookup, ProgramMethod context) {
     if (lookup.hasReboundReference()) {
       DexClass holder = appView.definitionFor(lookup.getReboundReference().getHolderType());
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/callgraph/InvokeExtractor.java b/src/main/java/com/android/tools/r8/ir/conversion/callgraph/InvokeExtractor.java
index ecd06de..a817cd6 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/callgraph/InvokeExtractor.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/callgraph/InvokeExtractor.java
@@ -57,7 +57,7 @@
       // We don't care about calls to native methods.
       return;
     }
-    if (!appView.getKeepInfo(callee).isInliningAllowed(appView.options())) {
+    if (!appView.getKeepInfo(callee).isOptimizationAllowed(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
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
index 71db541..282f4f1 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
@@ -28,6 +28,7 @@
 import com.android.tools.r8.ir.desugar.records.RecordDesugaringEventConsumer.RecordInstructionDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.twr.TwrCloseResourceDesugaringEventConsumer;
 import com.android.tools.r8.shaking.Enqueuer.SyntheticAdditions;
+import com.android.tools.r8.shaking.KeepMethodInfo.Joiner;
 import com.google.common.collect.Sets;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -161,7 +162,7 @@
 
     @Override
     public void acceptTwrCloseResourceMethod(ProgramMethod closeMethod, ProgramMethod context) {
-      methodProcessor.scheduleDesugaredMethodForProcessing(closeMethod);
+      methodProcessor.scheduleMethodForProcessing(closeMethod, this);
     }
 
     @Override
@@ -333,7 +334,7 @@
     public void acceptInvokeStaticInterfaceOutliningMethod(
         ProgramMethod method, ProgramMethod context) {
       // Intentionally empty. The method will be hit by tracing if required.
-      additions.addNeverInlineMethod(method);
+      additions.addMinimumKeepInfo(method, Joiner::disallowInlining);
     }
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
index d7f7109..b934c40 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
@@ -14,6 +14,7 @@
 import com.android.tools.r8.cf.code.CfArrayStore;
 import com.android.tools.r8.cf.code.CfCheckCast;
 import com.android.tools.r8.cf.code.CfCmp;
+import com.android.tools.r8.cf.code.CfConstClass;
 import com.android.tools.r8.cf.code.CfConstNull;
 import com.android.tools.r8.cf.code.CfConstNumber;
 import com.android.tools.r8.cf.code.CfConstString;
@@ -109,6 +110,7 @@
     factory.createSynthesizedType("[Ljava/lang/CharSequence;");
     factory.createSynthesizedType("[Ljava/lang/Class;");
     factory.createSynthesizedType("[Ljava/lang/Object;");
+    factory.createSynthesizedType("[Ljava/lang/Throwable;");
     factory.createSynthesizedType("[Ljava/util/Map$Entry;");
   }
 
@@ -511,10 +513,14 @@
     CfLabel label15 = new CfLabel();
     CfLabel label16 = new CfLabel();
     CfLabel label17 = new CfLabel();
+    CfLabel label18 = new CfLabel();
+    CfLabel label19 = new CfLabel();
+    CfLabel label20 = new CfLabel();
+    CfLabel label21 = new CfLabel();
     return new CfCode(
         method.holder,
+        6,
         4,
-        3,
         ImmutableList.of(
             label0,
             new CfLoad(ValueType.OBJECT, 1),
@@ -749,7 +755,7 @@
                       FrameType.initialized(options.itemFactory.objectType)
                     }),
                 new ArrayDeque<>(Arrays.asList())),
-            new CfGoto(label16),
+            new CfGoto(label20),
             label12,
             new CfFrame(
                 new Int2ReferenceAVLTreeMap<>(
@@ -763,10 +769,76 @@
             new CfStore(ValueType.OBJECT, 2),
             label13,
             new CfLoad(ValueType.OBJECT, 0),
-            new CfIf(If.Type.EQ, ValueType.OBJECT, label14),
-            new CfLoad(ValueType.OBJECT, 0),
-            new CfGoto(label15),
+            new CfIf(If.Type.EQ, ValueType.OBJECT, label19),
             label14,
+            new CfConstClass(options.itemFactory.throwableType),
+            new CfConstString(options.itemFactory.createString("addSuppressed")),
+            new CfConstNumber(1, ValueType.INT),
+            new CfNewArray(options.itemFactory.createType("[Ljava/lang/Class;")),
+            new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+            new CfConstNumber(0, ValueType.INT),
+            new CfConstClass(options.itemFactory.throwableType),
+            new CfArrayStore(MemberType.OBJECT),
+            new CfInvoke(
+                182,
+                options.itemFactory.createMethod(
+                    options.itemFactory.classType,
+                    options.itemFactory.createProto(
+                        options.itemFactory.createType("Ljava/lang/reflect/Method;"),
+                        options.itemFactory.stringType,
+                        options.itemFactory.createType("[Ljava/lang/Class;")),
+                    options.itemFactory.createString("getDeclaredMethod")),
+                false),
+            new CfStore(ValueType.OBJECT, 3),
+            label15,
+            new CfLoad(ValueType.OBJECT, 3),
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfConstNumber(1, ValueType.INT),
+            new CfNewArray(options.itemFactory.createType("[Ljava/lang/Object;")),
+            new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+            new CfConstNumber(0, ValueType.INT),
+            new CfLoad(ValueType.OBJECT, 2),
+            new CfArrayStore(MemberType.OBJECT),
+            new CfInvoke(
+                182,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/lang/reflect/Method;"),
+                    options.itemFactory.createProto(
+                        options.itemFactory.objectType,
+                        options.itemFactory.objectType,
+                        options.itemFactory.createType("[Ljava/lang/Object;")),
+                    options.itemFactory.createString("invoke")),
+                false),
+            new CfStackInstruction(CfStackInstruction.Opcode.Pop),
+            label16,
+            new CfGoto(label18),
+            label17,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.throwableType),
+                      FrameType.initialized(options.itemFactory.objectType),
+                      FrameType.initialized(options.itemFactory.throwableType)
+                    }),
+                new ArrayDeque<>(
+                    Arrays.asList(
+                        FrameType.initialized(
+                            options.itemFactory.createType("Ljava/lang/Exception;"))))),
+            new CfStore(ValueType.OBJECT, 3),
+            label18,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.throwableType),
+                      FrameType.initialized(options.itemFactory.objectType),
+                      FrameType.initialized(options.itemFactory.throwableType)
+                    }),
+                new ArrayDeque<>(Arrays.asList())),
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfThrow(),
+            label19,
             new CfFrame(
                 new Int2ReferenceAVLTreeMap<>(
                     new int[] {0, 1, 2},
@@ -777,19 +849,8 @@
                     }),
                 new ArrayDeque<>(Arrays.asList())),
             new CfLoad(ValueType.OBJECT, 2),
-            label15,
-            new CfFrame(
-                new Int2ReferenceAVLTreeMap<>(
-                    new int[] {0, 1, 2},
-                    new FrameType[] {
-                      FrameType.initialized(options.itemFactory.throwableType),
-                      FrameType.initialized(options.itemFactory.objectType),
-                      FrameType.initialized(options.itemFactory.throwableType)
-                    }),
-                new ArrayDeque<>(
-                    Arrays.asList(FrameType.initialized(options.itemFactory.throwableType)))),
             new CfThrow(),
-            label16,
+            label20,
             new CfFrame(
                 new Int2ReferenceAVLTreeMap<>(
                     new int[] {0, 1},
@@ -799,7 +860,7 @@
                     }),
                 new ArrayDeque<>(Arrays.asList())),
             new CfReturnVoid(),
-            label17),
+            label21),
         ImmutableList.of(
             new CfTryCatch(
                 label2,
@@ -841,7 +902,12 @@
                 label0,
                 label11,
                 ImmutableList.of(options.itemFactory.throwableType),
-                ImmutableList.of(label12))),
+                ImmutableList.of(label12)),
+            new CfTryCatch(
+                label14,
+                label16,
+                ImmutableList.of(options.itemFactory.createType("Ljava/lang/Exception;")),
+                ImmutableList.of(label17))),
         ImmutableList.of());
   }
 
@@ -9079,6 +9145,163 @@
         ImmutableList.of());
   }
 
+  public static CfCode ThrowableMethods_addSuppressed(InternalOptions options, DexMethod method) {
+    CfLabel label0 = new CfLabel();
+    CfLabel label1 = new CfLabel();
+    CfLabel label2 = new CfLabel();
+    CfLabel label3 = new CfLabel();
+    CfLabel label4 = new CfLabel();
+    CfLabel label5 = new CfLabel();
+    return new CfCode(
+        method.holder,
+        6,
+        3,
+        ImmutableList.of(
+            label0,
+            new CfConstClass(options.itemFactory.throwableType),
+            new CfConstString(options.itemFactory.createString("addSuppressed")),
+            new CfConstNumber(1, ValueType.INT),
+            new CfNewArray(options.itemFactory.createType("[Ljava/lang/Class;")),
+            new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+            new CfConstNumber(0, ValueType.INT),
+            new CfConstClass(options.itemFactory.throwableType),
+            new CfArrayStore(MemberType.OBJECT),
+            new CfInvoke(
+                182,
+                options.itemFactory.createMethod(
+                    options.itemFactory.classType,
+                    options.itemFactory.createProto(
+                        options.itemFactory.createType("Ljava/lang/reflect/Method;"),
+                        options.itemFactory.stringType,
+                        options.itemFactory.createType("[Ljava/lang/Class;")),
+                    options.itemFactory.createString("getDeclaredMethod")),
+                false),
+            new CfStore(ValueType.OBJECT, 2),
+            label1,
+            new CfLoad(ValueType.OBJECT, 2),
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfConstNumber(1, ValueType.INT),
+            new CfNewArray(options.itemFactory.createType("[Ljava/lang/Object;")),
+            new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+            new CfConstNumber(0, ValueType.INT),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfArrayStore(MemberType.OBJECT),
+            new CfInvoke(
+                182,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/lang/reflect/Method;"),
+                    options.itemFactory.createProto(
+                        options.itemFactory.objectType,
+                        options.itemFactory.objectType,
+                        options.itemFactory.createType("[Ljava/lang/Object;")),
+                    options.itemFactory.createString("invoke")),
+                false),
+            new CfStackInstruction(CfStackInstruction.Opcode.Pop),
+            label2,
+            new CfGoto(label4),
+            label3,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.throwableType),
+                      FrameType.initialized(options.itemFactory.throwableType)
+                    }),
+                new ArrayDeque<>(
+                    Arrays.asList(
+                        FrameType.initialized(
+                            options.itemFactory.createType("Ljava/lang/Exception;"))))),
+            new CfStore(ValueType.OBJECT, 2),
+            label4,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.throwableType),
+                      FrameType.initialized(options.itemFactory.throwableType)
+                    }),
+                new ArrayDeque<>(Arrays.asList())),
+            new CfReturnVoid(),
+            label5),
+        ImmutableList.of(
+            new CfTryCatch(
+                label0,
+                label2,
+                ImmutableList.of(options.itemFactory.createType("Ljava/lang/Exception;")),
+                ImmutableList.of(label3))),
+        ImmutableList.of());
+  }
+
+  public static CfCode ThrowableMethods_getSuppressed(InternalOptions options, DexMethod method) {
+    CfLabel label0 = new CfLabel();
+    CfLabel label1 = new CfLabel();
+    CfLabel label2 = new CfLabel();
+    CfLabel label3 = new CfLabel();
+    CfLabel label4 = new CfLabel();
+    CfLabel label5 = new CfLabel();
+    return new CfCode(
+        method.holder,
+        3,
+        2,
+        ImmutableList.of(
+            label0,
+            new CfConstClass(options.itemFactory.throwableType),
+            new CfConstString(options.itemFactory.createString("getSuppressed")),
+            new CfConstNumber(0, ValueType.INT),
+            new CfNewArray(options.itemFactory.createType("[Ljava/lang/Class;")),
+            new CfInvoke(
+                182,
+                options.itemFactory.createMethod(
+                    options.itemFactory.classType,
+                    options.itemFactory.createProto(
+                        options.itemFactory.createType("Ljava/lang/reflect/Method;"),
+                        options.itemFactory.stringType,
+                        options.itemFactory.createType("[Ljava/lang/Class;")),
+                    options.itemFactory.createString("getDeclaredMethod")),
+                false),
+            new CfStore(ValueType.OBJECT, 1),
+            label1,
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfConstNumber(0, ValueType.INT),
+            new CfNewArray(options.itemFactory.createType("[Ljava/lang/Object;")),
+            new CfInvoke(
+                182,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/lang/reflect/Method;"),
+                    options.itemFactory.createProto(
+                        options.itemFactory.objectType,
+                        options.itemFactory.objectType,
+                        options.itemFactory.createType("[Ljava/lang/Object;")),
+                    options.itemFactory.createString("invoke")),
+                false),
+            new CfCheckCast(options.itemFactory.createType("[Ljava/lang/Throwable;")),
+            label2,
+            new CfReturn(ValueType.OBJECT),
+            label3,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0},
+                    new FrameType[] {FrameType.initialized(options.itemFactory.throwableType)}),
+                new ArrayDeque<>(
+                    Arrays.asList(
+                        FrameType.initialized(
+                            options.itemFactory.createType("Ljava/lang/Exception;"))))),
+            new CfStore(ValueType.OBJECT, 1),
+            label4,
+            new CfConstNumber(0, ValueType.INT),
+            new CfNewArray(options.itemFactory.createType("[Ljava/lang/Throwable;")),
+            new CfReturn(ValueType.OBJECT),
+            label5),
+        ImmutableList.of(
+            new CfTryCatch(
+                label0,
+                label2,
+                ImmutableList.of(options.itemFactory.createType("Ljava/lang/Exception;")),
+                ImmutableList.of(label3))),
+        ImmutableList.of());
+  }
+
   public static CfCode UnsafeMethods_compareAndSwapObject(
       InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java
index a332828..48dca2d 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java
@@ -162,7 +162,11 @@
       synthesizeConstantDynamicClass(builder);
     } else {
       // Unconditionally throw as the RI.
-      behaviour = resolution.isFailedResolution() ? THROW_NSME : THROW_ICCE;
+      behaviour =
+          resolution.isNoSuchMethodErrorResult(
+                  context.getContextClass(), appView.appInfoForDesugaring())
+              ? THROW_NSME
+              : THROW_ICCE;
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterSyntheticHelper.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterSyntheticHelper.java
index b1e77cd..1a46480 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterSyntheticHelper.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterSyntheticHelper.java
@@ -13,7 +13,7 @@
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.DesugaredLibraryRetargeterSynthesizerEventConsumer.DesugaredLibraryRetargeterInstructionEventConsumer;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.DesugaredLibraryRetargeterSynthesizerEventConsumer.DesugaredLibraryRetargeterL8SynthesizerEventConsumer;
-import com.android.tools.r8.ir.synthetic.EmulateInterfaceSyntheticCfCodeProvider;
+import com.android.tools.r8.ir.synthetic.EmulateDispatchSyntheticCfCodeProvider;
 import com.android.tools.r8.synthesis.SyntheticClassBuilder;
 import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
 import com.android.tools.r8.utils.DescriptorUtils;
@@ -164,9 +164,8 @@
               .setCode(
                   methodSig ->
                       appView.options().isDesugaredLibraryCompilation()
-                          ? new EmulateInterfaceSyntheticCfCodeProvider(
+                          ? new EmulateDispatchSyntheticCfCodeProvider(
                                   methodSig.getHolderType(),
-                                  emulatedDispatchMethod.getHolderType(),
                                   desugarMethod,
                                   itfMethod,
                                   Collections.emptyList(),
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/ProgramEmulatedInterfaceSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/ProgramEmulatedInterfaceSynthesizer.java
index 5c2ec42..3d91f5c 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/ProgramEmulatedInterfaceSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/ProgramEmulatedInterfaceSynthesizer.java
@@ -16,7 +16,7 @@
 import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaring;
 import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.itf.EmulatedInterfaceSynthesizerEventConsumer.L8ProgramEmulatedInterfaceSynthesizerEventConsumer;
-import com.android.tools.r8.ir.synthetic.EmulateInterfaceSyntheticCfCodeProvider;
+import com.android.tools.r8.ir.synthetic.EmulateDispatchSyntheticCfCodeProvider;
 import com.android.tools.r8.synthesis.SyntheticMethodBuilder;
 import com.android.tools.r8.synthesis.SyntheticNaming;
 import com.android.tools.r8.synthesis.SyntheticProgramClassBuilder;
@@ -145,9 +145,8 @@
         helper.ensureDefaultAsMethodOfProgramCompanionClassStub(method).getReference();
     List<Pair<DexType, DexMethod>> extraDispatchCases =
         getDispatchCases(method, theInterface, companionMethod);
-    return new EmulateInterfaceSyntheticCfCodeProvider(
+    return new EmulateDispatchSyntheticCfCodeProvider(
             emulatedInterfaceMethod.getHolderType(),
-            method.getHolderType(),
             companionMethod,
             libraryMethod,
             extraDispatchCases,
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/twr/TwrInstructionDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/twr/TwrInstructionDesugaring.java
index 716b6e2..1b5b27c 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/twr/TwrInstructionDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/twr/TwrInstructionDesugaring.java
@@ -3,15 +3,12 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.desugar.twr;
 
-import com.android.tools.r8.cf.code.CfConstNumber;
 import com.android.tools.r8.cf.code.CfInstruction;
 import com.android.tools.r8.cf.code.CfInvoke;
-import com.android.tools.r8.cf.code.CfNewArray;
-import com.android.tools.r8.cf.code.CfStackInstruction;
-import com.android.tools.r8.cf.code.CfStackInstruction.Opcode;
 import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.CfCode;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
@@ -19,7 +16,6 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.code.ValueType;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaring;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaringCollection;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
@@ -27,9 +23,11 @@
 import com.android.tools.r8.ir.desugar.LocalStackAllocator;
 import com.android.tools.r8.ir.desugar.backports.BackportedMethods;
 import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
+import com.android.tools.r8.utils.InternalOptions;
 import com.google.common.collect.ImmutableList;
 import java.util.Collection;
-import org.jetbrains.annotations.NotNull;
+import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
 import org.objectweb.asm.Opcodes;
 
 public class TwrInstructionDesugaring implements CfInstructionDesugaring {
@@ -66,58 +64,85 @@
     if (isTwrCloseResourceInvoke(instruction)) {
       return rewriteTwrCloseResourceInvoke(eventConsumer, context, methodProcessingContext);
     }
-    if (isTwrSuppressedInvoke(instruction, addSuppressed)) {
-      return rewriteTwrAddSuppressedInvoke();
-    }
-    if (isTwrSuppressedInvoke(instruction, getSuppressed)) {
-      return rewriteTwrGetSuppressedInvoke();
+    if (!appView.options().canUseSuppressedExceptions()) {
+      if (isTwrSuppressedInvoke(instruction, addSuppressed)) {
+        return rewriteTwrAddSuppressedInvoke(eventConsumer, methodProcessingContext);
+      }
+      if (isTwrSuppressedInvoke(instruction, getSuppressed)) {
+        return rewriteTwrGetSuppressedInvoke(eventConsumer, methodProcessingContext);
+      }
     }
     return null;
   }
 
-  private Collection<CfInstruction> rewriteTwrAddSuppressedInvoke() {
-    // Remove Throwable::addSuppressed(Throwable) call.
-    return ImmutableList.of(new CfStackInstruction(Opcode.Pop), new CfStackInstruction(Opcode.Pop));
+  private Collection<CfInstruction> rewriteTwrAddSuppressedInvoke(
+      CfInstructionDesugaringEventConsumer eventConsumer,
+      MethodProcessingContext methodProcessingContext) {
+    DexItemFactory factory = appView.dexItemFactory();
+    DexProto proto =
+        factory.createProto(factory.voidType, factory.throwableType, factory.throwableType);
+    return createAndCallSyntheticMethod(
+        SyntheticKind.BACKPORT,
+        proto,
+        BackportedMethods::ThrowableMethods_addSuppressed,
+        methodProcessingContext,
+        eventConsumer::acceptBackportedMethod,
+        methodProcessingContext.getMethodContext());
   }
 
-  private Collection<CfInstruction> rewriteTwrGetSuppressedInvoke() {
-    // Replace call to Throwable::getSuppressed() with new Throwable[0].
-    return ImmutableList.of(
-        new CfStackInstruction(Opcode.Pop),
-        new CfConstNumber(0, ValueType.INT),
-        new CfNewArray(dexItemFactory.createArrayType(1, dexItemFactory.throwableType)));
+  private Collection<CfInstruction> rewriteTwrGetSuppressedInvoke(
+      CfInstructionDesugaringEventConsumer eventConsumer,
+      MethodProcessingContext methodProcessingContext) {
+    DexItemFactory factory = appView.dexItemFactory();
+    DexProto proto =
+        factory.createProto(
+            factory.createArrayType(1, factory.throwableType), factory.throwableType);
+    return createAndCallSyntheticMethod(
+        SyntheticKind.BACKPORT,
+        proto,
+        BackportedMethods::ThrowableMethods_getSuppressed,
+        methodProcessingContext,
+        eventConsumer::acceptBackportedMethod,
+        methodProcessingContext.getMethodContext());
   }
 
-  @NotNull
   private ImmutableList<CfInstruction> rewriteTwrCloseResourceInvoke(
       CfInstructionDesugaringEventConsumer eventConsumer,
       ProgramMethod context,
       MethodProcessingContext methodProcessingContext) {
     // Synthesize a new method.
-    ProgramMethod closeMethod = createSyntheticCloseResourceMethod(methodProcessingContext);
-    eventConsumer.acceptTwrCloseResourceMethod(closeMethod, context);
-    // Rewrite the invoke to the new synthetic.
-    return ImmutableList.of(new CfInvoke(Opcodes.INVOKESTATIC, closeMethod.getReference(), false));
+    return createAndCallSyntheticMethod(
+        SyntheticKind.TWR_CLOSE_RESOURCE,
+        twrCloseResourceProto,
+        BackportedMethods::CloseResourceMethod_closeResourceImpl,
+        methodProcessingContext,
+        eventConsumer::acceptTwrCloseResourceMethod,
+        context);
   }
 
-  private ProgramMethod createSyntheticCloseResourceMethod(
-      MethodProcessingContext methodProcessingContext) {
-    return appView
-        .getSyntheticItems()
-        .createMethod(
-            SyntheticKind.TWR_CLOSE_RESOURCE,
-            methodProcessingContext.createUniqueContext(),
-            appView,
-            methodBuilder ->
-                methodBuilder
-                    .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
-                    .setProto(twrCloseResourceProto)
-                    // Will be traced by the enqueuer.
-                    .disableAndroidApiLevelCheck()
-                    .setCode(
-                        m ->
-                            BackportedMethods.CloseResourceMethod_closeResourceImpl(
-                                appView.options(), m)));
+  private ImmutableList<CfInstruction> createAndCallSyntheticMethod(
+      SyntheticKind kind,
+      DexProto proto,
+      BiFunction<InternalOptions, DexMethod, CfCode> generator,
+      MethodProcessingContext methodProcessingContext,
+      BiConsumer<ProgramMethod, ProgramMethod> eventConsumerCallback,
+      ProgramMethod context) {
+    ProgramMethod method =
+        appView
+            .getSyntheticItems()
+            .createMethod(
+                kind,
+                methodProcessingContext.createUniqueContext(),
+                appView,
+                builder ->
+                    builder
+                        // Will be traced by the enqueuer.
+                        .disableAndroidApiLevelCheck()
+                        .setProto(proto)
+                        .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
+                        .setCode(methodSig -> generator.apply(appView.options(), methodSig)));
+    eventConsumerCallback.accept(method, context);
+    return ImmutableList.of(new CfInvoke(Opcodes.INVOKESTATIC, method.getReference(), false));
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index a10c4d7..7d12496 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -34,6 +34,7 @@
 import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.ir.analysis.type.TypeUtils;
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
 import com.android.tools.r8.ir.analysis.value.ConstantOrNonConstantNumberValue;
 import com.android.tools.r8.ir.analysis.value.SingleConstClassValue;
@@ -1530,8 +1531,7 @@
 
     TypeElement inTypeLattice = inValue.getType();
     TypeElement outTypeLattice = outValue.getType();
-    TypeElement castTypeLattice =
-        TypeElement.fromDexType(castType, inTypeLattice.nullability(), appView);
+    TypeElement castTypeLattice = castType.toTypeElement(appView, inTypeLattice.nullability());
 
     assert inTypeLattice.nullability().lessThanOrEqual(outTypeLattice.nullability());
 
@@ -1586,6 +1586,21 @@
       return RemoveCheckCastInstructionIfTrivialResult.REMOVED_CAST_DO_NARROW;
     }
 
+    // If the cast is guaranteed to succeed and only there to ensure the program type checks, then
+    // check if the program would still type check after removing the cast.
+    if (checkCast.isSafeCheckCast()
+        || checkCast
+            .getFirstOperand()
+            .getDynamicType(appViewWithLiveness)
+            .getDynamicUpperBoundType()
+            .lessThanOrEqualUpToNullability(castTypeLattice, appView)) {
+      TypeElement useType =
+          TypeUtils.computeUseType(appViewWithLiveness, context, checkCast.outValue());
+      if (inTypeLattice.lessThanOrEqualUpToNullability(useType, appView)) {
+        return RemoveCheckCastInstructionIfTrivialResult.REMOVED_CAST_DO_NARROW;
+      }
+    }
+
     // Otherwise, keep the checkcast to preserve verification errors. E.g., down-cast:
     // A < B < C
     // c = ...        // Even though we know c is of type A,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
index 35e919e..4e6bea5 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
@@ -341,11 +341,6 @@
       return true;
     }
 
-    if (appInfo.isNeverInlineMethod(singleTargetReference)) {
-      whyAreYouNotInliningReporter.reportMarkedAsNeverInline();
-      return true;
-    }
-
     if (appInfo.noSideEffects.containsKey(invoke.getInvokedMethod())
         || appInfo.noSideEffects.containsKey(resolutionResult.getResolvedMethod().getReference())
         || appInfo.noSideEffects.containsKey(singleTargetReference)) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/OutlinerImpl.java b/src/main/java/com/android/tools/r8/ir/optimize/OutlinerImpl.java
index 7ef7216..4093846 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/OutlinerImpl.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/OutlinerImpl.java
@@ -795,6 +795,9 @@
       while (index < currentCandidateInstructions.size()) {
         processInstruction(currentCandidateInstructions.get(index));
       }
+      if (actualInstructions > 0) {
+        candidate(start, index);
+      }
     }
 
     // Get int in-values for an instruction. For commutative binary operations using the current
@@ -1448,12 +1451,17 @@
         boolean sawLinearFlowWithCatchHandlers = false;
         while (instructionIterator.hasNext()) {
           Instruction instruction = instructionIterator.next();
-          // Disregard linear flow when there are catch handlers
-          if (instruction.getBlock() != block
-              && (block.hasCatchHandlers() || instruction.getBlock().hasCatchHandlers())) {
-            lastSeenBlock = instruction.getBlock();
-            sawLinearFlowWithCatchHandlers = true;
-            break;
+          if (instruction.getBlock() != block) {
+            // Disregard linear flow when there are catch handlers
+            if (block.hasCatchHandlers() || instruction.getBlock().hasCatchHandlers()) {
+              lastSeenBlock = instruction.getBlock();
+              sawLinearFlowWithCatchHandlers = true;
+              break;
+            }
+            // Disregard revisiting already processed blocks.
+            if (seenBlocks.contains(instruction.getBlock())) {
+              break;
+            }
           }
           builder.add(instruction);
           counter++;
@@ -1464,6 +1472,7 @@
           }
           lastSeenBlock = instruction.getBlock();
         }
+        // Add all seen blocks including trivial goto blocks skipped by the linear iterator.
         seenBlocks.addAll(instructionIterator.getSeenBlocks());
         if (sawLinearFlowWithCatchHandlers) {
           assert lastSeenBlock != block;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
index d317655..056d9c6 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
@@ -1224,6 +1224,9 @@
         .isPossiblyFalse()) {
       return false;
     }
+    if (!appView.getKeepInfo(singleTarget).isClassInliningAllowed(appView.options())) {
+      return false;
+    }
     if (!singleTarget
         .getDefinition()
         .isInliningCandidate(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/ConditionalClassInlinerMethodConstraint.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/ConditionalClassInlinerMethodConstraint.java
index 5403b86..4aceb17 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/ConditionalClassInlinerMethodConstraint.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/ConditionalClassInlinerMethodConstraint.java
@@ -11,7 +11,6 @@
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfo;
 import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.RewrittenTypeInfo;
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
 import com.android.tools.r8.ir.analysis.value.SingleConstValue;
 import com.android.tools.r8.ir.analysis.value.objectstate.ObjectState;
@@ -51,11 +50,10 @@
                 // usages of that parameter for class inlining.
                 return;
               }
-              if (argumentInfo.isRewrittenTypeInfo()) {
+              if (argumentInfo.isRewrittenTypeInfo()
+                  && argumentInfo.asRewrittenTypeInfo().getNewType().isIntType()) {
                 // This is due to enum unboxing. After enum unboxing, we no longer need information
                 // about the usages of this parameter for class inlining.
-                RewrittenTypeInfo rewrittenTypeInfo = argumentInfo.asRewrittenTypeInfo();
-                assert rewrittenTypeInfo.verifyIsDueToUnboxing(appView.dexItemFactory());
                 return;
               }
               backing.put(changes.getNewArgumentIndex(argumentIndex), usagePerContext);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
index 1fe7a27..e055f70 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
@@ -63,11 +63,14 @@
           if (unboxedEnums.hasUnboxedValueFor(singleFieldValue.getField())) {
             prototypeChanges =
                 prototypeChanges.withRewrittenReturnInfo(
-                    new RewrittenTypeInfo(
-                        rewrittenTypeInfo.getOldType(),
-                        rewrittenTypeInfo.getNewType(),
-                        abstractValueFactory.createSingleNumberValue(
-                            unboxedEnums.getUnboxedValue(singleFieldValue.getField()))));
+                    RewrittenTypeInfo.builder()
+                        .setCastType(rewrittenTypeInfo.getCastType())
+                        .setOldType(rewrittenTypeInfo.getOldType())
+                        .setNewType(rewrittenTypeInfo.getNewType())
+                        .setSingleValue(
+                            abstractValueFactory.createSingleNumberValue(
+                                unboxedEnums.getUnboxedValue(singleFieldValue.getField())))
+                        .build());
           }
         }
       }
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 2ee5a6e..e855aa1 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
@@ -119,6 +119,10 @@
     return constants.getOrDefault(argIndex, UnknownValue.getInstance());
   }
 
+  public Nullability getNullability(int argIndex) {
+    return getDynamicType(argIndex).getNullability();
+  }
+
   public static CallSiteOptimizationInfo fromMethodState(
       AppView<AppInfoWithLiveness> appView,
       ProgramMethod method,
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 62db1f4..2cb900b 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
@@ -445,7 +445,13 @@
   }
 
   public MutableMethodOptimizationInfo fixupUnusedArguments(MethodOptimizationInfoFixer fixer) {
-    unusedArguments = fixer.fixupUnusedArguments(unusedArguments);
+    fixupUnusedArguments(fixer.fixupUnusedArguments(unusedArguments));
+    return this;
+  }
+
+  public MutableMethodOptimizationInfo fixupUnusedArguments(BitSet unusedArguments) {
+    this.unusedArguments =
+        unusedArguments != null && !unusedArguments.isEmpty() ? unusedArguments : null;
     return this;
   }
 
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 c6cd18d..a763db6 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
@@ -210,6 +210,16 @@
     method.getDefinition().getMutableOptimizationInfo().setUnusedArguments(unusedArguments);
   }
 
+  public void fixupUnusedArguments(ProgramMethod method, Consumer<BitSet> fixer) {
+    if (method.getOptimizationInfo().hasUnusedArguments()) {
+      MutableMethodOptimizationInfo optimizationInfo =
+          method.getDefinition().getMutableOptimizationInfo();
+      BitSet newUnusedArguments = (BitSet) optimizationInfo.getUnusedArguments().clone();
+      fixer.accept(newUnusedArguments);
+      optimizationInfo.fixupUnusedArguments(newUnusedArguments);
+    }
+  }
+
   // Unset methods.
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/inliner/DefaultInliningReasonStrategy.java b/src/main/java/com/android/tools/r8/ir/optimize/inliner/DefaultInliningReasonStrategy.java
index 3a9c668..537150e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/inliner/DefaultInliningReasonStrategy.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/inliner/DefaultInliningReasonStrategy.java
@@ -39,7 +39,7 @@
     DexEncodedMethod targetMethod = target.getDefinition();
     DexMethod targetReference = target.getReference();
     if (targetMethod.getOptimizationInfo().forceInline()) {
-      assert !appView.appInfo().isNeverInlineMethod(targetReference);
+      assert appView.getKeepInfo(target).isInliningAllowed(appView.options());
       return Reason.FORCE;
     }
     if (appView.appInfo().hasLiveness()
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/typechecks/CheckCastAndInstanceOfMethodSpecialization.java b/src/main/java/com/android/tools/r8/ir/optimize/typechecks/CheckCastAndInstanceOfMethodSpecialization.java
index 13f384e..8c6a531 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/typechecks/CheckCastAndInstanceOfMethodSpecialization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/typechecks/CheckCastAndInstanceOfMethodSpecialization.java
@@ -142,6 +142,7 @@
           parentMethodDefinition.getCode().buildIR(parentMethod, appView, parentMethod.getOrigin());
       converter.markProcessed(code, feedback);
       // Fixup method optimization info (the method no longer returns a constant).
+      feedback.fixupUnusedArguments(parentMethod, unusedArguments -> unusedArguments.clear(0));
       feedback.unsetAbstractReturnValue(parentMethod);
       feedback.unsetClassInlinerMethodConstraint(parentMethod);
     } else {
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/EmulateInterfaceSyntheticCfCodeProvider.java b/src/main/java/com/android/tools/r8/ir/synthetic/EmulateDispatchSyntheticCfCodeProvider.java
similarity index 69%
rename from src/main/java/com/android/tools/r8/ir/synthetic/EmulateInterfaceSyntheticCfCodeProvider.java
rename to src/main/java/com/android/tools/r8/ir/synthetic/EmulateDispatchSyntheticCfCodeProvider.java
index e1ba3b2..4847e78 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/EmulateInterfaceSyntheticCfCodeProvider.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/EmulateDispatchSyntheticCfCodeProvider.java
@@ -1,4 +1,4 @@
-// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// Copyright (c) 2022, 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.
 
@@ -28,24 +28,23 @@
 import java.util.List;
 import org.objectweb.asm.Opcodes;
 
-public class EmulateInterfaceSyntheticCfCodeProvider extends SyntheticCfCodeProvider {
+public class EmulateDispatchSyntheticCfCodeProvider extends SyntheticCfCodeProvider {
 
-  private final DexType interfaceType;
-  private final DexMethod companionMethod;
-  private final DexMethod libraryMethod;
+  private final DexType receiverType;
+  private final DexMethod forwardingMethod;
+  private final DexMethod interfaceMethod;
   private final List<Pair<DexType, DexMethod>> extraDispatchCases;
 
-  public EmulateInterfaceSyntheticCfCodeProvider(
+  public EmulateDispatchSyntheticCfCodeProvider(
       DexType holder,
-      DexType interfaceType,
-      DexMethod companionMethod,
-      DexMethod libraryMethod,
+      DexMethod forwardingMethod,
+      DexMethod interfaceMethod,
       List<Pair<DexType, DexMethod>> extraDispatchCases,
       AppView<?> appView) {
     super(appView, holder);
-    this.interfaceType = interfaceType;
-    this.companionMethod = companionMethod;
-    this.libraryMethod = libraryMethod;
+    this.receiverType = forwardingMethod.getParameter(0);
+    this.forwardingMethod = forwardingMethod;
+    this.interfaceMethod = interfaceMethod;
     this.extraDispatchCases = extraDispatchCases;
   }
 
@@ -60,22 +59,22 @@
 
     ImmutableInt2ReferenceSortedMap.Builder<FrameType> localsBuilder =
         ImmutableInt2ReferenceSortedMap.builder();
-    localsBuilder.put(0, FrameType.initialized(interfaceType));
+    localsBuilder.put(0, FrameType.initialized(receiverType));
     int index = 1;
-    for (DexType param : libraryMethod.proto.parameters.values) {
+    for (DexType param : interfaceMethod.proto.parameters.values) {
       localsBuilder.put(index++, FrameType.initialized(param));
     }
     ImmutableInt2ReferenceSortedMap<FrameType> locals = localsBuilder.build();
 
-    instructions.add(new CfLoad(ValueType.fromDexType(interfaceType), 0));
-    instructions.add(new CfInstanceOf(libraryMethod.holder));
+    instructions.add(new CfLoad(ValueType.fromDexType(receiverType), 0));
+    instructions.add(new CfInstanceOf(interfaceMethod.holder));
     instructions.add(new CfIf(If.Type.EQ, ValueType.INT, labels[nextLabel]));
 
     // Branch with library call.
-    instructions.add(new CfLoad(ValueType.fromDexType(interfaceType), 0));
-    instructions.add(new CfCheckCast(libraryMethod.holder));
+    instructions.add(new CfLoad(ValueType.fromDexType(receiverType), 0));
+    instructions.add(new CfCheckCast(interfaceMethod.holder));
     loadExtraParameters(instructions);
-    instructions.add(new CfInvoke(Opcodes.INVOKEINTERFACE, libraryMethod, true));
+    instructions.add(new CfInvoke(Opcodes.INVOKEINTERFACE, interfaceMethod, true));
     addReturn(instructions);
 
     // SubInterface dispatch (subInterfaces are ordered).
@@ -83,12 +82,12 @@
       // Type check basic block.
       instructions.add(labels[nextLabel++]);
       instructions.add(new CfFrame(locals, ImmutableDeque.of()));
-      instructions.add(new CfLoad(ValueType.fromDexType(interfaceType), 0));
+      instructions.add(new CfLoad(ValueType.fromDexType(receiverType), 0));
       instructions.add(new CfInstanceOf(dispatch.getFirst()));
       instructions.add(new CfIf(If.Type.EQ, ValueType.INT, labels[nextLabel]));
 
       // Call basic block.
-      instructions.add(new CfLoad(ValueType.fromDexType(interfaceType), 0));
+      instructions.add(new CfLoad(ValueType.fromDexType(receiverType), 0));
       instructions.add(new CfCheckCast(dispatch.getFirst()));
       loadExtraParameters(instructions);
       instructions.add(new CfInvoke(Opcodes.INVOKESTATIC, dispatch.getSecond(), false));
@@ -98,25 +97,25 @@
     // Branch with companion call.
     instructions.add(labels[nextLabel]);
     instructions.add(new CfFrame(locals, ImmutableDeque.of()));
-    instructions.add(new CfLoad(ValueType.fromDexType(interfaceType), 0));
+    instructions.add(new CfLoad(ValueType.fromDexType(receiverType), 0));
     loadExtraParameters(instructions);
-    instructions.add(new CfInvoke(Opcodes.INVOKESTATIC, companionMethod, false));
+    instructions.add(new CfInvoke(Opcodes.INVOKESTATIC, forwardingMethod, false));
     addReturn(instructions);
     return standardCfCodeFromInstructions(instructions);
   }
 
   private void loadExtraParameters(List<CfInstruction> instructions) {
     int index = 1;
-    for (DexType type : libraryMethod.proto.parameters.values) {
+    for (DexType type : interfaceMethod.proto.parameters.values) {
       instructions.add(new CfLoad(ValueType.fromDexType(type), index++));
     }
   }
 
   private void addReturn(List<CfInstruction> instructions) {
-    if (libraryMethod.proto.returnType == appView.dexItemFactory().voidType) {
+    if (interfaceMethod.proto.returnType == appView.dexItemFactory().voidType) {
       instructions.add(new CfReturnVoid());
     } else {
-      instructions.add(new CfReturn(ValueType.fromDexType(libraryMethod.proto.returnType)));
+      instructions.add(new CfReturn(ValueType.fromDexType(interfaceMethod.proto.returnType)));
     }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
index f948457..70e6d04 100644
--- a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
@@ -6,7 +6,6 @@
 import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
 
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.BottomUpClassHierarchyTraversal;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexField;
@@ -19,12 +18,15 @@
 import com.android.tools.r8.graph.SubtypingInfo;
 import com.android.tools.r8.graph.TopDownClassHierarchyTraversal;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.SetUtils;
 import com.android.tools.r8.utils.Timing;
+import com.android.tools.r8.utils.TraversalContinuation;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Sets;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Comparator;
 import java.util.Deque;
 import java.util.IdentityHashMap;
 import java.util.List;
@@ -37,8 +39,12 @@
   private final AppView<AppInfoWithLiveness> appView;
   private final SubtypingInfo subtypingInfo;
   private final Map<DexField, DexString> renaming = new IdentityHashMap<>();
-  private Map<DexType, ReservedFieldNamingState> reservedNamingStates = new IdentityHashMap<>();
+  private final Map<DexType, ReservedFieldNamingState> reservedNamingStates =
+      new IdentityHashMap<>();
   private final MemberNamingStrategy strategy;
+  private final Map<DexType, DexType> frontiers = new IdentityHashMap<>();
+  private final Map<DexType, Set<ReservedFieldNamingState>> frontierStatesForInterfaces =
+      new IdentityHashMap<>();
 
   FieldNameMinifier(
       AppView<AppInfoWithLiveness> appView,
@@ -60,8 +66,8 @@
     // Rename the definitions.
     timing.begin("rename-definitions");
     renameFieldsInInterfaces(interfaces);
-    propagateReservedFieldNamesUpwards();
     renameFieldsInClasses();
+    renameFieldsInUnrelatedClasspathClasses();
     timing.end();
     // Rename the references that are not rebound to definitions for some reasons.
     timing.begin("rename-references");
@@ -93,19 +99,36 @@
   }
 
   private void reserveFieldNames() {
-    // Reserve all field names that need to be reserved.
-    appView
-        .appInfo()
-        .forEachTypeInHierarchyOfLiveProgramClasses(
+    // Build up all reservations in the class hierarchy such that all reserved names are placed
+    // at the boundary between a library class and a program class - referred to as the frontier.
+    // Special handling is done for interfaces by always considering them to be roots. When
+    // traversing down the hierarchy we built up a map from interface to reservation states:
+    // - when we reach a frontier find all directly and indirectly implemented interfaces and
+    //   add the current reservation state
+    // - when we see a program class that implements a direct super type that is an interface also
+    //   add the current reservation state. Note that even though we do not visit super interfaces
+    //   here, this still works because a super interface will be in the same partition.
+    // For an in depth description see MethodNameMinifier.
+    TopDownClassHierarchyTraversal.forAllClasses(appView)
+        .visit(
+            appView.appInfo().classes(),
             clazz -> {
-              ReservedFieldNamingState reservedNames = null;
+              DexType frontier =
+                  clazz.superType == null
+                      ? appView.dexItemFactory().objectType
+                      : frontiers.getOrDefault(clazz.superType, clazz.type);
+              // If frontier != clazz.type we have seen a program class that is on the boundary.
+              // Otherwise, if we are visiting a program class then that is the frontier.
+              if (frontier != clazz.type || clazz.isProgramClass()) {
+                DexType existingValue = frontiers.put(clazz.type, frontier);
+                assert existingValue == null;
+              }
+              ReservedFieldNamingState reservationState =
+                  getOrCreateReservedFieldNamingState(frontier);
               for (DexEncodedField field : clazz.fields()) {
                 DexString reservedName = strategy.getReservedName(field, clazz);
                 if (reservedName != null) {
-                  if (reservedNames == null) {
-                    reservedNames = getOrCreateReservedFieldNamingState(clazz.type);
-                  }
-                  reservedNames.markReservedDirectly(
+                  reservationState.markReserved(
                       reservedName, field.getReference().name, field.getReference().type);
                   // TODO(b/148846065): Consider lazily computing the renaming on actual lookups.
                   if (reservedName != field.getReference().name) {
@@ -113,51 +136,47 @@
                   }
                 }
               }
-
-              // For interfaces, propagate reserved names to all implementing classes.
-              if (clazz.isInterface() && reservedNames != null) {
-                for (DexType implementationType :
-                    subtypingInfo.allImmediateImplementsSubtypes(clazz.type)) {
-                  DexClass implementation = appView.definitionFor(implementationType);
-                  if (implementation != null) {
-                    assert !implementation.isInterface();
-                    getOrCreateReservedFieldNamingState(implementationType)
-                        .includeReservations(reservedNames);
+              if (clazz.isInterface()) {
+                frontierStatesForInterfaces.put(
+                    clazz.type, SetUtils.newIdentityHashSet(reservationState));
+              }
+              // Include all reservations from super frontier states. This will propagate reserved
+              // names for interfaces down to implementing subtypes.
+              for (DexType superType : clazz.allImmediateSupertypes()) {
+                // No need to visit object since there are no fields there.
+                if (superType != appView.dexItemFactory().objectType) {
+                  ReservedFieldNamingState superReservationState =
+                      getOrCreateReservedFieldNamingState(
+                          frontiers.getOrDefault(superType, superType));
+                  if (superReservationState != reservationState) {
+                    reservationState.includeReservations(superReservationState);
+                  }
+                  if (clazz.isProgramClass()) {
+                    DexClass superClass = appView.definitionFor(superType, clazz.asProgramClass());
+                    if (superClass != null && superClass.isInterface()) {
+                      frontierStatesForInterfaces.get(superType).add(reservationState);
+                    }
                   }
                 }
               }
-            });
-
-    // TODO(b/148846065): Consider lazily computing the renaming on actual lookups.
-    appView
-        .appInfo()
-        .forEachReferencedClasspathClass(
-            clazz -> {
-              for (DexEncodedField field : clazz.fields()) {
-                DexString reservedName = strategy.getReservedName(field, clazz);
-                if (reservedName != null && reservedName != field.getReference().name) {
-                  renaming.put(field.getReference(), reservedName);
-                }
+              if (frontier == clazz.type && clazz.isProgramClass()) {
+                patchUpAllIndirectlyImplementingInterfacesFromLibraryAndClassPath(
+                    clazz.asProgramClass(), reservationState);
               }
             });
-
-    propagateReservedFieldNamesUpwards();
   }
 
-  private void propagateReservedFieldNamesUpwards() {
-    BottomUpClassHierarchyTraversal.forProgramClasses(appView, subtypingInfo)
-        .visit(
-            appView.appInfo().classes(),
-            clazz -> {
-              ReservedFieldNamingState reservedNames = getReservedFieldNamingState(clazz.type);
-              if (reservedNames != null) {
-                for (DexType supertype : clazz.allImmediateSupertypes()) {
-                  if (supertype.isProgramType(appView)) {
-                    getOrCreateReservedFieldNamingState(supertype)
-                        .includeReservationsFromBelow(reservedNames);
-                  }
-                }
+  private void patchUpAllIndirectlyImplementingInterfacesFromLibraryAndClassPath(
+      DexProgramClass clazz, ReservedFieldNamingState reservationState) {
+    appView
+        .appInfo()
+        .traverseSuperTypes(
+            clazz,
+            (superType, superClass, isInterface) -> {
+              if (isInterface && superClass.isNotProgramClass()) {
+                frontierStatesForInterfaces.get(superType).add(reservationState);
               }
+              return TraversalContinuation.CONTINUE;
             });
   }
 
@@ -179,8 +198,7 @@
                           .clone();
 
               ReservedFieldNamingState reservedNames =
-                  getOrCreateReservedFieldNamingState(clazz.type);
-              // TODO(b/213041051): This could avoid duplication of strings downwards.
+                  getReservedFieldNamingState(frontiers.getOrDefault(clazz.type, clazz.type));
               FieldNamingState state = parentState.createChildState(reservedNames);
               if (clazz.isProgramClass()) {
                 clazz.asProgramClass().forEachProgramField(field -> renameField(field, state));
@@ -191,7 +209,24 @@
             });
   }
 
+  private void renameFieldsInUnrelatedClasspathClasses() {
+    if (appView.options().getProguardConfiguration().hasApplyMappingFile()) {
+      appView
+          .appInfo()
+          .forEachReferencedClasspathClass(
+              clazz -> {
+                for (DexEncodedField field : clazz.fields()) {
+                  DexString reservedName = strategy.getReservedName(field, clazz);
+                  if (reservedName != null && reservedName != field.getReference().name) {
+                    renaming.put(field.getReference(), reservedName);
+                  }
+                }
+              });
+    }
+  }
+
   private void renameFieldsInInterfaces(Collection<DexClass> interfaces) {
+    // TODO(b/213415674): Only consider interfaces in the hierarchy of classes.
     InterfacePartitioning partitioning = new InterfacePartitioning(this);
     for (Set<DexClass> partition : partitioning.sortedPartitions(interfaces)) {
       renameFieldsInInterfacePartition(partition);
@@ -199,18 +234,24 @@
   }
 
   private void renameFieldsInInterfacePartition(Set<DexClass> partition) {
-    ReservedFieldNamingState reservedNamesInPartition = new ReservedFieldNamingState(appView);
-    for (DexClass clazz : partition) {
-      ReservedFieldNamingState reservedNamesInInterface = getReservedFieldNamingState(clazz.type);
-      if (reservedNamesInInterface != null) {
-        reservedNamesInPartition.includeReservations(reservedNamesInInterface);
-        reservedNamesInPartition.includeReservationsFromBelow(reservedNamesInInterface);
-      }
-    }
-
     ReservedFieldNamingState namesToBeReservedInImplementsSubclasses =
         new ReservedFieldNamingState(appView);
-
+    ReservedFieldNamingState reservedNamesInPartition = new ReservedFieldNamingState(appView);
+    for (DexClass clazz : partition) {
+      ReservedFieldNamingState reservedNamesInInterface =
+          getReservedFieldNamingState(frontiers.getOrDefault(clazz.type, clazz.type));
+      if (reservedNamesInInterface != null) {
+        reservedNamesInPartition.includeReservations(reservedNamesInInterface);
+        Set<ReservedFieldNamingState> reservedFieldNamingStates =
+            frontierStatesForInterfaces.get(clazz.type);
+        assert reservedFieldNamingStates != null;
+        reservedFieldNamingStates.forEach(
+            reservedStates -> {
+              reservedNamesInPartition.includeReservations(reservedStates);
+              reservedStates.setInterfaceMinificationState(namesToBeReservedInImplementsSubclasses);
+            });
+      }
+    }
     FieldNamingState state = new FieldNamingState(appView, strategy, reservedNamesInPartition);
     for (DexClass clazz : partition) {
       if (clazz.isProgramClass()) {
@@ -220,25 +261,11 @@
             .forEachProgramField(
                 field -> {
                   DexString newName = renameField(field, state);
-                  namesToBeReservedInImplementsSubclasses.markReservedDirectly(
+                  namesToBeReservedInImplementsSubclasses.markReserved(
                       newName, field.getReference().name, field.getReference().type);
                 });
       }
     }
-
-    Set<DexType> visited = Sets.newIdentityHashSet();
-    for (DexClass clazz : partition) {
-      for (DexType implementationType : subtypingInfo.allImmediateImplementsSubtypes(clazz.type)) {
-        if (!visited.add(implementationType)) {
-          continue;
-        }
-        DexClass implementation = appView.definitionFor(implementationType);
-        if (implementation != null) {
-          getOrCreateReservedFieldNamingState(implementationType)
-              .setInterfaceMinificationState(namesToBeReservedInImplementsSubclasses);
-        }
-      }
-    }
   }
 
   private DexString renameField(ProgramField field, FieldNamingState state) {
@@ -279,12 +306,12 @@
 
   static class InterfacePartitioning {
 
-    private final FieldNameMinifier minfier;
+    private final FieldNameMinifier minifier;
     private final AppView<AppInfoWithLiveness> appView;
     private final Set<DexType> visited = Sets.newIdentityHashSet();
 
     InterfacePartitioning(FieldNameMinifier minifier) {
-      this.minfier = minifier;
+      this.minifier = minifier;
       appView = minifier.appView;
     }
 
@@ -303,7 +330,7 @@
     }
 
     private Set<DexClass> buildSortedPartition(DexClass src) {
-      Set<DexClass> partition = new TreeSet<>((x, y) -> x.type.compareTo(y.type));
+      Set<DexClass> partition = new TreeSet<>(Comparator.comparing(DexClass::getType));
 
       Deque<DexType> worklist = new ArrayDeque<>();
       worklist.add(src.type);
@@ -325,7 +352,7 @@
         if (clazz.isInterface()) {
           partition.add(clazz);
 
-          for (DexType subtype : minfier.subtypingInfo.allImmediateSubtypes(type)) {
+          for (DexType subtype : minifier.subtypingInfo.allImmediateSubtypes(type)) {
             if (visited.add(subtype)) {
               worklist.add(subtype);
             }
@@ -334,7 +361,7 @@
           if (visited.add(clazz.superType)) {
             worklist.add(clazz.superType);
           }
-          for (DexType subclass : minfier.subtypingInfo.allImmediateExtendsSubtypes(type)) {
+          for (DexType subclass : minifier.subtypingInfo.allImmediateExtendsSubtypes(type)) {
             if (visited.add(subclass)) {
               worklist.add(subclass);
             }
diff --git a/src/main/java/com/android/tools/r8/naming/FieldNamingState.java b/src/main/java/com/android/tools/r8/naming/FieldNamingState.java
index 9e99065..e507112 100644
--- a/src/main/java/com/android/tools/r8/naming/FieldNamingState.java
+++ b/src/main/java/com/android/tools/r8/naming/FieldNamingState.java
@@ -45,10 +45,7 @@
   }
 
   public FieldNamingState createChildState(ReservedFieldNamingState reservedNames) {
-    FieldNamingState childState =
-        new FieldNamingState(appView, strategy, reservedNames, internalStates);
-    childState.includeReservations(this.reservedNames);
-    return childState;
+    return new FieldNamingState(appView, strategy, reservedNames, internalStates);
   }
 
   public DexString getOrCreateNameFor(ProgramField field) {
diff --git a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
index 7e1c7df..cd6c271 100644
--- a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
@@ -20,7 +20,6 @@
 import com.google.common.collect.HashBiMap;
 import com.google.common.collect.ImmutableMap;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.IdentityHashMap;
 import java.util.List;
 import java.util.Map;
@@ -134,7 +133,7 @@
   // The use of a bidirectional map allows us to map a naming state to the type it represents,
   // which is useful for debugging.
   private final BiMap<DexType, MethodReservationState<?>> reservationStates = HashBiMap.create();
-  private final Map<DexType, MethodNamingState<?>> namingStates = new HashMap<>();
+  private final Map<DexType, MethodNamingState<?>> namingStates = new IdentityHashMap<>();
   private final Map<DexType, DexType> frontiers = new IdentityHashMap<>();
 
   private final MethodNamingState<?> rootNamingState;
diff --git a/src/main/java/com/android/tools/r8/naming/ReservedFieldNamingState.java b/src/main/java/com/android/tools/r8/naming/ReservedFieldNamingState.java
index 258646e..2b66d18 100644
--- a/src/main/java/com/android/tools/r8/naming/ReservedFieldNamingState.java
+++ b/src/main/java/com/android/tools/r8/naming/ReservedFieldNamingState.java
@@ -43,8 +43,8 @@
     return internalState == null ? null : internalState.getReservedByName(name);
   }
 
-  void markReservedDirectly(DexString name, DexString originalName, DexType type) {
-    getOrCreateInternalState(type).markReservedDirectly(name, originalName);
+  void markReserved(DexString name, DexString originalName, DexType type) {
+    getOrCreateInternalState(type).markReserved(name, originalName);
   }
 
   void includeReservations(ReservedFieldNamingState reservedNames) {
@@ -54,13 +54,6 @@
     includeInterfaceReservationState(reservedNames);
   }
 
-  void includeReservationsFromBelow(ReservedFieldNamingState reservedNames) {
-    for (Map.Entry<DexType, InternalState> entry : reservedNames.internalStates.entrySet()) {
-      getOrCreateInternalState(entry.getKey()).includeReservationsFromBelow(entry.getValue());
-    }
-    includeInterfaceReservationState(reservedNames);
-  }
-
   private void includeInterfaceReservationState(ReservedFieldNamingState reservedNames) {
     if (reservedNames.interfaceMinificationState != null) {
       assert interfaceMinificationState == null
@@ -71,7 +64,7 @@
 
   void setInterfaceMinificationState(ReservedFieldNamingState namingState) {
     assert namingState != null;
-    assert interfaceMinificationState == null;
+    assert interfaceMinificationState == null || interfaceMinificationState == namingState;
     this.interfaceMinificationState = namingState;
   }
 
@@ -82,25 +75,19 @@
 
   static class InternalState {
 
-    private Map<DexString, DexString> reservedNamesDirect = new IdentityHashMap<>();
-    private Map<DexString, DexString> reservedNamesBelow = new IdentityHashMap<>();
+    private final Map<DexString, DexString> reservedNames = new IdentityHashMap<>();
 
     DexString getReservedByName(DexString name) {
-      DexString reservedBy = reservedNamesDirect.get(name);
-      return reservedBy != null ? reservedBy : reservedNamesBelow.get(name);
+      DexString reservedBy = reservedNames.get(name);
+      return reservedBy != null ? reservedBy : reservedNames.get(name);
     }
 
-    void markReservedDirectly(DexString name, DexString originalName) {
-      reservedNamesDirect.put(name, originalName);
+    void markReserved(DexString name, DexString originalName) {
+      reservedNames.put(name, originalName);
     }
 
     void includeReservations(InternalState state) {
-      reservedNamesDirect.putAll(state.reservedNamesDirect);
-    }
-
-    void includeReservationsFromBelow(InternalState state) {
-      reservedNamesBelow.putAll(state.reservedNamesDirect);
-      reservedNamesBelow.putAll(state.reservedNamesBelow);
+      reservedNames.putAll(state.reservedNames);
     }
   }
 }
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 4764635..5f43dd3 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
@@ -107,7 +107,7 @@
       reprocessingCriteriaCollection.analyzeArgumentUses(method, code);
     } else {
       assert !methodProcessor.isPrimaryMethodProcessor();
-      assert reprocessingCriteriaCollection == null;
+      assert !methodProcessor.isPostMethodProcessor() || reprocessingCriteriaCollection == null;
     }
   }
 
@@ -170,9 +170,10 @@
             .run(stronglyConnectedProgramComponents, affectedClasses::add, executorService, timing);
 
     // Find all the code objects that need reprocessing.
-    new ArgumentPropagatorMethodReprocessingEnqueuer(appView)
+    new ArgumentPropagatorMethodReprocessingEnqueuer(appView, reprocessingCriteriaCollection)
         .enqueueMethodForReprocessing(
             graphLens, postMethodProcessorBuilder, executorService, timing);
+    reprocessingCriteriaCollection = null;
 
     // Finally, apply the graph lens to the program (i.e., remove constant parameters from method
     // definitions).
@@ -209,7 +210,6 @@
             stronglyConnectedProgramComponents,
             interfaceDispatchOutsideProgram)
         .populateOptimizationInfo(converter, executorService, timing);
-    reprocessingCriteriaCollection = null;
     timing.end();
   }
 
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 897aaa8..7c196b1 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
@@ -93,9 +93,11 @@
           return method.toTypeSubstitutedMethod(
               methodReferenceAfterParameterRemoval,
               builder -> {
-                RewrittenPrototypeDescription prototypeChanges =
-                    graphLens.getPrototypeChanges(methodReferenceAfterParameterRemoval);
-                builder.apply(prototypeChanges.createParameterAnnotationsRemover(method));
+                if (graphLens.hasPrototypeChanges(methodReferenceAfterParameterRemoval)) {
+                  RewrittenPrototypeDescription prototypeChanges =
+                      graphLens.getPrototypeChanges(methodReferenceAfterParameterRemoval);
+                  builder.apply(prototypeChanges.createParameterAnnotationsRemover(method));
+                }
               });
         });
   }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java
index eef092d..2839aeb 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java
@@ -502,12 +502,6 @@
     // then use UnknownParameterState.
     if (parameterTypeElement.isClassType()) {
       DynamicType dynamicType = argument.getDynamicType(appView);
-      if (!parameterReprocessingCriteria.shouldReprocessDueToDynamicType()) {
-        dynamicType =
-            parameterReprocessingCriteria.widenDynamicClassType(
-                appView, dynamicType, parameterTypeElement.asClassType());
-      }
-
       DynamicType widenedDynamicType =
           WideningUtils.widenDynamicNonReceiverType(appView, dynamicType, parameterType);
       return abstractValue.isUnknown() && widenedDynamicType.isUnknown()
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 3ed1334..17865e4 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
@@ -35,7 +35,7 @@
   }
 
   public boolean hasPrototypeChanges(DexMethod method) {
-    return method != internalGetPreviousMethodSignature(method);
+    return prototypeChanges.containsKey(method);
   }
 
   public RewrittenPrototypeDescription getPrototypeChanges(DexMethod method) {
@@ -43,6 +43,10 @@
     return prototypeChanges.getOrDefault(method, RewrittenPrototypeDescription.none());
   }
 
+  public boolean isAffected(DexMethod method) {
+    return method != internalGetPreviousMethodSignature(method) || hasPrototypeChanges(method);
+  }
+
   @Override
   protected FieldLookupResult internalDescribeLookupField(FieldLookupResult previous) {
     FieldLookupResult lookupResult = super.internalDescribeLookupField(previous);
@@ -61,8 +65,7 @@
   protected RewrittenPrototypeDescription internalDescribePrototypeChanges(
       RewrittenPrototypeDescription prototypeChanges, DexMethod method) {
     DexMethod previous = internalGetPreviousMethodSignature(method);
-    if (previous == method) {
-      assert !this.prototypeChanges.containsKey(method);
+    if (!hasPrototypeChanges(method)) {
       return prototypeChanges;
     }
     RewrittenPrototypeDescription newPrototypeChanges =
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 f3e9000..d79d23e 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
@@ -19,6 +19,7 @@
 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;
+import com.android.tools.r8.optimize.argumentpropagation.reprocessingcriteria.ArgumentPropagatorReprocessingCriteriaCollection;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Timing;
@@ -34,9 +35,13 @@
 public class ArgumentPropagatorMethodReprocessingEnqueuer {
 
   private final AppView<AppInfoWithLiveness> appView;
+  private final ArgumentPropagatorReprocessingCriteriaCollection reprocessingCriteriaCollection;
 
-  public ArgumentPropagatorMethodReprocessingEnqueuer(AppView<AppInfoWithLiveness> appView) {
+  public ArgumentPropagatorMethodReprocessingEnqueuer(
+      AppView<AppInfoWithLiveness> appView,
+      ArgumentPropagatorReprocessingCriteriaCollection reprocessingCriteriaCollection) {
     this.appView = appView;
+    this.reprocessingCriteriaCollection = reprocessingCriteriaCollection;
   }
 
   /**
@@ -61,7 +66,7 @@
     timing.end();
 
     timing.begin("Enqueue methods with non-trivial info");
-    enqueueMethodsWithNonTrivialOptimizationInfo(methodsToReprocessBuilder);
+    enqueueAffectedCallees(graphLens, methodsToReprocessBuilder);
     timing.end();
 
     timing.begin("Enqueue affected methods");
@@ -73,16 +78,34 @@
     timing.end();
   }
 
-  private void enqueueMethodsWithNonTrivialOptimizationInfo(
+  private void enqueueAffectedCallees(
+      ArgumentPropagatorGraphLens graphLens,
       LongLivedProgramMethodSetBuilder<ProgramMethodSet> methodsToReprocessBuilder) {
     GraphLens currentGraphLens = appView.graphLens();
     for (DexProgramClass clazz : appView.appInfo().classes()) {
       clazz.forEachProgramMethodMatching(
           DexEncodedMethod::hasCode,
           method -> {
+            if (method.getDefinition().getCode().isSharedCodeObject()) {
+              return;
+            }
+
+            if (graphLens != null) {
+              DexMethod rewrittenMethodSignature =
+                  graphLens.internalGetNextMethodSignature(method.getReference());
+              if (graphLens.hasPrototypeChanges(rewrittenMethodSignature)) {
+                assert !appView.appInfo().isNeverReprocessMethod(method);
+                methodsToReprocessBuilder.add(method, currentGraphLens);
+                appView.testing().callSiteOptimizationInfoInspector.accept(method);
+                return;
+              }
+            }
+
             CallSiteOptimizationInfo callSiteOptimizationInfo =
                 method.getOptimizationInfo().getArgumentInfos();
-            if (callSiteOptimizationInfo.isConcreteCallSiteOptimizationInfo()
+            if (reprocessingCriteriaCollection
+                    .getReprocessingCriteria(method)
+                    .shouldReprocess(appView, method, callSiteOptimizationInfo)
                 && !appView.appInfo().isNeverReprocessMethod(method)) {
               methodsToReprocessBuilder.add(method, currentGraphLens);
               appView.testing().callSiteOptimizationInfoInspector.accept(method);
@@ -99,6 +122,7 @@
       LongLivedProgramMethodSetBuilder<ProgramMethodSet> methodsToReprocessBuilder,
       ExecutorService executorService)
       throws ExecutionException {
+    GraphLens currentGraphLens = appView.graphLens();
     Collection<List<ProgramMethod>> methodsToReprocess =
         ThreadUtils.processItemsWithResults(
             appView.appInfo().classes(),
@@ -107,12 +131,7 @@
               clazz.forEachProgramMethodMatching(
                   DexEncodedMethod::hasCode,
                   method -> {
-                    if (graphLens.internalGetNextMethodSignature(method.getReference())
-                        != method.getReference()) {
-                      if (!method.getOptimizationInfo().hasBeenInlinedIntoSingleCallSite()) {
-                        methodsToReprocessInClass.add(method);
-                      }
-                    } else {
+                    if (!methodsToReprocessBuilder.contains(method, currentGraphLens)) {
                       AffectedMethodUseRegistry registry =
                           new AffectedMethodUseRegistry(appView, method, graphLens);
                       if (method.registerCodeReferencesWithResult(registry)) {
@@ -124,10 +143,9 @@
               return methodsToReprocessInClass;
             },
             executorService);
-    GraphLens currentGraphLens = appView.graphLens();
     methodsToReprocess.forEach(
-        methodsToReprocessForClass ->
-            methodsToReprocessBuilder.addAll(methodsToReprocessForClass, currentGraphLens));
+        methodsToReprocessInClass ->
+            methodsToReprocessBuilder.addAll(methodsToReprocessInClass, currentGraphLens));
   }
 
   static class AffectedMethodUseRegistry extends UseRegistryWithResult<Boolean, ProgramMethod> {
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java
index 94da691..f9bfde1 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java
@@ -14,6 +14,8 @@
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.type.DynamicType;
 import com.android.tools.r8.ir.analysis.type.Nullability;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.ir.analysis.value.AbstractValue;
 import com.android.tools.r8.ir.conversion.IRConverter;
 import com.android.tools.r8.ir.optimize.info.ConcreteCallSiteOptimizationInfo;
 import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo;
@@ -22,17 +24,16 @@
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteClassTypeParameterState;
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteMethodState;
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteMonomorphicMethodState;
-import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteMonomorphicMethodStateOrUnknown;
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteParameterState;
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcretePrimitiveTypeParameterState;
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodState;
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodStateCollectionByReference;
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.ParameterState;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.StateCloner;
 import com.android.tools.r8.optimize.argumentpropagation.propagation.InParameterFlowPropagator;
 import com.android.tools.r8.optimize.argumentpropagation.propagation.InterfaceMethodArgumentPropagator;
 import com.android.tools.r8.optimize.argumentpropagation.propagation.VirtualDispatchMethodArgumentPropagator;
 import com.android.tools.r8.optimize.argumentpropagation.reprocessingcriteria.ArgumentPropagatorReprocessingCriteriaCollection;
-import com.android.tools.r8.optimize.argumentpropagation.reprocessingcriteria.MethodReprocessingCriteria;
 import com.android.tools.r8.optimize.argumentpropagation.utils.WideningUtils;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.ListUtils;
@@ -45,7 +46,6 @@
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.function.BiConsumer;
-import java.util.stream.IntStream;
 
 /**
  * Propagates the argument flow information collected by the {@link ArgumentPropagatorCodeScanner}.
@@ -187,6 +187,14 @@
 
     ConcreteMonomorphicMethodState monomorphicMethodState = concreteMethodState.asMonomorphic();
 
+    // Widen the dynamic type information so that we don't store any trivial dynamic types.
+    // Note that all dynamic types are already being widened when the method states are created, but
+    // this does not guarantee that they are non-trivial at this point, since we may refine the
+    // object allocation info collection during the primary optimization pass.
+    if (!widenDynamicTypes(method, monomorphicMethodState)) {
+      return;
+    }
+
     // Verify that there is no parameter with bottom info.
     assert monomorphicMethodState.getParameterStates().stream().noneMatch(ParameterState::isBottom);
 
@@ -196,50 +204,18 @@
         .map(ParameterState::asConcrete)
         .noneMatch(ConcreteParameterState::hasInParameters);
 
-    // Verify that the dynamic type information is correct.
-    assert IntStream.range(0, monomorphicMethodState.getParameterStates().size())
-        .filter(
-            index -> {
-              ParameterState parameterState = monomorphicMethodState.getParameterState(index);
-              return parameterState.isConcrete() && parameterState.asConcrete().isClassParameter();
-            })
-        .allMatch(
-            index -> {
-              DynamicType dynamicType =
-                  monomorphicMethodState
-                      .getParameterState(index)
-                      .asConcrete()
-                      .asClassParameter()
-                      .getDynamicType();
-              DexType staticType = method.getArgumentType(index);
-              assert dynamicType.isUnknown()
-                  || !WideningUtils.widenDynamicNonReceiverType(appView, dynamicType, staticType)
-                      .isUnknown();
-              return true;
-            });
-
-    // If we have any reprocessing criteria for the given method, check that they are satisfied
-    // before reenqueing.
-    MethodReprocessingCriteria reprocessingCriteria =
-        reprocessingCriteriaCollection.getReprocessingCriteria(method);
-    ConcreteMonomorphicMethodStateOrUnknown widenedMethodState =
-        reprocessingCriteria.widenMethodState(appView, method, monomorphicMethodState);
-    if (widenedMethodState.isUnknown()) {
-      return;
-    }
-
-    ConcreteMonomorphicMethodState finalMethodState = widenedMethodState.asMonomorphic();
     getSimpleFeedback()
         .setArgumentInfos(
             method,
-            ConcreteCallSiteOptimizationInfo.fromMethodState(appView, method, finalMethodState));
+            ConcreteCallSiteOptimizationInfo.fromMethodState(
+                appView, method, monomorphicMethodState));
 
     // Strengthen the return value of the method if the method is known to return one of the
     // arguments.
     MethodOptimizationInfo optimizationInfo = method.getOptimizationInfo();
     if (optimizationInfo.returnsArgument()) {
       ParameterState returnedArgumentState =
-          finalMethodState.getParameterState(optimizationInfo.getReturnedArgument());
+          monomorphicMethodState.getParameterState(optimizationInfo.getReturnedArgument());
       OptimizationFeedback.getSimple()
           .methodReturnsAbstractValue(
               method.getDefinition(), appView, returnedArgumentState.getAbstractValue(appView));
@@ -328,4 +304,44 @@
           appView.abstractValueFactory().createZeroValue());
     }
   }
+
+  private boolean widenDynamicTypes(
+      ProgramMethod method, ConcreteMonomorphicMethodState methodState) {
+    for (int argumentIndex = 0;
+        argumentIndex < methodState.getParameterStates().size();
+        argumentIndex++) {
+      ConcreteParameterState parameterState =
+          methodState.getParameterState(argumentIndex).asConcrete();
+      if (parameterState == null || !parameterState.isClassParameter()) {
+        continue;
+      }
+      DynamicType dynamicType = parameterState.asClassParameter().getDynamicType();
+      DexType staticType = method.getArgumentType(argumentIndex);
+      if (shouldWidenDynamicTypeToUnknown(dynamicType, staticType)) {
+        methodState.setParameterState(
+            argumentIndex,
+            parameterState.mutableJoin(
+                appView,
+                new ConcreteClassTypeParameterState(AbstractValue.bottom(), DynamicType.unknown()),
+                staticType,
+                StateCloner.getIdentity()));
+      }
+    }
+    return !methodState.isEffectivelyUnknown();
+  }
+
+  private boolean shouldWidenDynamicTypeToUnknown(DynamicType dynamicType, DexType staticType) {
+    if (dynamicType.isUnknown()) {
+      return false;
+    }
+    if (WideningUtils.widenDynamicNonReceiverType(appView, dynamicType, staticType).isUnknown()) {
+      return true;
+    }
+    TypeElement staticTypeElement = staticType.toTypeElement(appView);
+    TypeElement dynamicUpperBoundType = dynamicType.getDynamicUpperBoundType(staticTypeElement);
+    if (!dynamicUpperBoundType.lessThanOrEqual(staticTypeElement, appView)) {
+      return true;
+    }
+    return false;
+  }
 }
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 6b94973..4890dd9 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
@@ -36,7 +36,6 @@
 import com.android.tools.r8.shaking.KeepFieldInfo;
 import com.android.tools.r8.utils.AccessUtils;
 import com.android.tools.r8.utils.BooleanBox;
-import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.IntBox;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.Pair;
@@ -47,7 +46,11 @@
 import com.android.tools.r8.utils.collections.ProgramMethodSet;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Sets;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceMaps;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
 import it.unimi.dsi.fastutil.ints.IntArraySet;
+import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
 import it.unimi.dsi.fastutil.ints.IntSet;
 import it.unimi.dsi.fastutil.ints.IntSets;
 import java.util.ArrayList;
@@ -57,11 +60,13 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.function.Consumer;
+import java.util.function.IntFunction;
 import java.util.function.IntPredicate;
 
 public class ArgumentPropagatorProgramOptimizer {
@@ -69,22 +74,45 @@
   static class AllowedPrototypeChanges {
 
     private static final AllowedPrototypeChanges EMPTY =
-        new AllowedPrototypeChanges(false, IntSets.EMPTY_SET);
+        new AllowedPrototypeChanges(null, Int2ReferenceMaps.emptyMap(), IntSets.EMPTY_SET);
 
-    boolean canRewriteToVoid;
+    DexType newReturnType;
+    Int2ReferenceMap<DexType> newParameterTypes;
     IntSet removableParameterIndices;
 
-    AllowedPrototypeChanges(boolean canRewriteToVoid, IntSet removableParameterIndices) {
-      this.canRewriteToVoid = canRewriteToVoid;
+    AllowedPrototypeChanges(
+        DexType newReturnType,
+        Int2ReferenceMap<DexType> newParameterTypes,
+        IntSet removableParameterIndices) {
+      this.newReturnType = newReturnType;
+      this.newParameterTypes = newParameterTypes;
       this.removableParameterIndices = removableParameterIndices;
     }
 
     public static AllowedPrototypeChanges create(RewrittenPrototypeDescription prototypeChanges) {
-      return prototypeChanges.isEmpty()
-          ? empty()
-          : new AllowedPrototypeChanges(
-              prototypeChanges.hasBeenChangedToReturnVoid(),
-              prototypeChanges.getArgumentInfoCollection().getKeys());
+      if (prototypeChanges.isEmpty()) {
+        return empty();
+      }
+      DexType newReturnType =
+          prototypeChanges.hasRewrittenReturnInfo()
+              ? prototypeChanges.getRewrittenReturnInfo().getNewType()
+              : null;
+      Int2ReferenceMap<DexType> newParameterTypes = new Int2ReferenceOpenHashMap<>();
+      IntSet removableParameterIndices = new IntOpenHashSet();
+      prototypeChanges
+          .getArgumentInfoCollection()
+          .forEach(
+              (argumentIndex, argumentInfo) -> {
+                if (argumentInfo.isRemovedArgumentInfo()) {
+                  removableParameterIndices.add(argumentIndex);
+                } else {
+                  assert argumentInfo.isRewrittenTypeInfo();
+                  RewrittenTypeInfo rewrittenTypeInfo = argumentInfo.asRewrittenTypeInfo();
+                  newParameterTypes.put(argumentIndex, rewrittenTypeInfo.getNewType());
+                }
+              });
+      return new AllowedPrototypeChanges(
+          newReturnType, newParameterTypes, removableParameterIndices);
     }
 
     public static AllowedPrototypeChanges empty() {
@@ -93,7 +121,7 @@
 
     @Override
     public int hashCode() {
-      return BooleanUtils.intValue(canRewriteToVoid) | (removableParameterIndices.hashCode() << 1);
+      return Objects.hash(newReturnType, newParameterTypes, removableParameterIndices);
     }
 
     @Override
@@ -102,7 +130,8 @@
         return false;
       }
       AllowedPrototypeChanges other = (AllowedPrototypeChanges) obj;
-      return canRewriteToVoid == other.canRewriteToVoid
+      return newReturnType == other.newReturnType
+          && newParameterTypes.equals(other.newParameterTypes)
           && removableParameterIndices.equals(other.removableParameterIndices);
     }
   }
@@ -308,31 +337,46 @@
               return;
             }
 
-            // Find the parameters that are constant or unused in all methods.
+            if (containsImmediateInterfaceOfInstantiatedLambda(methods)) {
+              return;
+            }
+
+            // Find the parameters that are either (i) the same constant, (ii) all unused, or (iii)
+            // all possible to strengthen to the same stronger type, in all methods.
+            Int2ReferenceMap<DexType> newParameterTypes = new Int2ReferenceOpenHashMap<>();
             IntSet removableVirtualMethodParametersInAllMethods = new IntArraySet();
             for (int parameterIndex = 1;
                 parameterIndex < signature.getProto().getArity() + 1;
                 parameterIndex++) {
-              if (canRemoveParameterFromVirtualMethods(parameterIndex, methods)) {
+              if (canRemoveParameterFromVirtualMethods(methods, parameterIndex)) {
                 removableVirtualMethodParametersInAllMethods.add(parameterIndex);
+              } else {
+                DexType newParameterType =
+                    getNewParameterTypeForVirtualMethods(methods, parameterIndex);
+                if (newParameterType != null) {
+                  newParameterTypes.put(parameterIndex, newParameterType);
+                }
               }
             }
 
             // If any prototype changes can be made, record it.
             SingleValue returnValueForVirtualMethods =
-                getReturnValueForVirtualMethods(signature, methods);
-            boolean canRewriteVirtualMethodsToVoid = returnValueForVirtualMethods != null;
-            if (canRewriteVirtualMethodsToVoid
+                getReturnValueForVirtualMethods(methods, signature);
+            DexType newReturnType =
+                getNewReturnTypeForVirtualMethods(methods, returnValueForVirtualMethods);
+            if (newReturnType != null
+                || !newParameterTypes.isEmpty()
                 || !removableVirtualMethodParametersInAllMethods.isEmpty()) {
               allowedPrototypeChangesForVirtualMethods.put(
                   signature,
                   new AllowedPrototypeChanges(
-                      canRewriteVirtualMethodsToVoid,
+                      newReturnType,
+                      newParameterTypes,
                       removableVirtualMethodParametersInAllMethods));
             }
 
             // Also record the found return value for abstract virtual methods.
-            if (canRewriteVirtualMethodsToVoid) {
+            if (newReturnType == dexItemFactory.voidType) {
               for (ProgramMethod method : methods) {
                 if (method.getAccessFlags().isAbstract()) {
                   returnValuesForVirtualMethods.put(method, returnValueForVirtualMethods);
@@ -370,7 +414,7 @@
     }
 
     private SingleValue getReturnValueForVirtualMethods(
-        DexMethodSignature signature, ProgramMethodSet methods) {
+        ProgramMethodSet methods, DexMethodSignature signature) {
       if (signature.getReturnType().isVoidType()) {
         return null;
       }
@@ -378,14 +422,6 @@
       SingleValue returnValue = null;
       for (ProgramMethod method : methods) {
         if (method.getDefinition().isAbstract()) {
-          DexProgramClass holder = method.getHolder();
-          if (holder.isInterface()) {
-            ObjectAllocationInfoCollection objectAllocationInfoCollection =
-                appView.appInfo().getObjectAllocationInfoCollection();
-            if (objectAllocationInfoCollection.isImmediateInterfaceOfInstantiatedLambda(holder)) {
-              return null;
-            }
-          }
           // OK, this can be rewritten to have void return type.
           continue;
         }
@@ -403,18 +439,24 @@
       return returnValue;
     }
 
+    private boolean containsImmediateInterfaceOfInstantiatedLambda(ProgramMethodSet methods) {
+      for (ProgramMethod method : methods) {
+        DexProgramClass holder = method.getHolder();
+        if (holder.isInterface()) {
+          ObjectAllocationInfoCollection objectAllocationInfoCollection =
+              appView.appInfo().getObjectAllocationInfoCollection();
+          if (objectAllocationInfoCollection.isImmediateInterfaceOfInstantiatedLambda(holder)) {
+            return true;
+          }
+        }
+      }
+      return false;
+    }
+
     private boolean canRemoveParameterFromVirtualMethods(
-        int parameterIndex, ProgramMethodSet methods) {
+        ProgramMethodSet methods, int parameterIndex) {
       for (ProgramMethod method : methods) {
         if (method.getDefinition().isAbstract()) {
-          DexProgramClass holder = method.getHolder();
-          if (holder.isInterface()) {
-            ObjectAllocationInfoCollection objectAllocationInfoCollection =
-                appView.appInfo().getObjectAllocationInfoCollection();
-            if (objectAllocationInfoCollection.isImmediateInterfaceOfInstantiatedLambda(holder)) {
-              return false;
-            }
-          }
           // OK, this parameter can be removed.
           continue;
         }
@@ -435,6 +477,48 @@
       return true;
     }
 
+    private DexType getNewReturnTypeForVirtualMethods(
+        ProgramMethodSet methods, SingleValue returnValue) {
+      if (returnValue != null) {
+        return dexItemFactory.voidType;
+      }
+      DexType newReturnType = null;
+      for (ProgramMethod method : methods) {
+        if (method.getDefinition().isAbstract()) {
+          // OK, this method can have any return type.
+          continue;
+        }
+        DexType newReturnTypeForMethod = getNewReturnType(method, null);
+        if (newReturnTypeForMethod == null
+            || (newReturnType != null && newReturnType != newReturnTypeForMethod)) {
+          return null;
+        }
+        newReturnType = newReturnTypeForMethod;
+      }
+      assert newReturnType == null || newReturnType != methods.getFirst().getReturnType();
+      return newReturnType;
+    }
+
+    private DexType getNewParameterTypeForVirtualMethods(
+        ProgramMethodSet methods, int parameterIndex) {
+      DexType newParameterType = null;
+      for (ProgramMethod method : methods) {
+        if (method.getAccessFlags().isAbstract()) {
+          // OK, this parameter can have any type.
+          continue;
+        }
+        DexType newParameterTypeForMethod = getNewParameterType(method, parameterIndex);
+        if (newParameterTypeForMethod == null
+            || (newParameterType != null && newParameterType != newParameterTypeForMethod)) {
+          return null;
+        }
+        newParameterType = newParameterTypeForMethod;
+      }
+      assert newParameterType == null
+          || newParameterType != methods.getFirst().getArgumentType(parameterIndex);
+      return newParameterType;
+    }
+
     // Returns true if the class was changed as a result of argument propagation.
     private boolean visitClass(
         DexProgramClass clazz,
@@ -654,84 +738,107 @@
       }
 
       IntSet removableParameterIndices = allowedPrototypeChanges.removableParameterIndices;
-
+      Int2ReferenceMap<DexType> newParameterTypes = allowedPrototypeChanges.newParameterTypes;
       if (method.getAccessFlags().isAbstract()) {
-        // Treat the parameters as unused.
-        ArgumentInfoCollection.Builder removableParametersBuilder =
-            ArgumentInfoCollection.builder();
-        for (int removableParameterIndex : removableParameterIndices) {
-          removableParametersBuilder.addArgumentInfo(
-              removableParameterIndex,
-              RemovedArgumentInfo.builder()
-                  .setType(method.getArgumentType(removableParameterIndex))
-                  .build());
-        }
-        return RewrittenPrototypeDescription.create(
-            Collections.emptyList(),
-            computeReturnChangesForMethod(method, allowedPrototypeChanges.canRewriteToVoid),
-            removableParametersBuilder.build());
+        return computePrototypeChangesForAbstractVirtualMethod(
+            method,
+            allowedPrototypeChanges.newReturnType,
+            newParameterTypes,
+            removableParameterIndices);
       }
 
       RewrittenPrototypeDescription prototypeChanges =
           computePrototypeChangesForMethod(
               method,
-              allowedPrototypeChanges.canRewriteToVoid,
+              allowedPrototypeChanges.newReturnType,
+              newParameterTypes::get,
               removableParameterIndices::contains);
-      assert prototypeChanges.getArgumentInfoCollection().size()
+      assert prototypeChanges.getArgumentInfoCollection().numberOfRemovedArguments()
           == removableParameterIndices.size();
       return prototypeChanges;
     }
 
-    private RewrittenPrototypeDescription computePrototypeChangesForMethod(ProgramMethod method) {
-      return computePrototypeChangesForMethod(method, true, parameterIndex -> true);
-    }
-
-    private RewrittenPrototypeDescription computePrototypeChangesForMethod(
+    private RewrittenPrototypeDescription computePrototypeChangesForAbstractVirtualMethod(
         ProgramMethod method,
-        boolean allowToVoidRewriting,
-        IntPredicate removableParameterIndices) {
-      return RewrittenPrototypeDescription.create(
-          Collections.emptyList(),
-          computeReturnChangesForMethod(method, allowToVoidRewriting),
-          computeParameterChangesForMethod(method, removableParameterIndices));
-    }
-
-    private ArgumentInfoCollection computeParameterChangesForMethod(
-        ProgramMethod method, IntPredicate removableParameterIndices) {
-      ConcreteCallSiteOptimizationInfo optimizationInfo =
-          method.getOptimizationInfo().getArgumentInfos().asConcreteCallSiteOptimizationInfo();
-      if (optimizationInfo == null) {
-        return ArgumentInfoCollection.empty();
-      }
-
-      ArgumentInfoCollection.Builder removableParametersBuilder = ArgumentInfoCollection.builder();
-      for (int argumentIndex = method.getDefinition().getFirstNonReceiverArgumentIndex();
+        DexType newReturnType,
+        Int2ReferenceMap<DexType> newParameterTypes,
+        IntSet removableParameterIndices) {
+      // Treat the parameters as unused.
+      ArgumentInfoCollection.Builder argumentInfoCollectionBuilder =
+          ArgumentInfoCollection.builder();
+      for (int argumentIndex = 0;
           argumentIndex < method.getDefinition().getNumberOfArguments();
           argumentIndex++) {
-        if (!removableParameterIndices.test(argumentIndex)) {
-          continue;
-        }
-        AbstractValue abstractValue = optimizationInfo.getAbstractArgumentValue(argumentIndex);
-        if (abstractValue.isSingleValue()
-            && abstractValue.asSingleValue().isMaterializableInContext(appView, method)) {
-          removableParametersBuilder.addArgumentInfo(
+        if (removableParameterIndices.contains(argumentIndex)) {
+          argumentInfoCollectionBuilder.addArgumentInfo(
               argumentIndex,
-              RemovedArgumentInfo.builder()
-                  .setSingleValue(abstractValue.asSingleValue())
-                  .setType(method.getArgumentType(argumentIndex))
+              RemovedArgumentInfo.builder().setType(method.getArgumentType(argumentIndex)).build());
+        } else if (newParameterTypes.containsKey(argumentIndex)) {
+          DexType newParameterType = newParameterTypes.get(argumentIndex);
+          argumentInfoCollectionBuilder.addArgumentInfo(
+              argumentIndex,
+              RewrittenTypeInfo.builder()
+                  .setCastType(newParameterType)
+                  .setOldType(method.getArgumentType(argumentIndex))
+                  .setNewType(newParameterType)
                   .build());
         }
       }
-      return removableParametersBuilder.build();
+      return RewrittenPrototypeDescription.create(
+          Collections.emptyList(),
+          computeReturnChangesForMethod(method, newReturnType),
+          argumentInfoCollectionBuilder.build());
     }
 
-    private RewrittenTypeInfo computeReturnChangesForMethod(
-        ProgramMethod method, boolean allowToVoidRewriting) {
-      if (!allowToVoidRewriting) {
-        assert !returnValuesForVirtualMethods.containsKey(method);
+    private RewrittenPrototypeDescription computePrototypeChangesForMethod(ProgramMethod method) {
+      IntFunction<DexType> parameterIndexToParameterType =
+          appView.getKeepInfo(method).isParameterTypeStrengtheningAllowed(options)
+              ? parameterIndex -> getNewParameterType(method, parameterIndex)
+              : parameterIndex -> null;
+      return computePrototypeChangesForMethod(
+          method, getNewReturnType(method), parameterIndexToParameterType, parameterIndex -> true);
+    }
+
+    private DexType getNewReturnType(ProgramMethod method) {
+      return getNewReturnType(method, getReturnValue(method));
+    }
+
+    private DexType getNewReturnType(ProgramMethod method, SingleValue returnValue) {
+      DexType staticType = method.getReturnType();
+      if (staticType.isVoidType()
+          || !appView.getKeepInfo(method).isReturnTypeStrengtheningAllowed(options)) {
         return null;
       }
+      if (returnValue != null) {
+        return dexItemFactory.voidType;
+      }
+      TypeElement newReturnTypeElement =
+          method
+              .getOptimizationInfo()
+              .getDynamicType()
+              .getDynamicUpperBoundType(staticType.toTypeElement(appView));
+      assert newReturnTypeElement.isTop()
+          || newReturnTypeElement.lessThanOrEqual(staticType.toTypeElement(appView), appView);
+      if (!newReturnTypeElement.isClassType()) {
+        assert newReturnTypeElement.isArrayType()
+            || newReturnTypeElement.isNullType()
+            || newReturnTypeElement.isTop();
+        return null;
+      }
+      DexType newReturnType = newReturnTypeElement.asClassType().toDexType(dexItemFactory);
+      if (newReturnType == staticType) {
+        return null;
+      }
+      if (!appView.appInfo().isSubtype(newReturnType, staticType)) {
+        return null;
+      }
+      return AccessUtils.isAccessibleInSameContextsAs(
+              newReturnType, method.getReturnType(), appView)
+          ? newReturnType
+          : null;
+    }
 
+    private SingleValue getReturnValue(ProgramMethod method) {
       AbstractValue returnValue;
       if (method.getReturnType().isAlwaysNull(appView)) {
         returnValue = appView.abstractValueFactory().createNullValue();
@@ -743,13 +850,118 @@
         returnValue = method.getOptimizationInfo().getAbstractReturnValue();
       }
 
-      if (!returnValue.isSingleValue()
-          || !returnValue.asSingleValue().isMaterializableInAllContexts(appView)) {
+      return returnValue.isSingleValue()
+              && returnValue.asSingleValue().isMaterializableInAllContexts(appView)
+          ? returnValue.asSingleValue()
+          : null;
+    }
+
+    private DexType getNewParameterType(ProgramMethod method, int parameterIndex) {
+      if (!appView.getKeepInfo(method).isParameterTypeStrengtheningAllowed(options)) {
         return null;
       }
+      DexType staticType = method.getArgumentType(parameterIndex);
+      if (!staticType.isClassType()) {
+        return null;
+      }
+      DynamicType dynamicType =
+          method.getOptimizationInfo().getArgumentInfos().getDynamicType(parameterIndex);
+      if (dynamicType == null || dynamicType.isUnknown()) {
+        return null;
+      }
+      TypeElement staticTypeElement = staticType.toTypeElement(appView);
+      TypeElement dynamicUpperBoundType = dynamicType.getDynamicUpperBoundType(staticTypeElement);
+      assert dynamicUpperBoundType.lessThanOrEqual(staticTypeElement, appView);
+      assert dynamicUpperBoundType.isReferenceType();
+      if (dynamicUpperBoundType.isNullType()) {
+        return null;
+      }
+      if (dynamicUpperBoundType.isArrayType()) {
+        return null;
+      }
+      assert dynamicUpperBoundType.isClassType();
+      DexType newParameterType = dynamicUpperBoundType.asClassType().toDexType(dexItemFactory);
+      if (newParameterType == staticType) {
+        return null;
+      }
+      if (!appView.appInfo().isSubtype(newParameterType, staticType)) {
+        return null;
+      }
+      return AccessUtils.isAccessibleInSameContextsAs(newParameterType, staticType, appView)
+          ? newParameterType
+          : null;
+    }
 
-      SingleValue singleValue = returnValue.asSingleValue();
-      return RewrittenTypeInfo.toVoid(method.getReturnType(), dexItemFactory, singleValue);
+    private RewrittenPrototypeDescription computePrototypeChangesForMethod(
+        ProgramMethod method,
+        DexType newReturnType,
+        IntFunction<DexType> newParameterTypes,
+        IntPredicate removableParameterIndices) {
+      return RewrittenPrototypeDescription.create(
+          Collections.emptyList(),
+          computeReturnChangesForMethod(method, newReturnType),
+          computeParameterChangesForMethod(method, newParameterTypes, removableParameterIndices));
+    }
+
+    private ArgumentInfoCollection computeParameterChangesForMethod(
+        ProgramMethod method,
+        IntFunction<DexType> newParameterTypes,
+        IntPredicate removableParameterIndices) {
+      ConcreteCallSiteOptimizationInfo optimizationInfo =
+          method.getOptimizationInfo().getArgumentInfos().asConcreteCallSiteOptimizationInfo();
+      if (optimizationInfo == null) {
+        return ArgumentInfoCollection.empty();
+      }
+
+      ArgumentInfoCollection.Builder parameterChangesBuilder = ArgumentInfoCollection.builder();
+      for (int argumentIndex = method.getDefinition().getFirstNonReceiverArgumentIndex();
+          argumentIndex < method.getDefinition().getNumberOfArguments();
+          argumentIndex++) {
+        if (removableParameterIndices.test(argumentIndex)) {
+          AbstractValue abstractValue = optimizationInfo.getAbstractArgumentValue(argumentIndex);
+          if (abstractValue.isSingleValue()
+              && abstractValue.asSingleValue().isMaterializableInContext(appView, method)) {
+            parameterChangesBuilder.addArgumentInfo(
+                argumentIndex,
+                RemovedArgumentInfo.builder()
+                    .setSingleValue(abstractValue.asSingleValue())
+                    .setType(method.getArgumentType(argumentIndex))
+                    .build());
+            continue;
+          }
+        }
+
+        DexType dynamicType = newParameterTypes.apply(argumentIndex);
+        if (dynamicType != null) {
+          DexType staticType = method.getArgumentType(argumentIndex);
+          assert dynamicType != staticType;
+          parameterChangesBuilder.addArgumentInfo(
+              argumentIndex,
+              RewrittenTypeInfo.builder()
+                  .setCastType(dynamicType)
+                  .setOldType(staticType)
+                  .setNewType(dynamicType)
+                  .build());
+        }
+      }
+      return parameterChangesBuilder.build();
+    }
+
+    private RewrittenTypeInfo computeReturnChangesForMethod(
+        ProgramMethod method, DexType newReturnType) {
+      if (newReturnType == null) {
+        assert !returnValuesForVirtualMethods.containsKey(method);
+        return null;
+      }
+      assert newReturnType != method.getReturnType();
+      return RewrittenTypeInfo.builder()
+          .applyIf(
+              newReturnType == dexItemFactory.voidType,
+              builder -> builder.setSingleValue(getReturnValue(method)))
+          .setCastType(newReturnType)
+          .setOldType(method.getReturnType())
+          .setNewType(newReturnType)
+          .build();
     }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteMonomorphicMethodState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteMonomorphicMethodState.java
index c98c383..d32df00 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteMonomorphicMethodState.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteMonomorphicMethodState.java
@@ -21,9 +21,8 @@
   public ConcreteMonomorphicMethodState(List<ParameterState> parameterStates) {
     assert Streams.stream(Iterables.skip(parameterStates, 1))
         .noneMatch(x -> x.isConcrete() && x.asConcrete().isReceiverParameter());
-    assert Iterables.any(parameterStates, parameterState -> !parameterState.isUnknown())
-        : "Must use UnknownMethodState instead";
     this.parameterStates = parameterStates;
+    assert !isEffectivelyUnknown() : "Must use UnknownMethodState instead";
   }
 
   public ParameterState getParameterState(int index) {
@@ -34,6 +33,10 @@
     return parameterStates;
   }
 
+  public boolean isEffectivelyUnknown() {
+    return Iterables.all(parameterStates, ParameterState::isUnknown);
+  }
+
   @Override
   public ConcreteMonomorphicMethodState mutableCopy() {
     List<ParameterState> copiedParametersStates = new ArrayList<>(size());
@@ -75,10 +78,7 @@
           || !parameterStates.get(argumentIndex).asConcrete().isReceiverParameter();
     }
 
-    if (Iterables.all(parameterStates, ParameterState::isUnknown)) {
-      return unknown();
-    }
-    return this;
+    return isEffectivelyUnknown() ? unknown() : this;
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/AlwaysFalseParameterReprocessingCriteria.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/AlwaysFalseParameterReprocessingCriteria.java
index 90d7e4a..cfec454 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/AlwaysFalseParameterReprocessingCriteria.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/AlwaysFalseParameterReprocessingCriteria.java
@@ -6,7 +6,8 @@
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteParameterState;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.optimize.info.ConcreteCallSiteOptimizationInfo;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 
 /**
@@ -34,7 +35,9 @@
   @Override
   public boolean shouldReprocess(
       AppView<AppInfoWithLiveness> appView,
-      ConcreteParameterState parameterState,
+      ProgramMethod method,
+      ConcreteCallSiteOptimizationInfo methodState,
+      int parameterIndex,
       DexType parameterType) {
     return false;
   }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/AlwaysTrueParameterReprocessingCriteria.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/AlwaysTrueParameterReprocessingCriteria.java
index 72d382f..ff85df7 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/AlwaysTrueParameterReprocessingCriteria.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/AlwaysTrueParameterReprocessingCriteria.java
@@ -6,7 +6,8 @@
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteParameterState;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.optimize.info.ConcreteCallSiteOptimizationInfo;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 
 /**
@@ -32,7 +33,9 @@
   @Override
   public boolean shouldReprocess(
       AppView<AppInfoWithLiveness> appView,
-      ConcreteParameterState parameterState,
+      ProgramMethod method,
+      ConcreteCallSiteOptimizationInfo methodState,
+      int parameterIndex,
       DexType parameterType) {
     return true;
   }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/MethodReprocessingCriteria.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/MethodReprocessingCriteria.java
index 658383f..69e0354 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/MethodReprocessingCriteria.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/MethodReprocessingCriteria.java
@@ -7,13 +7,9 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteMonomorphicMethodState;
-import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteMonomorphicMethodStateOrUnknown;
-import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodState;
-import com.android.tools.r8.optimize.argumentpropagation.codescanner.ParameterState;
-import com.android.tools.r8.optimize.argumentpropagation.codescanner.UnknownParameterState;
+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.google.common.collect.Iterables;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
 
@@ -43,35 +39,29 @@
         parameterIndex, ParameterReprocessingCriteria.alwaysReprocess());
   }
 
-  public ConcreteMonomorphicMethodStateOrUnknown widenMethodState(
+  public boolean shouldReprocess(
       AppView<AppInfoWithLiveness> appView,
       ProgramMethod method,
-      ConcreteMonomorphicMethodState methodState) {
-    for (int parameterIndex = 0; parameterIndex < methodState.size(); parameterIndex++) {
-      ParameterState parameterState = methodState.getParameterState(parameterIndex);
-      assert !parameterState.isBottom();
-      if (parameterState.isUnknown()) {
-        continue;
+      CallSiteOptimizationInfo methodState) {
+    if (!methodState.isConcreteCallSiteOptimizationInfo()) {
+      return false;
+    }
+    ConcreteCallSiteOptimizationInfo concreteMethodState =
+        methodState.asConcreteCallSiteOptimizationInfo();
+    for (int parameterIndex = 0;
+        parameterIndex < method.getDefinition().getNumberOfArguments();
+        parameterIndex++) {
+      if (methodState.getAbstractArgumentValue(parameterIndex).isSingleValue()) {
+        return true;
       }
-
-      if (parameterState.getAbstractValue(appView).isSingleValue()) {
-        // Don't widen when we have information that can be used for parameter removal.
-        continue;
-      }
-
       ParameterReprocessingCriteria parameterReprocessingCriteria =
           getParameterReprocessingCriteria(parameterIndex);
       DexType parameterType = method.getArgumentType(parameterIndex);
       if (parameterReprocessingCriteria.shouldReprocess(
-          appView, parameterState.asConcrete(), parameterType)) {
-        continue;
+          appView, method, concreteMethodState, parameterIndex, parameterType)) {
+        return true;
       }
-
-      methodState.setParameterState(parameterIndex, UnknownParameterState.get());
     }
-
-    return Iterables.all(methodState.getParameterStates(), ParameterState::isUnknown)
-        ? MethodState.unknown()
-        : methodState;
+    return false;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/NonTrivialParameterReprocessingCriteria.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/NonTrivialParameterReprocessingCriteria.java
index b55bf13..47f01c1 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/NonTrivialParameterReprocessingCriteria.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/NonTrivialParameterReprocessingCriteria.java
@@ -6,11 +6,10 @@
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.type.DynamicType;
 import com.android.tools.r8.ir.analysis.type.Nullability;
-import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteParameterState;
-import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcretePrimitiveTypeParameterState;
-import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteReferenceTypeParameterState;
+import com.android.tools.r8.ir.optimize.info.ConcreteCallSiteOptimizationInfo;
 import com.android.tools.r8.optimize.argumentpropagation.utils.WideningUtils;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 
@@ -31,13 +30,16 @@
   @Override
   public boolean shouldReprocess(
       AppView<AppInfoWithLiveness> appView,
-      ConcreteParameterState parameterState,
+      ProgramMethod method,
+      ConcreteCallSiteOptimizationInfo methodState,
+      int parameterIndex,
       DexType parameterType) {
-    if (parameterState.isReferenceParameter()) {
-      return shouldReprocess(appView, parameterState.asReferenceParameter(), parameterType);
+    if (parameterType.isReferenceType()) {
+      return shouldReprocessReferenceParameter(
+          appView, method, methodState, parameterIndex, parameterType);
     } else {
-      assert parameterState.isPrimitiveParameter();
-      return shouldReprocess(appView, parameterState.asPrimitiveParameter(), parameterType);
+      assert parameterType.isPrimitiveType();
+      return shouldReprocessPrimitiveParameter(methodState, parameterIndex);
     }
   }
 
@@ -56,34 +58,35 @@
     return true;
   }
 
-  private boolean shouldReprocess(
-      AppView<AppInfoWithLiveness> appView,
-      ConcretePrimitiveTypeParameterState parameterState,
-      DexType parameterType) {
-    return true;
+  private boolean shouldReprocessPrimitiveParameter(
+      ConcreteCallSiteOptimizationInfo methodState, int parameterIndex) {
+    return methodState.getAbstractArgumentValue(parameterIndex).isNonTrivial();
   }
 
-  private boolean shouldReprocess(
+  private boolean shouldReprocessReferenceParameter(
       AppView<AppInfoWithLiveness> appView,
-      ConcreteReferenceTypeParameterState parameterState,
+      ProgramMethod method,
+      ConcreteCallSiteOptimizationInfo methodState,
+      int parameterIndex,
       DexType parameterType) {
     if (shouldReprocessDueToAbstractValue()
-        && !parameterState.getAbstractValue(appView).isUnknown()) {
+        && !methodState.getAbstractArgumentValue(parameterIndex).isUnknown()) {
       return true;
     }
     if (shouldReprocessDueToDynamicType()) {
       DynamicType widenedDynamicType =
           WideningUtils.widenDynamicNonReceiverType(
               appView,
-              parameterState.getDynamicType().withNullability(Nullability.maybeNull()),
+              methodState.getDynamicType(parameterIndex).withNullability(Nullability.maybeNull()),
               parameterType);
       if (!widenedDynamicType.isUnknown()) {
         return true;
       }
     }
+    boolean isReceiverParameter = parameterIndex == 0 && method.getDefinition().isInstance();
     if (shouldReprocessDueToNullability()
-        && !parameterState.isReceiverParameter()
-        && !parameterState.getNullability().isUnknown()) {
+        && !isReceiverParameter
+        && !methodState.getNullability(parameterIndex).isUnknown()) {
       return true;
     }
     return false;
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/ParameterReprocessingCriteria.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/ParameterReprocessingCriteria.java
index d7c8890..9fb773e 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/ParameterReprocessingCriteria.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/ParameterReprocessingCriteria.java
@@ -6,9 +6,10 @@
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
 import com.android.tools.r8.ir.analysis.type.DynamicType;
-import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteParameterState;
+import com.android.tools.r8.ir.optimize.info.ConcreteCallSiteOptimizationInfo;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 
 public abstract class ParameterReprocessingCriteria {
@@ -35,7 +36,9 @@
 
   public abstract boolean shouldReprocess(
       AppView<AppInfoWithLiveness> appView,
-      ConcreteParameterState parameterState,
+      ProgramMethod method,
+      ConcreteCallSiteOptimizationInfo methodState,
+      int parameterIndex,
       DexType parameterType);
 
   public abstract boolean shouldReprocessDueToAbstractValue();
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 cde8896..d38df4c 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -146,8 +146,6 @@
   public final Map<DexMember<?, ?>, ProguardMemberRule> assumedValues;
   /** All methods that should be inlined if possible due to a configuration directive. */
   private final Set<DexMethod> alwaysInline;
-  /** All methods that *must* never be inlined due to a configuration directive (testing only). */
-  private final Set<DexMethod> neverInline;
   /**
    * All methods that *must* never be inlined as a result of having a single caller due to a
    * configuration directive (testing only).
@@ -228,7 +226,6 @@
       Map<DexMember<?, ?>, ProguardMemberRule> noSideEffects,
       Map<DexMember<?, ?>, ProguardMemberRule> assumedValues,
       Set<DexMethod> alwaysInline,
-      Set<DexMethod> neverInline,
       Set<DexMethod> neverInlineDueToSingleCaller,
       Set<DexMethod> whyAreYouNotInlining,
       Set<DexMethod> keepConstantArguments,
@@ -266,7 +263,6 @@
     this.assumedValues = assumedValues;
     this.callSites = callSites;
     this.alwaysInline = alwaysInline;
-    this.neverInline = neverInline;
     this.neverInlineDueToSingleCaller = neverInlineDueToSingleCaller;
     this.whyAreYouNotInlining = whyAreYouNotInlining;
     this.keepConstantArguments = keepConstantArguments;
@@ -312,7 +308,6 @@
         previous.noSideEffects,
         previous.assumedValues,
         previous.alwaysInline,
-        previous.neverInline,
         previous.neverInlineDueToSingleCaller,
         previous.whyAreYouNotInlining,
         previous.keepConstantArguments,
@@ -363,7 +358,6 @@
         pruneMapFromMembers(previous.noSideEffects, prunedItems, executorService, futures),
         pruneMapFromMembers(previous.assumedValues, prunedItems, executorService, futures),
         pruneMethods(previous.alwaysInline, prunedItems, executorService, futures),
-        pruneMethods(previous.neverInline, prunedItems, executorService, futures),
         pruneMethods(previous.neverInlineDueToSingleCaller, prunedItems, executorService, futures),
         pruneMethods(previous.whyAreYouNotInlining, prunedItems, executorService, futures),
         pruneMethods(previous.keepConstantArguments, prunedItems, executorService, futures),
@@ -571,7 +565,6 @@
         noSideEffects,
         assumedValues,
         alwaysInline,
-        neverInline,
         neverInlineDueToSingleCaller,
         whyAreYouNotInlining,
         keepConstantArguments,
@@ -654,7 +647,6 @@
     this.assumedValues = previous.assumedValues;
     this.callSites = previous.callSites;
     this.alwaysInline = previous.alwaysInline;
-    this.neverInline = previous.neverInline;
     this.neverInlineDueToSingleCaller = previous.neverInlineDueToSingleCaller;
     this.whyAreYouNotInlining = previous.whyAreYouNotInlining;
     this.keepConstantArguments = previous.keepConstantArguments;
@@ -790,10 +782,6 @@
     return alwaysInline.isEmpty();
   }
 
-  public boolean isNeverInlineMethod(DexMethod method) {
-    return neverInline.contains(method);
-  }
-
   public boolean isNeverInlineDueToSingleCallerMethod(ProgramMethod method) {
     return neverInlineDueToSingleCaller.contains(method.getReference());
   }
@@ -1119,7 +1107,7 @@
       return false;
     }
     if (!method.getReturnType().isAlwaysNull(this)
-        && !getKeepInfo().getMethodInfo(method, this).isInliningAllowed(options())) {
+        && !getKeepInfo().getMethodInfo(method, this).isOptimizationAllowed(options())) {
       return false;
     }
     return true;
@@ -1295,7 +1283,6 @@
             assumedValues,
             (reference, rules) -> assumedValues.get(lens.getOriginalMemberSignature(reference))),
         lens.rewriteReferences(alwaysInline),
-        lens.rewriteReferences(neverInline),
         lens.rewriteReferences(neverInlineDueToSingleCaller),
         lens.rewriteReferences(whyAreYouNotInlining),
         lens.rewriteReferences(keepConstantArguments),
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index c7a2bab..2a5405b 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -3413,7 +3413,8 @@
 
     private final Map<DexMethod, ProgramMethod> liveMethods = new ConcurrentHashMap<>();
 
-    private final ProgramMethodSet neverInlineMethods = ProgramMethodSet.createConcurrent();
+    private final ProgramMethodMap<KeepMethodInfo.Joiner> minimumKeepInfo =
+        ProgramMethodMap.createConcurrent();
 
     private final Map<DexType, DexClasspathClass> syntheticClasspathClasses =
         new ConcurrentHashMap<>();
@@ -3481,8 +3482,9 @@
       return set;
     }
 
-    public void addNeverInlineMethod(ProgramMethod method) {
-      neverInlineMethods.add(method);
+    public void addMinimumKeepInfo(ProgramMethod method, Consumer<KeepMethodInfo.Joiner> consumer) {
+      consumer.accept(
+          minimumKeepInfo.computeIfAbsent(method, ignoreKey(KeepMethodInfo::newEmptyJoiner)));
     }
 
     void enqueueWorkItems(Enqueuer enqueuer) {
@@ -3509,7 +3511,10 @@
                 enqueuer.appInfo(), clazz, itfs);
           });
 
-      neverInlineMethods.forEach(m -> enqueuer.rootSet.neverInline.add(m.getReference()));
+      minimumKeepInfo.forEach(
+          (method, minimumKeepInfoForMethod) ->
+              enqueuer.applyMinimumKeepInfoWhenLiveOrTargeted(
+                  method, UnconditionalKeepInfoEvent.get(), minimumKeepInfoForMethod));
     }
   }
 
@@ -3740,6 +3745,7 @@
               ? ImmutableSet.of(lambdaSynthesisContext.getReference())
               : ImmutableSet.of(syntheticClass.getType());
         };
+    amendKeepInfoWithCompanionMethods();
     AppInfoWithLiveness appInfoWithLiveness =
         new AppInfoWithLiveness(
             appInfo.getSyntheticItems().commit(app),
@@ -3768,7 +3774,6 @@
             rootSet.noSideEffects,
             rootSet.assumedValues,
             amendWithCompanionMethods(rootSet.alwaysInline),
-            amendWithCompanionMethods(rootSet.neverInline),
             amendWithCompanionMethods(rootSet.neverInlineDueToSingleCaller),
             amendWithCompanionMethods(rootSet.whyAreYouNotInlining),
             amendWithCompanionMethods(rootSet.keepConstantArguments),
@@ -3794,6 +3799,28 @@
     return new EnqueuerResult(appInfoWithLiveness);
   }
 
+  private void forEachCompanionMethod(BiConsumer<DexMethod, DexMethod> consumer) {
+    if (interfaceProcessor != null) {
+      interfaceProcessor.forEachMethodToMove(consumer);
+    }
+  }
+
+  private void amendKeepInfoWithCompanionMethods() {
+    forEachCompanionMethod(
+        (methodReference, companionReference) -> {
+          ProgramMethod companion = appView.definitionFor(companionReference).asProgramMethod();
+          KeepMethodInfo.Joiner minimumKeepInfoForCompanion =
+              keepInfo.getMethodInfo(methodReference, appInfo).joiner();
+          KeepMethodInfo.Joiner extraMinimumKeepInfoForCompanion =
+              dependentMinimumKeepInfo
+                  .getUnconditionalMinimumKeepInfoOrDefault(MinimumKeepInfoCollection.empty())
+                  .getOrDefault(methodReference, KeepMethodInfo.newEmptyJoiner())
+                  .asMethodJoiner();
+          keepInfo.evaluateMethodRule(
+              companion, minimumKeepInfoForCompanion.merge(extraMinimumKeepInfoForCompanion));
+        });
+  }
+
   private Set<DexMethod> amendWithCompanionMethods(Set<DexMethod> methods) {
     if (methods.isEmpty() || interfaceProcessor == null) {
       return methods;
diff --git a/src/main/java/com/android/tools/r8/shaking/GlobalKeepInfoConfiguration.java b/src/main/java/com/android/tools/r8/shaking/GlobalKeepInfoConfiguration.java
index 1eeaa21..d10b35c 100644
--- a/src/main/java/com/android/tools/r8/shaking/GlobalKeepInfoConfiguration.java
+++ b/src/main/java/com/android/tools/r8/shaking/GlobalKeepInfoConfiguration.java
@@ -16,6 +16,8 @@
 
   boolean isAccessModificationEnabled();
 
+  boolean isMethodStaticizingEnabled();
+
   boolean isRepackagingEnabled();
 
   boolean isForceProguardCompatibilityEnabled();
diff --git a/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluator.java b/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluator.java
index 7292248..98ffa2a 100644
--- a/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluator.java
+++ b/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluator.java
@@ -18,6 +18,7 @@
 import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.SubtypingInfo;
+import com.android.tools.r8.shaking.InlineRule.Type;
 import com.android.tools.r8.shaking.RootSetUtils.ConsequentRootSet;
 import com.android.tools.r8.shaking.RootSetUtils.ConsequentRootSetBuilder;
 import com.android.tools.r8.shaking.RootSetUtils.RootSetBuilder;
@@ -346,11 +347,18 @@
         rootSetBuilder.runPerRule(executorService, futures, neverClassInlineRuleForCondition, null);
       }
 
+      InlineRule neverInlineForClassInliningRuleForCondition =
+          materializedRule.neverInlineRuleForCondition(dexItemFactory, Type.NEVER_CLASS_INLINE);
+      if (neverInlineForClassInliningRuleForCondition != null) {
+        rootSetBuilder.runPerRule(
+            executorService, futures, neverInlineForClassInliningRuleForCondition, null);
+      }
+
       // If the condition of the -if rule has any members, then we need to keep these members to
       // ensure that the subsequent rule will be applied again in the second round of tree
       // shaking.
       InlineRule neverInlineRuleForCondition =
-          materializedRule.neverInlineRuleForCondition(dexItemFactory);
+          materializedRule.neverInlineRuleForCondition(dexItemFactory, Type.NEVER);
       if (neverInlineRuleForCondition != null) {
         rootSetBuilder.runPerRule(executorService, futures, neverInlineRuleForCondition, null);
       }
diff --git a/src/main/java/com/android/tools/r8/shaking/InlineRule.java b/src/main/java/com/android/tools/r8/shaking/InlineRule.java
index 7f77fab..505bdbe 100644
--- a/src/main/java/com/android/tools/r8/shaking/InlineRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/InlineRule.java
@@ -13,6 +13,7 @@
   public enum Type {
     ALWAYS,
     NEVER,
+    NEVER_CLASS_INLINE,
     NEVER_SINGLE_CALLER
   }
 
@@ -103,6 +104,8 @@
         return "alwaysinline";
       case NEVER:
         return "neverinline";
+      case NEVER_CLASS_INLINE:
+        return "neverclassinlinemethod";
       case NEVER_SINGLE_CALLER:
         return "neversinglecaller";
     }
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java b/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
index 36555ac..570ed16 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
@@ -245,6 +245,12 @@
       this.methodRuleInstances = methodRuleInstances;
     }
 
+    public void removeKeepInfoForMergedClasses(PrunedItems prunedItems) {
+      if (prunedItems.hasRemovedClasses()) {
+        keepClassInfo.keySet().removeAll(prunedItems.getRemovedClasses());
+      }
+    }
+
     public void removeKeepInfoForPrunedItems(PrunedItems prunedItems) {
       if (prunedItems.hasRemovedClasses()) {
         keepClassInfo.keySet().removeAll(prunedItems.getRemovedClasses());
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 9938bbc..b43c44d 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java
@@ -24,8 +24,19 @@
     return bottom().joiner();
   }
 
+  private final boolean allowClassInlining;
+  private final boolean allowInlining;
+  private final boolean allowMethodStaticizing;
+  private final boolean allowParameterTypeStrengthening;
+  private final boolean allowReturnTypeStrengthening;
+
   private KeepMethodInfo(Builder builder) {
     super(builder);
+    this.allowClassInlining = builder.isClassInliningAllowed();
+    this.allowInlining = builder.isInliningAllowed();
+    this.allowMethodStaticizing = builder.isMethodStaticizingAllowed();
+    this.allowParameterTypeStrengthening = builder.isParameterTypeStrengtheningAllowed();
+    this.allowReturnTypeStrengthening = builder.isReturnTypeStrengtheningAllowed();
   }
 
   // This builder is not private as there are known instances where it is safe to modify keep info
@@ -39,6 +50,53 @@
     return isParameterRemovalAllowed(configuration);
   }
 
+  public boolean isClassInliningAllowed(GlobalKeepInfoConfiguration configuration) {
+    return isOptimizationAllowed(configuration) && internalIsClassInliningAllowed();
+  }
+
+  boolean internalIsClassInliningAllowed() {
+    return allowClassInlining;
+  }
+
+  public boolean isInliningAllowed(GlobalKeepInfoConfiguration configuration) {
+    return isOptimizationAllowed(configuration) && internalIsInliningAllowed();
+  }
+
+  boolean internalIsInliningAllowed() {
+    return allowInlining;
+  }
+
+  public boolean isMethodStaticizingAllowed(GlobalKeepInfoConfiguration configuration) {
+    return isOptimizationAllowed(configuration)
+        && isShrinkingAllowed(configuration)
+        && configuration.isMethodStaticizingEnabled()
+        && internalIsMethodStaticizingAllowed();
+  }
+
+  boolean internalIsMethodStaticizingAllowed() {
+    return allowMethodStaticizing;
+  }
+
+  public boolean isParameterTypeStrengtheningAllowed(GlobalKeepInfoConfiguration configuration) {
+    return isOptimizationAllowed(configuration)
+        && isShrinkingAllowed(configuration)
+        && internalIsParameterTypeStrengtheningAllowed();
+  }
+
+  boolean internalIsParameterTypeStrengtheningAllowed() {
+    return allowParameterTypeStrengthening;
+  }
+
+  public boolean isReturnTypeStrengtheningAllowed(GlobalKeepInfoConfiguration configuration) {
+    return isOptimizationAllowed(configuration)
+        && isShrinkingAllowed(configuration)
+        && internalIsReturnTypeStrengtheningAllowed();
+  }
+
+  boolean internalIsReturnTypeStrengtheningAllowed() {
+    return allowReturnTypeStrengthening;
+  }
+
   public Joiner joiner() {
     assert !isTop();
     return new Joiner(this);
@@ -54,18 +112,110 @@
     return this.equals(bottom());
   }
 
-  public boolean isInliningAllowed(GlobalKeepInfoConfiguration configuration) {
-    return isOptimizationAllowed(configuration);
-  }
-
   public static class Builder extends KeepInfo.Builder<Builder, KeepMethodInfo> {
 
+    private boolean allowClassInlining;
+    private boolean allowInlining;
+    private boolean allowMethodStaticizing;
+    private boolean allowParameterTypeStrengthening;
+    private boolean allowReturnTypeStrengthening;
+
     private Builder() {
       super();
     }
 
     private Builder(KeepMethodInfo original) {
       super(original);
+      allowClassInlining = original.internalIsClassInliningAllowed();
+      allowInlining = original.internalIsInliningAllowed();
+      allowMethodStaticizing = original.internalIsMethodStaticizingAllowed();
+      allowParameterTypeStrengthening = original.internalIsParameterTypeStrengtheningAllowed();
+      allowReturnTypeStrengthening = original.internalIsReturnTypeStrengtheningAllowed();
+    }
+
+    public boolean isClassInliningAllowed() {
+      return allowClassInlining;
+    }
+
+    public Builder setAllowClassInlining(boolean allowClassInlining) {
+      this.allowClassInlining = allowClassInlining;
+      return self();
+    }
+
+    public Builder allowClassInlining() {
+      return setAllowClassInlining(true);
+    }
+
+    public Builder disallowClassInlining() {
+      return setAllowClassInlining(false);
+    }
+
+    public boolean isInliningAllowed() {
+      return allowInlining;
+    }
+
+    public Builder setAllowInlining(boolean allowInlining) {
+      this.allowInlining = allowInlining;
+      return self();
+    }
+
+    public Builder allowInlining() {
+      return setAllowInlining(true);
+    }
+
+    public Builder disallowInlining() {
+      return setAllowInlining(false);
+    }
+
+    public boolean isMethodStaticizingAllowed() {
+      return allowMethodStaticizing;
+    }
+
+    public Builder setAllowMethodStaticizing(boolean allowMethodStaticizing) {
+      this.allowMethodStaticizing = allowMethodStaticizing;
+      return self();
+    }
+
+    public Builder allowMethodStaticizing() {
+      return setAllowMethodStaticizing(true);
+    }
+
+    public Builder disallowMethodStaticizing() {
+      return setAllowMethodStaticizing(false);
+    }
+
+    public boolean isParameterTypeStrengtheningAllowed() {
+      return allowParameterTypeStrengthening;
+    }
+
+    public Builder setAllowParameterTypeStrengthening(boolean allowParameterTypeStrengthening) {
+      this.allowParameterTypeStrengthening = allowParameterTypeStrengthening;
+      return self();
+    }
+
+    public Builder allowParameterTypeStrengthening() {
+      return setAllowParameterTypeStrengthening(true);
+    }
+
+    public Builder disallowParameterTypeStrengthening() {
+      return setAllowParameterTypeStrengthening(false);
+    }
+
+    public boolean isReturnTypeStrengtheningAllowed() {
+      return allowReturnTypeStrengthening;
+    }
+
+    public Builder setAllowReturnTypeStrengthening(boolean allowReturnTypeStrengthening) {
+      this.allowReturnTypeStrengthening = allowReturnTypeStrengthening;
+      return self();
+    }
+
+    public Builder allowReturnTypeStrengthening() {
+      return setAllowReturnTypeStrengthening(true);
+    }
+
+    public Builder disallowReturnTypeStrengthening() {
+      return setAllowReturnTypeStrengthening(false);
     }
 
     @Override
@@ -89,9 +239,40 @@
     }
 
     @Override
+    boolean internalIsEqualTo(KeepMethodInfo other) {
+      return super.internalIsEqualTo(other)
+          && isClassInliningAllowed() == other.internalIsClassInliningAllowed()
+          && isInliningAllowed() == other.internalIsInliningAllowed()
+          && isMethodStaticizingAllowed() == other.internalIsMethodStaticizingAllowed()
+          && isParameterTypeStrengtheningAllowed()
+              == other.internalIsParameterTypeStrengtheningAllowed()
+          && isReturnTypeStrengtheningAllowed() == other.internalIsReturnTypeStrengtheningAllowed();
+    }
+
+    @Override
     public KeepMethodInfo doBuild() {
       return new KeepMethodInfo(this);
     }
+
+    @Override
+    public Builder makeTop() {
+      return super.makeTop()
+          .disallowClassInlining()
+          .disallowInlining()
+          .disallowMethodStaticizing()
+          .disallowParameterTypeStrengthening()
+          .disallowReturnTypeStrengthening();
+    }
+
+    @Override
+    public Builder makeBottom() {
+      return super.makeBottom()
+          .allowClassInlining()
+          .allowInlining()
+          .allowMethodStaticizing()
+          .allowParameterTypeStrengthening()
+          .allowReturnTypeStrengthening();
+    }
   }
 
   public static class Joiner extends KeepInfo.Joiner<Joiner, Builder, KeepMethodInfo> {
@@ -100,6 +281,31 @@
       super(info.builder());
     }
 
+    public Joiner disallowClassInlining() {
+      builder.disallowClassInlining();
+      return self();
+    }
+
+    public Joiner disallowInlining() {
+      builder.disallowInlining();
+      return self();
+    }
+
+    public Joiner disallowMethodStaticizing() {
+      builder.disallowMethodStaticizing();
+      return self();
+    }
+
+    public Joiner disallowParameterTypeStrengthening() {
+      builder.disallowParameterTypeStrengthening();
+      return self();
+    }
+
+    public Joiner disallowReturnTypeStrengthening() {
+      builder.disallowReturnTypeStrengthening();
+      return self();
+    }
+
     @Override
     public Joiner asMethodJoiner() {
       return this;
@@ -108,7 +314,16 @@
     @Override
     public Joiner merge(Joiner joiner) {
       // Should be extended to merge the fields of this class in case any are added.
-      return super.merge(joiner);
+      return super.merge(joiner)
+          .applyIf(!joiner.builder.isClassInliningAllowed(), Joiner::disallowClassInlining)
+          .applyIf(!joiner.builder.isInliningAllowed(), Joiner::disallowInlining)
+          .applyIf(!joiner.builder.isMethodStaticizingAllowed(), Joiner::disallowMethodStaticizing)
+          .applyIf(
+              !joiner.builder.isParameterTypeStrengtheningAllowed(),
+              Joiner::disallowParameterTypeStrengthening)
+          .applyIf(
+              !joiner.builder.isReturnTypeStrengtheningAllowed(),
+              Joiner::disallowReturnTypeStrengthening);
     }
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/shaking/MinimumKeepInfoCollection.java b/src/main/java/com/android/tools/r8/shaking/MinimumKeepInfoCollection.java
index 8062beb..5a07f2a 100644
--- a/src/main/java/com/android/tools/r8/shaking/MinimumKeepInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/shaking/MinimumKeepInfoCollection.java
@@ -90,6 +90,11 @@
         });
   }
 
+  public KeepInfo.Joiner<?, ?, ?> getOrDefault(
+      DexReference reference, KeepInfo.Joiner<?, ?, ?> defaultValue) {
+    return minimumKeepInfo.getOrDefault(reference, defaultValue);
+  }
+
   public KeepInfo.Joiner<?, ?, ?> getOrCreateMinimumKeepInfoFor(DexReference reference) {
     return minimumKeepInfo.computeIfAbsent(
         reference, ignoreKey(() -> KeepInfo.newEmptyJoinerFor(reference)));
diff --git a/src/main/java/com/android/tools/r8/shaking/NoMethodStaticizingRule.java b/src/main/java/com/android/tools/r8/shaking/NoMethodStaticizingRule.java
new file mode 100644
index 0000000..84fd67d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/NoMethodStaticizingRule.java
@@ -0,0 +1,84 @@
+// Copyright (c) 2022, 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.shaking;
+
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
+import java.util.List;
+
+public class NoMethodStaticizingRule extends NoOptimizationBaseRule<NoMethodStaticizingRule> {
+
+  public static final String RULE_NAME = "nomethodstaticizing";
+
+  public static class Builder
+      extends NoOptimizationBaseRule.Builder<NoMethodStaticizingRule, Builder> {
+
+    Builder() {
+      super();
+    }
+
+    @Override
+    public NoMethodStaticizingRule.Builder self() {
+      return this;
+    }
+
+    @Override
+    public NoMethodStaticizingRule build() {
+      return new NoMethodStaticizingRule(
+          origin,
+          getPosition(),
+          source,
+          buildClassAnnotations(),
+          classAccessFlags,
+          negatedClassAccessFlags,
+          classTypeNegated,
+          classType,
+          classNames,
+          buildInheritanceAnnotations(),
+          inheritanceClassName,
+          inheritanceIsExtends,
+          memberRules);
+    }
+  }
+
+  NoMethodStaticizingRule(
+      Origin origin,
+      Position position,
+      String source,
+      List<ProguardTypeMatcher> classAnnotations,
+      ProguardAccessFlags classAccessFlags,
+      ProguardAccessFlags negatedClassAccessFlags,
+      boolean classTypeNegated,
+      ProguardClassType classType,
+      ProguardClassNameList classNames,
+      List<ProguardTypeMatcher> inheritanceAnnotations,
+      ProguardTypeMatcher inheritanceClassName,
+      boolean inheritanceIsExtends,
+      List<ProguardMemberRule> memberRules) {
+    super(
+        origin,
+        position,
+        source,
+        classAnnotations,
+        classAccessFlags,
+        negatedClassAccessFlags,
+        classTypeNegated,
+        classType,
+        classNames,
+        inheritanceAnnotations,
+        inheritanceClassName,
+        inheritanceIsExtends,
+        memberRules);
+  }
+
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  @Override
+  String typeString() {
+    return RULE_NAME;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/NoOptimizationBaseRule.java b/src/main/java/com/android/tools/r8/shaking/NoOptimizationBaseRule.java
new file mode 100644
index 0000000..a397a7e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/NoOptimizationBaseRule.java
@@ -0,0 +1,51 @@
+// Copyright (c) 2022, 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.shaking;
+
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
+import java.util.List;
+
+public abstract class NoOptimizationBaseRule<R extends NoOptimizationBaseRule<R>>
+    extends ProguardConfigurationRule {
+
+  public abstract static class Builder<R extends NoOptimizationBaseRule<R>, B extends Builder<R, B>>
+      extends ProguardConfigurationRule.Builder<R, B> {
+
+    Builder() {
+      super();
+    }
+  }
+
+  NoOptimizationBaseRule(
+      Origin origin,
+      Position position,
+      String source,
+      List<ProguardTypeMatcher> classAnnotations,
+      ProguardAccessFlags classAccessFlags,
+      ProguardAccessFlags negatedClassAccessFlags,
+      boolean classTypeNegated,
+      ProguardClassType classType,
+      ProguardClassNameList classNames,
+      List<ProguardTypeMatcher> inheritanceAnnotations,
+      ProguardTypeMatcher inheritanceClassName,
+      boolean inheritanceIsExtends,
+      List<ProguardMemberRule> memberRules) {
+    super(
+        origin,
+        position,
+        source,
+        classAnnotations,
+        classAccessFlags,
+        negatedClassAccessFlags,
+        classTypeNegated,
+        classType,
+        classNames,
+        inheritanceAnnotations,
+        inheritanceClassName,
+        inheritanceIsExtends,
+        memberRules);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/NoParameterTypeStrengtheningRule.java b/src/main/java/com/android/tools/r8/shaking/NoParameterTypeStrengtheningRule.java
new file mode 100644
index 0000000..e0b6594
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/NoParameterTypeStrengtheningRule.java
@@ -0,0 +1,85 @@
+// Copyright (c) 2022, 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.shaking;
+
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
+import java.util.List;
+
+public class NoParameterTypeStrengtheningRule
+    extends NoOptimizationBaseRule<NoParameterTypeStrengtheningRule> {
+
+  public static final String RULE_NAME = "noparametertypestrengthening";
+
+  public static class Builder
+      extends NoOptimizationBaseRule.Builder<NoParameterTypeStrengtheningRule, Builder> {
+
+    Builder() {
+      super();
+    }
+
+    @Override
+    public NoParameterTypeStrengtheningRule.Builder self() {
+      return this;
+    }
+
+    @Override
+    public NoParameterTypeStrengtheningRule build() {
+      return new NoParameterTypeStrengtheningRule(
+          origin,
+          getPosition(),
+          source,
+          buildClassAnnotations(),
+          classAccessFlags,
+          negatedClassAccessFlags,
+          classTypeNegated,
+          classType,
+          classNames,
+          buildInheritanceAnnotations(),
+          inheritanceClassName,
+          inheritanceIsExtends,
+          memberRules);
+    }
+  }
+
+  NoParameterTypeStrengtheningRule(
+      Origin origin,
+      Position position,
+      String source,
+      List<ProguardTypeMatcher> classAnnotations,
+      ProguardAccessFlags classAccessFlags,
+      ProguardAccessFlags negatedClassAccessFlags,
+      boolean classTypeNegated,
+      ProguardClassType classType,
+      ProguardClassNameList classNames,
+      List<ProguardTypeMatcher> inheritanceAnnotations,
+      ProguardTypeMatcher inheritanceClassName,
+      boolean inheritanceIsExtends,
+      List<ProguardMemberRule> memberRules) {
+    super(
+        origin,
+        position,
+        source,
+        classAnnotations,
+        classAccessFlags,
+        negatedClassAccessFlags,
+        classTypeNegated,
+        classType,
+        classNames,
+        inheritanceAnnotations,
+        inheritanceClassName,
+        inheritanceIsExtends,
+        memberRules);
+  }
+
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  @Override
+  String typeString() {
+    return RULE_NAME;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/NoReturnTypeStrengtheningRule.java b/src/main/java/com/android/tools/r8/shaking/NoReturnTypeStrengtheningRule.java
new file mode 100644
index 0000000..21c6371
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/NoReturnTypeStrengtheningRule.java
@@ -0,0 +1,85 @@
+// Copyright (c) 2022, 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.shaking;
+
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
+import java.util.List;
+
+public class NoReturnTypeStrengtheningRule
+    extends NoOptimizationBaseRule<NoReturnTypeStrengtheningRule> {
+
+  public static final String RULE_NAME = "noreturntypestrengthening";
+
+  public static class Builder
+      extends NoOptimizationBaseRule.Builder<NoReturnTypeStrengtheningRule, Builder> {
+
+    Builder() {
+      super();
+    }
+
+    @Override
+    public NoReturnTypeStrengtheningRule.Builder self() {
+      return this;
+    }
+
+    @Override
+    public NoReturnTypeStrengtheningRule build() {
+      return new NoReturnTypeStrengtheningRule(
+          origin,
+          getPosition(),
+          source,
+          buildClassAnnotations(),
+          classAccessFlags,
+          negatedClassAccessFlags,
+          classTypeNegated,
+          classType,
+          classNames,
+          buildInheritanceAnnotations(),
+          inheritanceClassName,
+          inheritanceIsExtends,
+          memberRules);
+    }
+  }
+
+  NoReturnTypeStrengtheningRule(
+      Origin origin,
+      Position position,
+      String source,
+      List<ProguardTypeMatcher> classAnnotations,
+      ProguardAccessFlags classAccessFlags,
+      ProguardAccessFlags negatedClassAccessFlags,
+      boolean classTypeNegated,
+      ProguardClassType classType,
+      ProguardClassNameList classNames,
+      List<ProguardTypeMatcher> inheritanceAnnotations,
+      ProguardTypeMatcher inheritanceClassName,
+      boolean inheritanceIsExtends,
+      List<ProguardMemberRule> memberRules) {
+    super(
+        origin,
+        position,
+        source,
+        classAnnotations,
+        classAccessFlags,
+        negatedClassAccessFlags,
+        classTypeNegated,
+        classType,
+        classNames,
+        inheritanceAnnotations,
+        inheritanceClassName,
+        inheritanceIsExtends,
+        memberRules);
+  }
+
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  @Override
+  String typeString() {
+    return RULE_NAME;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
index b92040b..b1fdef2 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -500,6 +500,24 @@
           configurationBuilder.addRule(rule);
           return true;
         }
+        if (acceptString(NoMethodStaticizingRule.RULE_NAME)) {
+          ProguardConfigurationRule rule =
+              parseNoOptimizationRule(optionStart, NoMethodStaticizingRule.builder());
+          configurationBuilder.addRule(rule);
+          return true;
+        }
+        if (acceptString(NoParameterTypeStrengtheningRule.RULE_NAME)) {
+          ProguardConfigurationRule rule =
+              parseNoOptimizationRule(optionStart, NoParameterTypeStrengtheningRule.builder());
+          configurationBuilder.addRule(rule);
+          return true;
+        }
+        if (acceptString(NoReturnTypeStrengtheningRule.RULE_NAME)) {
+          ProguardConfigurationRule rule =
+              parseNoOptimizationRule(optionStart, NoReturnTypeStrengtheningRule.builder());
+          configurationBuilder.addRule(rule);
+          return true;
+        }
         if (acceptString("neverpropagatevalue")) {
           MemberValuePropagationRule rule =
               parseMemberValuePropagationRule(MemberValuePropagationRule.Type.NEVER, optionStart);
@@ -828,6 +846,16 @@
       return keepRuleBuilder.build();
     }
 
+    private <R extends NoOptimizationBaseRule<R>, B extends NoOptimizationBaseRule.Builder<R, B>>
+        R parseNoOptimizationRule(Position start, B builder) throws ProguardRuleParserException {
+      builder.setOrigin(origin).setStart(start);
+      parseClassSpec(builder, false);
+      Position end = getPosition();
+      builder.setSource(getSourceSnippet(contents, start, end));
+      builder.setEnd(end);
+      return builder.build();
+    }
+
     private MemberValuePropagationRule parseMemberValuePropagationRule(
         MemberValuePropagationRule.Type type, Position start)
         throws ProguardRuleParserException {
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java
index f61b4ee..f060d22 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java
@@ -219,7 +219,8 @@
    * <p>Therefore, each time the subsequent rule of an -if rule is applied, we also apply a
    * -neverinline rule for the condition of the -if rule.
    */
-  protected InlineRule neverInlineRuleForCondition(DexItemFactory dexItemFactory) {
+  protected InlineRule neverInlineRuleForCondition(
+      DexItemFactory dexItemFactory, InlineRule.Type type) {
     if (getMemberRules() == null || getMemberRules().isEmpty()) {
       return null;
     }
@@ -242,7 +243,7 @@
             .filter(rule -> rule.getRuleType().includesMethods())
             .map(memberRule -> memberRule.materialize(dexItemFactory))
             .collect(Collectors.toList()),
-        InlineRule.Type.NEVER);
+        type);
   }
 
   protected NoHorizontalClassMergingRule noHorizontalClassMergingRuleForCondition(
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
index 3644e3d..9629fb2 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
@@ -267,6 +267,9 @@
         markMatchingFields(clazz, memberKeepRules, rule, null, ifRule);
       } else if (rule instanceof InlineRule
           || rule instanceof ConstantArgumentRule
+          || rule instanceof NoMethodStaticizingRule
+          || rule instanceof NoParameterTypeStrengtheningRule
+          || rule instanceof NoReturnTypeStrengtheningRule
           || rule instanceof UnusedArgumentRule
           || rule instanceof ReprocessMethodRule
           || rule instanceof WhyAreYouNotInliningRule) {
@@ -1189,7 +1192,16 @@
               alwaysInline.add(reference);
               break;
             case NEVER:
-              neverInline.add(reference);
+              dependentMinimumKeepInfo
+                  .getOrCreateUnconditionalMinimumKeepInfoFor(item.getReference())
+                  .asMethodJoiner()
+                  .disallowInlining();
+              break;
+            case NEVER_CLASS_INLINE:
+              dependentMinimumKeepInfo
+                  .getOrCreateUnconditionalMinimumKeepInfoFor(item.getReference())
+                  .asMethodJoiner()
+                  .disallowClassInlining();
               break;
             case NEVER_SINGLE_CALLER:
               neverInlineDueToSingleCaller.add(reference);
@@ -1243,6 +1255,27 @@
       } else if (context instanceof NoHorizontalClassMergingRule) {
         noHorizontalClassMerging.add(item.asClass().type);
         context.markAsUsed();
+      } else if (context instanceof NoMethodStaticizingRule) {
+        assert item.isProgramMethod();
+        dependentMinimumKeepInfo
+            .getOrCreateUnconditionalMinimumKeepInfoFor(item.getReference())
+            .asMethodJoiner()
+            .disallowMethodStaticizing();
+        context.markAsUsed();
+      } else if (context instanceof NoParameterTypeStrengtheningRule) {
+        assert item.isProgramMethod();
+        dependentMinimumKeepInfo
+            .getOrCreateUnconditionalMinimumKeepInfoFor(item.getReference())
+            .asMethodJoiner()
+            .disallowParameterTypeStrengthening();
+        context.markAsUsed();
+      } else if (context instanceof NoReturnTypeStrengtheningRule) {
+        assert item.isProgramMethod();
+        dependentMinimumKeepInfo
+            .getOrCreateUnconditionalMinimumKeepInfoFor(item.getReference())
+            .asMethodJoiner()
+            .disallowReturnTypeStrengthening();
+        context.markAsUsed();
       } else if (context instanceof MemberValuePropagationRule) {
         switch (((MemberValuePropagationRule) context).getType()) {
           case NEVER:
@@ -1578,7 +1611,6 @@
 
   abstract static class RootSetBase {
 
-    final Set<DexMethod> neverInline;
     final Set<DexMethod> neverInlineDueToSingleCaller;
     final Set<DexType> neverClassInline;
     private final DependentMinimumKeepInfoCollection dependentMinimumKeepInfo;
@@ -1587,14 +1619,12 @@
     public final ProgramMethodMap<ProgramMethod> pendingMethodMoveInverse;
 
     RootSetBase(
-        Set<DexMethod> neverInline,
         Set<DexMethod> neverInlineDueToSingleCaller,
         Set<DexType> neverClassInline,
         DependentMinimumKeepInfoCollection dependentMinimumKeepInfo,
         Map<DexType, Set<ProguardKeepRuleBase>> dependentKeepClassCompatRule,
         List<DelayedRootSetActionItem> delayedRootSetActionItems,
         ProgramMethodMap<ProgramMethod> pendingMethodMoveInverse) {
-      this.neverInline = neverInline;
       this.neverInlineDueToSingleCaller = neverInlineDueToSingleCaller;
       this.neverClassInline = neverClassInline;
       this.dependentMinimumKeepInfo = dependentMinimumKeepInfo;
@@ -1660,7 +1690,6 @@
         List<DelayedRootSetActionItem> delayedRootSetActionItems,
         ProgramMethodMap<ProgramMethod> pendingMethodMoveInverse) {
       super(
-          neverInline,
           neverInlineDueToSingleCaller,
           neverClassInline,
           dependentMinimumKeepInfo,
@@ -1713,7 +1742,6 @@
     }
 
     void addConsequentRootSet(ConsequentRootSet consequentRootSet) {
-      neverInline.addAll(consequentRootSet.neverInline);
       neverInlineDueToSingleCaller.addAll(consequentRootSet.neverInlineDueToSingleCaller);
       neverClassInline.addAll(consequentRootSet.neverClassInline);
       consequentRootSet.dependentKeepClassCompatRule.forEach(
@@ -1999,7 +2027,6 @@
         List<DelayedRootSetActionItem> delayedRootSetActionItems,
         ProgramMethodMap<ProgramMethod> pendingMethodMoveInverse) {
       super(
-          neverInline,
           neverInlineDueToSingleCaller,
           neverClassInline,
           dependentMinimumKeepInfo,
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 6d51c04..c882cf4 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -723,7 +723,7 @@
     KeepInfoCollection keepInfo = appView.appInfo().getKeepInfo();
     keepInfo.mutate(
         mutator ->
-            mutator.removeKeepInfoForPrunedItems(
+            mutator.removeKeepInfoForMergedClasses(
                 PrunedItems.builder().setRemovedClasses(mergedClasses.keySet()).build()));
     timing.end();
 
diff --git a/src/main/java/com/android/tools/r8/tracereferences/TraceReferences.java b/src/main/java/com/android/tools/r8/tracereferences/TraceReferences.java
index b9cbec8..e911b47 100644
--- a/src/main/java/com/android/tools/r8/tracereferences/TraceReferences.java
+++ b/src/main/java/com/android/tools/r8/tracereferences/TraceReferences.java
@@ -67,7 +67,9 @@
     for (ProgramResourceProvider provider : command.getSource()) {
       forEachDescriptor(provider, targetDescriptors::remove);
     }
-    Tracer tracer = new Tracer(targetDescriptors, builder.build(), command.getReporter());
+    InternalOptions options = new InternalOptions();
+    options.lookupLibraryBeforeProgram = true;
+    Tracer tracer = new Tracer(targetDescriptors, builder.build(), command.getReporter(), options);
     tracer.run(command.getConsumer());
   }
 
diff --git a/src/main/java/com/android/tools/r8/tracereferences/Tracer.java b/src/main/java/com/android/tools/r8/tracereferences/Tracer.java
index 43bdb0f..4650eb7 100644
--- a/src/main/java/com/android/tools/r8/tracereferences/Tracer.java
+++ b/src/main/java/com/android/tools/r8/tracereferences/Tracer.java
@@ -56,14 +56,16 @@
   private final DiagnosticsHandler diagnostics;
   private final Predicate<DexType> targetPredicate;
 
-  Tracer(Set<String> targetDescriptors, AndroidApp inputApp, DiagnosticsHandler diagnostics)
+  Tracer(
+      Set<String> targetDescriptors,
+      AndroidApp inputApp,
+      DiagnosticsHandler diagnostics,
+      InternalOptions options)
       throws IOException {
     this(
         AppView.createForTracer(
             AppInfoWithClassHierarchy.createInitialAppInfoWithClassHierarchy(
-                new ApplicationReader(inputApp, new InternalOptions(), Timing.empty())
-                    .read()
-                    .toDirect(),
+                new ApplicationReader(inputApp, options, Timing.empty()).read().toDirect(),
                 ClassToFeatureSplitMap.createEmptyClassToFeatureSplitMap(),
                 MainDexInfo.none())),
         diagnostics,
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 1000a75..f3dcf92 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -690,6 +690,11 @@
         && getProguardConfiguration().isAccessModificationAllowed();
   }
 
+  @Override
+  public boolean isMethodStaticizingEnabled() {
+    return callSiteOptimizationOptions().isMethodStaticizingEnabled();
+  }
+
   public boolean keepInnerClassStructure() {
     return getProguardConfiguration().getKeepAttributes().signature
         || getProguardConfiguration().getKeepAttributes().innerClasses;
@@ -1258,21 +1263,12 @@
   public class CallSiteOptimizationOptions {
 
     private boolean enabled = true;
-
-    // Each time we see an invoke with more dispatch targets than the threshold, we stop call site
-    // propagation for all these dispatch targets. The motivation for this is that it is expensive
-    // and that we are somewhat unlikely to have precise knowledge about the value of arguments when
-    // there are many (possibly spurious) call graph edges.
-    private final int maxNumberOfDispatchTargetsBeforeAbandoning = 10;
+    private boolean enableMethodStaticizing = true;
 
     public void disableOptimization() {
       enabled = false;
     }
 
-    public int getMaxNumberOfDispatchTargetsBeforeAbandoning() {
-      return maxNumberOfDispatchTargetsBeforeAbandoning;
-    }
-
     public int getMaxNumberOfInParameters() {
       return 10;
     }
@@ -1284,6 +1280,10 @@
       return enabled;
     }
 
+    public boolean isMethodStaticizingEnabled() {
+      return enableMethodStaticizing;
+    }
+
     public CallSiteOptimizationOptions setEnabled(boolean enabled) {
       if (enabled) {
         assert isEnabled();
@@ -1292,6 +1292,11 @@
       }
       return this;
     }
+
+    public CallSiteOptimizationOptions setEnableMethodStaticizing(boolean enableMethodStaticizing) {
+      this.enableMethodStaticizing = enableMethodStaticizing;
+      return this;
+    }
   }
 
   public class ClassInlinerOptions {
@@ -1915,6 +1920,7 @@
   }
 
   public boolean canUseSuppressedExceptions() {
+    // TODO(b/214239152): Suppressed exceptions are @hide from at least 4.0.1 / Android I / API 14.
     return !isDesugaring() || hasMinApi(AndroidApiLevel.K);
   }
 
diff --git a/src/main/java/com/android/tools/r8/utils/collections/DexClassAndMethodSetBase.java b/src/main/java/com/android/tools/r8/utils/collections/DexClassAndMethodSetBase.java
index 00749bb..cc8bc7f 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/DexClassAndMethodSetBase.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/DexClassAndMethodSetBase.java
@@ -46,6 +46,10 @@
     return backing.get(method);
   }
 
+  public T getFirst() {
+    return iterator().next();
+  }
+
   public boolean contains(DexMethod method) {
     return backing.containsKey(method);
   }
diff --git a/src/test/examplesAndroidO/trywithresources/TryWithResources.java b/src/test/examplesAndroidO/trywithresources/TryWithResources.java
index ac779d6..2c407cf 100644
--- a/src/test/examplesAndroidO/trywithresources/TryWithResources.java
+++ b/src/test/examplesAndroidO/trywithresources/TryWithResources.java
@@ -36,20 +36,12 @@
       dumpException(cause, indent + "  cause: ");
     }
 
-    // Dump suppressed UNLESS it is a desugared code running
-    // on JVM, in which case we avoid dumping suppressed, since
-    // the output will be used for comparison with desugared code
-    // running on device.
-    if (!desugaredCodeRunningOnJvm()) {
-      Throwable[] suppressed = e.getSuppressed();
-      for (int i = 0; i < suppressed.length; i++) {
-        dumpException(suppressed[i], indent + "supp[" + i + "]: ");
-      }
+    Throwable[] suppressed = e.getSuppressed();
+    for (int i = 0; i < suppressed.length; i++) {
+      dumpException(suppressed[i], indent + "supp[" + i + "]: ");
     }
   }
 
-  abstract boolean desugaredCodeRunningOnJvm();
-
   // --- TEST SYMBOLS ---
 
   static class Resource implements Closeable {
@@ -190,9 +182,7 @@
       packer.act(new RuntimeException("original exception Z"));
 
       for (Throwable unpacked : unpacker.get()) {
-        if (!desugaredCodeRunningOnJvm()) {
-          dumpException(unpacked);
-        }
+        dumpException(unpacked);
       }
     }
   }
diff --git a/src/test/examplesAndroidO/trywithresources/TryWithResourcesDesugaredTests.java b/src/test/examplesAndroidO/trywithresources/TryWithResourcesDesugaredTests.java
index 10a96f9..65e7221 100644
--- a/src/test/examplesAndroidO/trywithresources/TryWithResourcesDesugaredTests.java
+++ b/src/test/examplesAndroidO/trywithresources/TryWithResourcesDesugaredTests.java
@@ -4,19 +4,6 @@
 package trywithresources;
 
 public class TryWithResourcesDesugaredTests extends TryWithResources {
-  private boolean isAndroid() {
-    try {
-      Class.forName("dalvik.system.VMRuntime");
-      return true;
-    } catch (Exception ignored) {
-    }
-    return false;
-  }
-
-  @Override
-  boolean desugaredCodeRunningOnJvm() {
-    return !isAndroid();
-  }
 
   public static void main(String[] args) throws Exception {
     new TryWithResourcesDesugaredTests().test();
diff --git a/src/test/examplesAndroidO/trywithresources/TryWithResourcesNotDesugaredTests.java b/src/test/examplesAndroidO/trywithresources/TryWithResourcesNotDesugaredTests.java
index d2a05c3..8180fb1 100644
--- a/src/test/examplesAndroidO/trywithresources/TryWithResourcesNotDesugaredTests.java
+++ b/src/test/examplesAndroidO/trywithresources/TryWithResourcesNotDesugaredTests.java
@@ -4,10 +4,6 @@
 package trywithresources;
 
 public class TryWithResourcesNotDesugaredTests extends TryWithResources {
-  @Override
-  boolean desugaredCodeRunningOnJvm() {
-    return false;
-  }
 
   public static void main(String[] args) throws Exception {
     new TryWithResourcesNotDesugaredTests().test();
diff --git a/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
index dfebfd7..6006f32 100644
--- a/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
@@ -99,6 +99,7 @@
           Assert.assertTrue(
               descriptor.endsWith(getCompanionClassNameSuffix() + ";")
                   || SyntheticItemsTestUtils.isExternalTwrCloseMethod(reference)
+                  || SyntheticItemsTestUtils.isMaybeExternalSuppressedExceptionMethod(reference)
                   || SyntheticItemsTestUtils.isExternalLambda(reference)
                   || SyntheticItemsTestUtils.isExternalStaticInterfaceCall(reference)
                   || descriptor.equals(mainClassDescriptor));
diff --git a/src/test/java/com/android/tools/r8/NoMethodStaticizing.java b/src/test/java/com/android/tools/r8/NoMethodStaticizing.java
new file mode 100644
index 0000000..ecfd3af
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/NoMethodStaticizing.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2022, 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;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+@Target({ElementType.METHOD})
+public @interface NoMethodStaticizing {}
diff --git a/src/test/java/com/android/tools/r8/NoParameterTypeStrengthening.java b/src/test/java/com/android/tools/r8/NoParameterTypeStrengthening.java
new file mode 100644
index 0000000..c862859
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/NoParameterTypeStrengthening.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2022, 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;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+@Target({ElementType.METHOD, ElementType.PARAMETER})
+public @interface NoParameterTypeStrengthening {}
diff --git a/src/test/java/com/android/tools/r8/NoReturnTypeStrengthening.java b/src/test/java/com/android/tools/r8/NoReturnTypeStrengthening.java
new file mode 100644
index 0000000..faf89d0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/NoReturnTypeStrengthening.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2022, 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;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+@Target({ElementType.METHOD})
+public @interface NoReturnTypeStrengthening {}
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java
index 1fa7630..090ebb5 100644
--- a/src/test/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -18,6 +18,9 @@
 import com.android.tools.r8.shaking.CollectingGraphConsumer;
 import com.android.tools.r8.shaking.NoFieldTypeStrengtheningRule;
 import com.android.tools.r8.shaking.NoHorizontalClassMergingRule;
+import com.android.tools.r8.shaking.NoMethodStaticizingRule;
+import com.android.tools.r8.shaking.NoParameterTypeStrengtheningRule;
+import com.android.tools.r8.shaking.NoReturnTypeStrengtheningRule;
 import com.android.tools.r8.shaking.NoUnusedInterfaceRemovalRule;
 import com.android.tools.r8.shaking.NoVerticalClassMergingRule;
 import com.android.tools.r8.shaking.ProguardConfiguration;
@@ -436,6 +439,11 @@
         "-" + name + " class * { @" + annotation.getTypeName() + " <fields>; }");
   }
 
+  T addInternalMatchAnnotationOnMethodRule(String name, Class<? extends Annotation> annotation) {
+    return addInternalKeepRules(
+        "-" + name + " class * { @" + annotation.getTypeName() + " <methods>; }");
+  }
+
   T addInternalMatchInterfaceRule(String name, Class<?> matchInterface) {
     return addInternalKeepRules("-" + name + " @" + matchInterface.getTypeName() + " class *");
   }
@@ -502,6 +510,24 @@
             NoFieldTypeStrengtheningRule.RULE_NAME, NoFieldTypeStrengthening.class);
   }
 
+  public T enableNoMethodStaticizingAnnotations() {
+    return addNoMethodStaticizingAnnotation()
+        .addInternalMatchAnnotationOnMethodRule(
+            NoMethodStaticizingRule.RULE_NAME, NoMethodStaticizing.class);
+  }
+
+  public T enableNoParameterTypeStrengtheningAnnotations() {
+    return addNoParameterTypeStrengtheningAnnotation()
+        .addInternalMatchAnnotationOnMethodRule(
+            NoParameterTypeStrengtheningRule.RULE_NAME, NoParameterTypeStrengthening.class);
+  }
+
+  public T enableNoReturnTypeStrengtheningAnnotations() {
+    return addNoReturnTypeStrengtheningAnnotation()
+        .addInternalMatchAnnotationOnMethodRule(
+            NoReturnTypeStrengtheningRule.RULE_NAME, NoReturnTypeStrengthening.class);
+  }
+
   public T enableNoUnusedInterfaceRemovalAnnotations() {
     return addNoUnusedInterfaceRemovalAnnotations()
         .addInternalMatchInterfaceRule(
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index 21113a9..d72151f 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -1788,6 +1788,10 @@
     return AndroidApiLevel.K;
   }
 
+  public static AndroidApiLevel apiLevelWithSuppressedExceptionsSupport() {
+    return AndroidApiLevel.K;
+  }
+
   public static AndroidApiLevel apiLevelWithPcAsLineNumberSupport() {
     return AndroidApiLevel.O;
   }
diff --git a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
index 7fc1ecc..0c3b2ae 100644
--- a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
@@ -466,6 +466,18 @@
     return addTestingAnnotation(NoHorizontalClassMerging.class);
   }
 
+  public final T addNoMethodStaticizingAnnotation() {
+    return addTestingAnnotation(NoMethodStaticizing.class);
+  }
+
+  public final T addNoParameterTypeStrengtheningAnnotation() {
+    return addTestingAnnotation(NoParameterTypeStrengthening.class);
+  }
+
+  public final T addNoReturnTypeStrengtheningAnnotation() {
+    return addTestingAnnotation(NoReturnTypeStrengthening.class);
+  }
+
   public final T addNoUnusedInterfaceRemovalAnnotations() {
     return addTestingAnnotation(NoUnusedInterfaceRemoval.class);
   }
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoOutlineForFullyMockedTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoOutlineForFullyMockedTest.java
index 91c55e4..76e8b57 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoOutlineForFullyMockedTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoOutlineForFullyMockedTest.java
@@ -13,6 +13,7 @@
 import static org.junit.Assume.assumeFalse;
 
 import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoReturnTypeStrengthening;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
@@ -60,6 +61,7 @@
         .apply(ApiModelingTestHelper::enableOutliningOfMethods)
         .apply(ApiModelingTestHelper::enableStubbingOfClasses)
         .enableInliningAnnotations()
+        .enableNoReturnTypeStrengtheningAnnotations()
         .compile()
         .applyIf(
             parameters.isDexRuntime()
@@ -93,6 +95,8 @@
   public static class Main {
 
     @NeverInline
+    // TODO(b/214329925): Type strengthening should consult API database.
+    @NoReturnTypeStrengthening
     public static Object create() {
       return AndroidBuildVersion.VERSION >= 23 ? new LibraryClass() : null;
     }
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/bridgestokeep/KeepNonVisibilityBridgeMethodsTest.java b/src/test/java/com/android/tools/r8/bridgeremoval/bridgestokeep/KeepNonVisibilityBridgeMethodsTest.java
index f1498b6..2472119 100644
--- a/src/test/java/com/android/tools/r8/bridgeremoval/bridgestokeep/KeepNonVisibilityBridgeMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/bridgestokeep/KeepNonVisibilityBridgeMethodsTest.java
@@ -56,7 +56,8 @@
         .addKeepRules(
             "-alwaysinline class * { @"
                 + AlwaysInline.class.getTypeName()
-                + " !synthetic <methods>; }")
+                + " !synthetic <methods>; }",
+            "-noparametertypestrengthening class * { synthetic <methods>; }")
         .enableNeverClassInliningAnnotations()
         // TODO(b/120764902): MemberSubject.getOriginalName() is not working without the @NeverMerge
         //  annotation on DataAdapter.Observer.
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/BridgeHoistingAccessibilityTest.java b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/BridgeHoistingAccessibilityTest.java
index 45855d8..bf166ba 100644
--- a/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/BridgeHoistingAccessibilityTest.java
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/BridgeHoistingAccessibilityTest.java
@@ -77,6 +77,8 @@
         .enableNoHorizontalClassMergingAnnotations()
         .enableNoVerticalClassMergingAnnotations()
         .enableNeverClassInliningAnnotations()
+        // TODO(b/173398086): uniqueMethodWithName() does not work with argument changes.
+        .noMinification()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(this::inspect)
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/KeptBridgeHoistingTest.java b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/KeptBridgeHoistingTest.java
index fc6ac6b..e45bdcc 100644
--- a/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/KeptBridgeHoistingTest.java
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/KeptBridgeHoistingTest.java
@@ -45,6 +45,8 @@
         .enableInliningAnnotations()
         .enableNoVerticalClassMergingAnnotations()
         .enableNeverClassInliningAnnotations()
+        // TODO(b/173398086): uniqueMethodWithName() does not work with signature changes.
+        .noMinification()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(this::inspect)
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/NonSuperclassBridgeHoistingTest.java b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/NonSuperclassBridgeHoistingTest.java
index e987a43..2819311 100644
--- a/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/NonSuperclassBridgeHoistingTest.java
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/NonSuperclassBridgeHoistingTest.java
@@ -47,6 +47,8 @@
         .enableInliningAnnotations()
         .enableNoVerticalClassMergingAnnotations()
         .enableNeverClassInliningAnnotations()
+        // TODO(b/173398086): uniqueMethodWithName() does not work with signature changes.
+        .noMinification()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(this::inspect)
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/PositiveBridgeHoistingTest.java b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/PositiveBridgeHoistingTest.java
index ae77a45..bdd55fc 100644
--- a/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/PositiveBridgeHoistingTest.java
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/PositiveBridgeHoistingTest.java
@@ -56,6 +56,8 @@
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .enableNoHorizontalClassMergingAnnotations()
+        // TODO(b/173398086): uniqueMethodWithName() does not work with argument changes.
+        .noMinification()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(this::inspect)
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/testclasses/BridgeHoistingAccessibilityTestClasses.java b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/testclasses/BridgeHoistingAccessibilityTestClasses.java
index 64c19e5..36cc8be 100644
--- a/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/testclasses/BridgeHoistingAccessibilityTestClasses.java
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/testclasses/BridgeHoistingAccessibilityTestClasses.java
@@ -31,6 +31,7 @@
     }
   }
 
+  @NoHorizontalClassMerging
   @NoVerticalClassMerging
   public static class AWithRangedInvoke {
 
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithDifferentInterfacesTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithDifferentInterfacesTest.java
index 497e4df..1d6c93f 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithDifferentInterfacesTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithDifferentInterfacesTest.java
@@ -10,6 +10,7 @@
 
 import com.android.tools.r8.NeverClassInline;
 import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoParameterTypeStrengthening;
 import com.android.tools.r8.NoVerticalClassMerging;
 import com.android.tools.r8.TestParameters;
 import org.junit.Test;
@@ -26,6 +27,7 @@
         .addKeepMainRule(Main.class)
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
+        .enableNoParameterTypeStrengtheningAnnotations()
         .enableNoVerticalClassMergingAnnotations()
         .setMinApi(parameters.getApiLevel())
         .addHorizontallyMergedClassesInspector(
@@ -78,7 +80,7 @@
 
   public static class Main {
     @NeverInline
-    public static void foo(I i) {
+    public static void foo(@NoParameterTypeStrengthening I i) {
       i.foo();
     }
 
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithIdenticalInterfacesTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithIdenticalInterfacesTest.java
index 9a5f503..29e7e0e 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithIdenticalInterfacesTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithIdenticalInterfacesTest.java
@@ -10,6 +10,7 @@
 
 import com.android.tools.r8.NeverClassInline;
 import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoParameterTypeStrengthening;
 import com.android.tools.r8.TestParameters;
 import org.junit.Test;
 
@@ -25,6 +26,7 @@
         .addKeepMainRule(Main.class)
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
+        .enableNoParameterTypeStrengtheningAnnotations()
         .setMinApi(parameters.getApiLevel())
         .addHorizontallyMergedClassesInspector(
             inspector ->
@@ -72,7 +74,7 @@
 
   public static class Main {
     @NeverInline
-    public static void foo(I i) {
+    public static void foo(@NoParameterTypeStrengthening I i) {
       i.foo();
     }
 
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/InterfacesVisibilityTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/InterfacesVisibilityTest.java
index b03e236..508b10b 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/InterfacesVisibilityTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/InterfacesVisibilityTest.java
@@ -32,6 +32,7 @@
         .addKeepMainRule(Main.class)
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
+        .enableNoParameterTypeStrengtheningAnnotations()
         .enableNoVerticalClassMergingAnnotations()
         .enableNoHorizontalClassMergingAnnotations()
         .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideMergeAbsentTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideMergeAbsentTest.java
index 1ec170d..51b6b55 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideMergeAbsentTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideMergeAbsentTest.java
@@ -10,6 +10,7 @@
 
 import com.android.tools.r8.NeverClassInline;
 import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoParameterTypeStrengthening;
 import com.android.tools.r8.NoVerticalClassMerging;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.classmerging.horizontal.HorizontalClassMergingTestBase;
@@ -27,6 +28,7 @@
         .addKeepMainRule(Main.class)
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
+        .enableNoParameterTypeStrengtheningAnnotations()
         .enableNoVerticalClassMergingAnnotations()
         .setMinApi(parameters.getApiLevel())
         .addHorizontallyMergedClassesInspector(
@@ -79,7 +81,7 @@
 
   public static class Main {
     @NeverInline
-    public static void doI(J i) {
+    public static void doI(@NoParameterTypeStrengthening J i) {
       i.m();
     }
 
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/testclasses/InterfacesVisibilityTestClasses.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/testclasses/InterfacesVisibilityTestClasses.java
index d5a5c14..c857046 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/testclasses/InterfacesVisibilityTestClasses.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/testclasses/InterfacesVisibilityTestClasses.java
@@ -7,12 +7,13 @@
 import com.android.tools.r8.NeverClassInline;
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.NoParameterTypeStrengthening;
 import com.android.tools.r8.NoVerticalClassMerging;
 
 public class InterfacesVisibilityTestClasses {
   public static class Invoker {
     @NeverInline
-    public static void invokeFoo(PackagePrivateInterface i) {
+    public static void invokeFoo(@NoParameterTypeStrengthening PackagePrivateInterface i) {
       i.foo();
     }
   }
diff --git a/src/test/java/com/android/tools/r8/desugar/constantdynamic/BasicConstantDynamicTest.java b/src/test/java/com/android/tools/r8/desugar/constantdynamic/BasicConstantDynamicTest.java
index d458f22..c5c6480 100644
--- a/src/test/java/com/android/tools/r8/desugar/constantdynamic/BasicConstantDynamicTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/constantdynamic/BasicConstantDynamicTest.java
@@ -109,9 +109,9 @@
     return transformer(A.class)
         .setVersion(CfVersion.V11)
         .transformConstStringToConstantDynamic(
-            "condy1", A.class, "myConstant", "constantName", Object.class)
+            "condy1", A.class, "myConstant", false, "constantName", Object.class)
         .transformConstStringToConstantDynamic(
-            "condy2", A.class, "myConstant", "constantName", Object.class)
+            "condy2", A.class, "myConstant", false, "constantName", Object.class)
         .transform();
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/constantdynamic/BootstrapMethodNotFoundConstantDynamicTest.java b/src/test/java/com/android/tools/r8/desugar/constantdynamic/BootstrapMethodNotFoundConstantDynamicTest.java
index 7b204ac..4cea83c 100644
--- a/src/test/java/com/android/tools/r8/desugar/constantdynamic/BootstrapMethodNotFoundConstantDynamicTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/constantdynamic/BootstrapMethodNotFoundConstantDynamicTest.java
@@ -102,7 +102,7 @@
     return transformer(A.class)
         .setVersion(CfVersion.V11)
         .transformConstStringToConstantDynamic(
-            "condy1", A.class, "methodNotFound", "constantName", Object.class)
+            "condy1", A.class, "methodNotFound", false, "constantName", Object.class)
         .transform();
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/constantdynamic/BootstrapMethodPrivateConstantDynamicTest.java b/src/test/java/com/android/tools/r8/desugar/constantdynamic/BootstrapMethodPrivateConstantDynamicTest.java
index 5f7e125..169a85d 100644
--- a/src/test/java/com/android/tools/r8/desugar/constantdynamic/BootstrapMethodPrivateConstantDynamicTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/constantdynamic/BootstrapMethodPrivateConstantDynamicTest.java
@@ -101,7 +101,7 @@
     return transformer(A.class)
         .setVersion(CfVersion.V11)
         .transformConstStringToConstantDynamic(
-            "condy1", A.class, "myConstant", "constantName", Object.class)
+            "condy1", A.class, "myConstant", false, "constantName", Object.class)
         .transform();
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/constantdynamic/BootstrapMethodVirtualConstantDynamicTest.java b/src/test/java/com/android/tools/r8/desugar/constantdynamic/BootstrapMethodVirtualConstantDynamicTest.java
index 3f9a957..a693c21 100644
--- a/src/test/java/com/android/tools/r8/desugar/constantdynamic/BootstrapMethodVirtualConstantDynamicTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/constantdynamic/BootstrapMethodVirtualConstantDynamicTest.java
@@ -101,7 +101,7 @@
     return transformer(A.class)
         .setVersion(CfVersion.V11)
         .transformConstStringToConstantDynamic(
-            "condy1", A.class, "myConstant", "constantName", Object.class)
+            "condy1", A.class, "myConstant", false, "constantName", Object.class)
         .transform();
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/constantdynamic/ConstantDynamicGetDeclaredMethodsTest.java b/src/test/java/com/android/tools/r8/desugar/constantdynamic/ConstantDynamicGetDeclaredMethodsTest.java
index 4810b1d..7deff9c 100644
--- a/src/test/java/com/android/tools/r8/desugar/constantdynamic/ConstantDynamicGetDeclaredMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/constantdynamic/ConstantDynamicGetDeclaredMethodsTest.java
@@ -126,7 +126,7 @@
     return transformer(A.class)
         .setVersion(CfVersion.V11)
         .transformConstStringToConstantDynamic(
-            "condy", A.class, "myConstant", "constantName", Object.class)
+            "condy", A.class, "myConstant", false, "constantName", Object.class)
         .transform();
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/constantdynamic/ConstantDynamicInDefaultInterfaceMethodICCETest.java b/src/test/java/com/android/tools/r8/desugar/constantdynamic/ConstantDynamicInDefaultInterfaceMethodICCETest.java
new file mode 100644
index 0000000..feff719
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/constantdynamic/ConstantDynamicInDefaultInterfaceMethodICCETest.java
@@ -0,0 +1,141 @@
+// Copyright (c) 2022, 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.desugar.constantdynamic;
+
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.DesugarTestConfiguration;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.cf.CfVersion;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.lang.invoke.MethodHandles;
+import java.util.List;
+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 ConstantDynamicInDefaultInterfaceMethodICCETest extends TestBase {
+
+  @Parameter() public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build());
+  }
+
+  private static final Class<?> MAIN_CLASS = A.class;
+
+  @Test
+  public void testReference() throws Exception {
+    assumeTrue(parameters.isCfRuntime());
+    assumeTrue(parameters.getRuntime().asCf().isNewerThanOrEqual(CfVm.JDK11));
+    assumeTrue(parameters.getApiLevel().isEqualTo(AndroidApiLevel.B));
+
+    testForJvm()
+        .addProgramClasses(MAIN_CLASS)
+        .addProgramClassFileData(getTransformedClasses())
+        .run(parameters.getRuntime(), MAIN_CLASS)
+        .assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class);
+  }
+
+  @Test
+  public void testDesugaring() throws Exception {
+    testForDesugaring(parameters)
+        .addProgramClasses(MAIN_CLASS)
+        .addProgramClassFileData(getTransformedClasses())
+        .run(parameters.getRuntime(), MAIN_CLASS)
+        .applyIf(
+            // When not desugaring the CF code requires JDK 11.
+            DesugarTestConfiguration::isNotDesugared,
+            r -> {
+              if (parameters.isCfRuntime()
+                  && parameters.getRuntime().asCf().isNewerThanOrEqual(CfVm.JDK11)) {
+                r.assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class);
+              } else {
+                r.assertFailureWithErrorThatThrows(UnsupportedClassVersionError.class);
+              }
+            })
+        .applyIf(
+            DesugarTestConfiguration::isDesugared,
+            r -> r.assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class));
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    assumeTrue(parameters.isDexRuntime() || parameters.getApiLevel().isEqualTo(AndroidApiLevel.B));
+
+    testForR8(parameters.getBackend())
+        .addProgramClasses(MAIN_CLASS)
+        .addProgramClassFileData(getTransformedClasses())
+        .setMinApi(parameters.getApiLevel())
+        .addKeepMainRule(MAIN_CLASS)
+        // TODO(b/198142625): Support CONSTANT_Dynamic output for class files.
+        .applyIf(
+            parameters.isCfRuntime(),
+            b -> {
+              assertThrows(
+                  CompilationFailedException.class,
+                  () ->
+                      b.compileWithExpectedDiagnostics(
+                          diagnostics -> {
+                            diagnostics.assertOnlyErrors();
+                            diagnostics.assertErrorsMatch(
+                                diagnosticMessage(
+                                    containsString(
+                                        "Unsupported dynamic constant (not desugaring)")));
+                          }));
+            },
+            b ->
+                b.run(parameters.getRuntime(), MAIN_CLASS)
+                    .assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class));
+  }
+
+  // Create a constant dynamic without the interface bit targeting an interface bootstrap method.
+  private byte[] getTransformedClasses() throws Exception {
+    return transformer(I.class)
+        .setVersion(CfVersion.V11)
+        .transformConstStringToConstantDynamic(
+            "condy1", I.class, "myConstant", false, "constantName", Object.class)
+        .transformConstStringToConstantDynamic(
+            "condy2", I.class, "myConstant", false, "constantName", Object.class)
+        .setPrivate(
+            I.class.getDeclaredMethod(
+                "myConstant", MethodHandles.Lookup.class, String.class, Class.class))
+        .transform();
+  }
+
+  public interface I {
+
+    default Object f() {
+      return "condy1"; // Will be transformed to Constant_DYNAMIC.
+    }
+
+    default Object g() {
+      return "condy2"; // Will be transformed to Constant_DYNAMIC.
+    }
+
+    /* private */ static Object myConstant(
+        MethodHandles.Lookup lookup, String name, Class<?> type) {
+      return new Object();
+    }
+  }
+
+  public static class A implements I {
+    public static void main(String[] args) {
+      A a = new A();
+      System.out.println(a.f() != null);
+      System.out.println(a.f() == a.g());
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/constantdynamic/ConstantDynamicInDefaultInterfaceMethodTest.java b/src/test/java/com/android/tools/r8/desugar/constantdynamic/ConstantDynamicInDefaultInterfaceMethodTest.java
index a09ae6a..ad46d90 100644
--- a/src/test/java/com/android/tools/r8/desugar/constantdynamic/ConstantDynamicInDefaultInterfaceMethodTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/constantdynamic/ConstantDynamicInDefaultInterfaceMethodTest.java
@@ -116,9 +116,9 @@
     return transformer(I.class)
         .setVersion(CfVersion.V11)
         .transformConstStringToConstantDynamic(
-            "condy1", I.class, "myConstant", "constantName", Object.class)
+            "condy1", I.class, "myConstant", true, "constantName", Object.class)
         .transformConstStringToConstantDynamic(
-            "condy2", I.class, "myConstant", "constantName", Object.class)
+            "condy2", I.class, "myConstant", true, "constantName", Object.class)
         .setPrivate(
             I.class.getDeclaredMethod(
                 "myConstant", MethodHandles.Lookup.class, String.class, Class.class))
diff --git a/src/test/java/com/android/tools/r8/desugar/constantdynamic/HierarchyConstantDynamicTest.java b/src/test/java/com/android/tools/r8/desugar/constantdynamic/HierarchyConstantDynamicTest.java
index edc3cab..b7e540b 100644
--- a/src/test/java/com/android/tools/r8/desugar/constantdynamic/HierarchyConstantDynamicTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/constantdynamic/HierarchyConstantDynamicTest.java
@@ -102,12 +102,12 @@
         transformer(A.class)
             .setVersion(CfVersion.V11)
             .transformConstStringToConstantDynamic(
-                "condy1", A.class, "myConstant", "constantName", Object.class)
+                "condy1", A.class, "myConstant", false, "constantName", Object.class)
             .transform(),
         transformer(B.class)
             .setVersion(CfVersion.V11)
             .transformConstStringToConstantDynamic(
-                "condy1", B.class, "myConstant", "constantName", Object.class)
+                "condy1", B.class, "myConstant", false, "constantName", Object.class)
             .transform());
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/constantdynamic/MultipleBootstrapMethodConstantDynamicTest.java b/src/test/java/com/android/tools/r8/desugar/constantdynamic/MultipleBootstrapMethodConstantDynamicTest.java
index b3f900a..a7df375 100644
--- a/src/test/java/com/android/tools/r8/desugar/constantdynamic/MultipleBootstrapMethodConstantDynamicTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/constantdynamic/MultipleBootstrapMethodConstantDynamicTest.java
@@ -110,13 +110,13 @@
     return transformer(A.class)
         .setVersion(CfVersion.V11)
         .transformConstStringToConstantDynamic(
-            "condy1", A.class, "myConstant1", "constantName", Object.class)
+            "condy1", A.class, "myConstant1", false, "constantName", Object.class)
         .transformConstStringToConstantDynamic(
-            "condy2", A.class, "myConstant1", "constantName", Object.class)
+            "condy2", A.class, "myConstant1", false, "constantName", Object.class)
         .transformConstStringToConstantDynamic(
-            "condy3", A.class, "myConstant2", "constantName", Object.class)
+            "condy3", A.class, "myConstant2", false, "constantName", Object.class)
         .transformConstStringToConstantDynamic(
-            "condy4", A.class, "myConstant2", "constantName", Object.class)
+            "condy4", A.class, "myConstant2", false, "constantName", Object.class)
         .transform();
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/constantdynamic/MultipleNamedConstantDynamicTest.java b/src/test/java/com/android/tools/r8/desugar/constantdynamic/MultipleNamedConstantDynamicTest.java
index 667473e..5ebc8da 100644
--- a/src/test/java/com/android/tools/r8/desugar/constantdynamic/MultipleNamedConstantDynamicTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/constantdynamic/MultipleNamedConstantDynamicTest.java
@@ -112,13 +112,13 @@
     return transformer(A.class)
         .setVersion(CfVersion.V11)
         .transformConstStringToConstantDynamic(
-            "condy1", A.class, "myConstant", "constantF", Object.class)
+            "condy1", A.class, "myConstant", false, "constantF", Object.class)
         .transformConstStringToConstantDynamic(
-            "condy2", A.class, "myConstant", "constantF", Object.class)
+            "condy2", A.class, "myConstant", false, "constantF", Object.class)
         .transformConstStringToConstantDynamic(
-            "condy3", A.class, "myConstant", "constantG", Object.class)
+            "condy3", A.class, "myConstant", false, "constantG", Object.class)
         .transformConstStringToConstantDynamic(
-            "condy4", A.class, "myConstant", "constantG", Object.class)
+            "condy4", A.class, "myConstant", false, "constantG", Object.class)
         .transform();
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/constantdynamic/MultipleTypesConstantDynamicTest.java b/src/test/java/com/android/tools/r8/desugar/constantdynamic/MultipleTypesConstantDynamicTest.java
index 48e0aab..fab07ce 100644
--- a/src/test/java/com/android/tools/r8/desugar/constantdynamic/MultipleTypesConstantDynamicTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/constantdynamic/MultipleTypesConstantDynamicTest.java
@@ -111,13 +111,13 @@
     return transformer(A.class)
         .setVersion(CfVersion.V11)
         .transformConstStringToConstantDynamic(
-            "condy1", A.class, "myConstant", "constantName", Object.class)
+            "condy1", A.class, "myConstant", false, "constantName", Object.class)
         .transformConstStringToConstantDynamic(
-            "condy2", A.class, "myConstant", "constantName", Object.class)
+            "condy2", A.class, "myConstant", false, "constantName", Object.class)
         .transformConstStringToConstantDynamic(
-            "condy3", A.class, "myConstant", "constantName", boolean[].class)
+            "condy3", A.class, "myConstant", false, "constantName", boolean[].class)
         .transformConstStringToConstantDynamic(
-            "condy4", A.class, "myConstant", "constantName", boolean[].class)
+            "condy4", A.class, "myConstant", false, "constantName", boolean[].class)
         .transform();
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/constantdynamic/SharedBootstrapMethodConstantDynamicTest.java b/src/test/java/com/android/tools/r8/desugar/constantdynamic/SharedBootstrapMethodConstantDynamicTest.java
index bff5968..1ae1ccf 100644
--- a/src/test/java/com/android/tools/r8/desugar/constantdynamic/SharedBootstrapMethodConstantDynamicTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/constantdynamic/SharedBootstrapMethodConstantDynamicTest.java
@@ -123,16 +123,16 @@
         transformer(A.class)
             .setVersion(CfVersion.V11)
             .transformConstStringToConstantDynamic(
-                "condy1", A.class, "myConstant", "constantName", Object.class)
+                "condy1", A.class, "myConstant", false, "constantName", Object.class)
             .transformConstStringToConstantDynamic(
-                "condy2", A.class, "myConstant", "constantName", Object.class)
+                "condy2", A.class, "myConstant", false, "constantName", Object.class)
             .transform(),
         transformer(B.class)
             .setVersion(CfVersion.V11)
             .transformConstStringToConstantDynamic(
-                "condy3", A.class, "myConstant", "constantName", Object.class)
+                "condy3", A.class, "myConstant", false, "constantName", Object.class)
             .transformConstStringToConstantDynamic(
-                "condy4", A.class, "myConstant", "constantName", Object.class)
+                "condy4", A.class, "myConstant", false, "constantName", Object.class)
             .transform());
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/sealed/SealedAttributeClasspathTest.java b/src/test/java/com/android/tools/r8/desugar/sealed/SealedAttributeClasspathTest.java
new file mode 100644
index 0000000..a74132f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/sealed/SealedAttributeClasspathTest.java
@@ -0,0 +1,60 @@
+// Copyright (c) 2022, 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.desugar.sealed;
+
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestCompilerBuilder;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.examples.jdk17.Sealed;
+import com.android.tools.r8.utils.InternalOptions.TestingOptions;
+import java.util.List;
+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 SealedAttributeClasspathTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withAllRuntimesAndApiLevels().build());
+  }
+
+  private void runTest(TestCompilerBuilder<?, ?, ?, ?, ?> builder) throws Exception {
+    builder
+        .addClasspathFiles(Sealed.jar())
+        .addInnerClasses(getClass())
+        .setMinApi(parameters.getApiLevel())
+        .addOptionsModification(TestingOptions::allowExperimentClassFileVersion)
+        .run(parameters.getRuntime(), TestRunner.class)
+        .assertSuccessWithOutputLines("Hello, world!");
+  }
+
+  @Test
+  public void testD8() throws Exception {
+    assumeTrue(parameters.isDexRuntime());
+    runTest(testForD8(parameters.getBackend()));
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    runTest(testForR8(parameters.getBackend()).addKeepMainRule(TestRunner.class));
+  }
+
+  public static class TestRunner {
+
+    public static void main(String[] args) {
+      System.out.println("Hello, world!");
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/sealed/SealedAttributeLibraryTest.java b/src/test/java/com/android/tools/r8/desugar/sealed/SealedAttributeLibraryTest.java
new file mode 100644
index 0000000..4eb4bd2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/sealed/SealedAttributeLibraryTest.java
@@ -0,0 +1,61 @@
+// Copyright (c) 2022, 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.desugar.sealed;
+
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestCompilerBuilder;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.examples.jdk17.Sealed;
+import com.android.tools.r8.utils.InternalOptions.TestingOptions;
+import java.util.List;
+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 SealedAttributeLibraryTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withAllRuntimesAndApiLevels().build());
+  }
+
+  private void runTest(TestCompilerBuilder<?, ?, ?, ?, ?> builder) throws Exception {
+    builder
+        .addDefaultRuntimeLibrary(parameters)
+        .addLibraryFiles(Sealed.jar())
+        .addInnerClasses(getClass())
+        .setMinApi(parameters.getApiLevel())
+        .addOptionsModification(TestingOptions::allowExperimentClassFileVersion)
+        .run(parameters.getRuntime(), TestRunner.class)
+        .assertSuccessWithOutputLines("Hello, world!");
+  }
+
+  @Test
+  public void testD8() throws Exception {
+    assumeTrue(parameters.isDexRuntime());
+    runTest(testForD8(parameters.getBackend()));
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    runTest(testForR8(parameters.getBackend()).addKeepMainRule(TestRunner.class));
+  }
+
+  public static class TestRunner {
+
+    public static void main(String[] args) {
+      System.out.println("Hello, world!");
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/sealed/SealedAttributeTest.java b/src/test/java/com/android/tools/r8/desugar/sealed/SealedAttributeTest.java
index 710f6f5..35293b5 100644
--- a/src/test/java/com/android/tools/r8/desugar/sealed/SealedAttributeTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/sealed/SealedAttributeTest.java
@@ -58,7 +58,8 @@
               .compileWithExpectedDiagnostics(
                   diagnostics -> {
                     diagnostics.assertErrorThatMatches(
-                        diagnosticMessage(containsString("Sealed classes are not supported")));
+                        diagnosticMessage(
+                            containsString("Sealed classes are not supported as program classes")));
                   });
         });
   }
@@ -75,7 +76,8 @@
               .compileWithExpectedDiagnostics(
                   diagnostics -> {
                     diagnostics.assertErrorThatMatches(
-                        diagnosticMessage(containsString("Sealed classes are not supported")));
+                        diagnosticMessage(
+                            containsString("Sealed classes are not supported as program classes")));
                   });
         });
   }
diff --git a/src/test/java/com/android/tools/r8/desugar/suppressedexceptions/SuppressedExceptionsTest.java b/src/test/java/com/android/tools/r8/desugar/suppressedexceptions/SuppressedExceptionsTest.java
new file mode 100644
index 0000000..e1d397a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/suppressedexceptions/SuppressedExceptionsTest.java
@@ -0,0 +1,143 @@
+// Copyright (c) 2022, 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.desugar.suppressedexceptions;
+
+import static com.android.tools.r8.desugar.suppressedexceptions.TwrSuppressedExceptionsTest.getInvokesTo;
+import static com.android.tools.r8.desugar.suppressedexceptions.TwrSuppressedExceptionsTest.hasInvokesTo;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.DesugarTestConfiguration;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.IntBox;
+import com.android.tools.r8.utils.StringUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class SuppressedExceptionsTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
+  }
+
+  public SuppressedExceptionsTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  public boolean runtimeHasSuppressedExceptionsSupport() {
+    // TODO(b/214239152): Update this if desugaring is changed.
+    // Despite 4.0.4 being API level 15 and add suppressed being officially added in 19 it is
+    // actually implemented. Thus, the backport implementation will use the functionality and run
+    // as expected by RI.
+    return parameters.isCfRuntime()
+        || parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V4_0_4);
+  }
+
+  public boolean apiLevelHasSuppressedExceptionsSupport() {
+    return parameters
+        .getApiLevel()
+        .isGreaterThanOrEqualTo(apiLevelWithSuppressedExceptionsSupport());
+  }
+
+  @Test
+  public void testD8() throws Exception {
+    testForDesugaring(parameters)
+        .addProgramClasses(TestClass.class)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(
+            runtimeHasSuppressedExceptionsSupport() ? StringUtils.lines("FOO") : "NONE")
+        .inspectIf(
+            DesugarTestConfiguration::isDesugared,
+            inspector ->
+                hasInvokesTo(
+                    inspector.clazz(TestClass.class).uniqueMethodWithName("main"),
+                    "getSuppressed",
+                    apiLevelHasSuppressedExceptionsSupport() ? 1 : 0))
+        .inspectIf(
+            DesugarTestConfiguration::isNotDesugared,
+            inspector ->
+                hasInvokesTo(
+                    inspector.clazz(TestClass.class).uniqueMethodWithName("main"),
+                    "getSuppressed",
+                    1));
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    assumeTrue(
+        "R8 does not desugar CF so only run the high API variant.",
+        parameters.isDexRuntime() || parameters.getApiLevel().isGreaterThan(AndroidApiLevel.B));
+    testForR8(parameters.getBackend())
+        .addInnerClasses(SuppressedExceptionsTest.class)
+        .setMinApi(parameters.getApiLevel())
+        .addKeepMainRule(TestClass.class)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(
+            runtimeHasSuppressedExceptionsSupport() ? StringUtils.lines("FOO") : "NONE")
+        .inspect(
+            inspector -> {
+              hasInvokesTo(
+                  inspector.clazz(TestClass.class).uniqueMethodWithName("main"),
+                  "getSuppressed",
+                  apiLevelHasSuppressedExceptionsSupport() ? 1 : 0);
+              IntBox gets = new IntBox(0);
+              IntBox adds = new IntBox(0);
+              inspector.forAllClasses(
+                  c ->
+                      c.forAllMethods(
+                          m -> {
+                            gets.increment(getInvokesTo(m, "getSuppressed").size());
+                            adds.increment(getInvokesTo(m, "addSuppressed").size());
+                          }));
+              if (apiLevelHasSuppressedExceptionsSupport()) {
+                assertEquals(1, gets.get());
+                assertEquals(1, adds.get());
+              } else {
+                assertEquals(0, gets.get());
+                assertEquals(0, adds.get());
+              }
+            });
+  }
+
+  static class TestClass {
+
+    public static void foo() {
+      throw new RuntimeException("FOO");
+    }
+
+    public static void bar() {
+      try {
+        foo();
+      } catch (RuntimeException e) {
+        RuntimeException bar = new RuntimeException("BAR");
+        bar.addSuppressed(e);
+        throw bar;
+      }
+    }
+
+    public static void main(String[] args) {
+      try {
+        bar();
+      } catch (RuntimeException e) {
+        Throwable[] suppressed = e.getSuppressed();
+        if (suppressed.length == 0) {
+          System.out.println("NONE");
+        } else {
+          for (Throwable throwable : suppressed) {
+            System.out.println(throwable.getMessage());
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/suppressedexceptions/TwrSuppressedExceptionsTest.java b/src/test/java/com/android/tools/r8/desugar/suppressedexceptions/TwrSuppressedExceptionsTest.java
new file mode 100644
index 0000000..e80af63
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/suppressedexceptions/TwrSuppressedExceptionsTest.java
@@ -0,0 +1,188 @@
+// Copyright (c) 2022, 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.desugar.suppressedexceptions;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.DesugarTestConfiguration;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.IntBox;
+import com.android.tools.r8.utils.StringUtils;
+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 java.io.Closeable;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class TwrSuppressedExceptionsTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
+  }
+
+  public TwrSuppressedExceptionsTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  public boolean runtimeHasSuppressedExceptionsSupport() {
+    // TODO(b/214239152): Update this if desugaring is changed.
+    // Despite 4.0.4 being API level 15 and add suppressed being officially added in 19 it is
+    // actually implemented. Thus, the backport implementation will use the functionality and run
+    // as expected by RI.
+    return parameters.isCfRuntime()
+        || parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V4_0_4);
+  }
+
+  public boolean apiLevelHasSuppressedExceptionsSupport() {
+    return parameters
+        .getApiLevel()
+        .isGreaterThanOrEqualTo(apiLevelWithSuppressedExceptionsSupport());
+  }
+
+  public boolean apiLevelHasTwrCloseResourceSupport() {
+    return parameters.getApiLevel().isGreaterThanOrEqualTo(apiLevelWithTwrCloseResourceSupport());
+  }
+
+  @Test
+  public void testD8() throws Exception {
+    testForDesugaring(parameters)
+        .addProgramClasses(TestClass.class, MyClosable.class)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(
+            runtimeHasSuppressedExceptionsSupport() ? StringUtils.lines("CLOSE") : "NONE")
+        .inspectIf(
+            DesugarTestConfiguration::isDesugared,
+            inspector -> {
+              ClassSubject clazz = inspector.clazz(TestClass.class);
+              hasInvokesTo(
+                  clazz.uniqueMethodWithName("bar"),
+                  "$closeResource",
+                  apiLevelHasTwrCloseResourceSupport() ? 4 : 0);
+              if (apiLevelHasSuppressedExceptionsSupport()) {
+                hasInvokesTo(clazz.mainMethod(), "getSuppressed", 1);
+              } else {
+                inspector.forAllClasses(
+                    c ->
+                        c.forAllMethods(
+                            m -> {
+                              hasInvokesTo(m, "getSuppressed", 0);
+                              hasInvokesTo(m, "addSuppressed", 0);
+                            }));
+              }
+            })
+        .inspectIf(
+            DesugarTestConfiguration::isNotDesugared,
+            inspector -> {
+              ClassSubject clazz = inspector.clazz(TestClass.class);
+              hasInvokesTo(clazz.uniqueMethodWithName("bar"), "$closeResource", 4);
+              hasInvokesTo(clazz.mainMethod(), "getSuppressed", 1);
+            });
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    assumeTrue(
+        "R8 does not desugar CF so only run the high API variant.",
+        parameters.isDexRuntime() || parameters.getApiLevel().isGreaterThan(AndroidApiLevel.B));
+    testForR8(parameters.getBackend())
+        .addInnerClasses(TwrSuppressedExceptionsTest.class)
+        .setMinApi(parameters.getApiLevel())
+        .addKeepMainRule(TestClass.class)
+        // TODO(b/214250388): Don't warn about AutoClosable in synthesized code.
+        .apply(
+            b -> {
+              if (!parameters.isCfRuntime() && !apiLevelHasTwrCloseResourceSupport()) {
+                b.addDontWarn(AutoCloseable.class);
+              }
+            })
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(
+            runtimeHasSuppressedExceptionsSupport() ? StringUtils.lines("CLOSE") : "NONE")
+        .inspect(
+            inspector -> {
+              IntBox gets = new IntBox(0);
+              IntBox adds = new IntBox(0);
+              inspector.forAllClasses(
+                  c ->
+                      c.forAllMethods(
+                          m -> {
+                            gets.increment(getInvokesTo(m, "getSuppressed").size());
+                            adds.increment(getInvokesTo(m, "addSuppressed").size());
+                          }));
+              if (apiLevelHasSuppressedExceptionsSupport()) {
+                hasInvokesTo(inspector.clazz(TestClass.class).mainMethod(), "getSuppressed", 1);
+                assertEquals(1, gets.get());
+                assertEquals(1, adds.get());
+              } else {
+                assertEquals(0, gets.get());
+                assertEquals(0, adds.get());
+              }
+            });
+  }
+
+  public static void hasInvokesTo(MethodSubject method, String callee, int count) {
+    List<InstructionSubject> getSuppressedCalls = getInvokesTo(method, callee);
+    assertEquals(count, getSuppressedCalls.size());
+  }
+
+  public static List<InstructionSubject> getInvokesTo(MethodSubject method, String callee) {
+    return method
+        .streamInstructions()
+        .filter(i -> i.isInvoke() && i.getMethod().getName().toString().equals(callee))
+        .collect(Collectors.toList());
+  }
+
+  static class MyClosable implements Closeable {
+
+    @Override
+    public void close() {
+      throw new RuntimeException("CLOSE");
+    }
+  }
+
+  static class TestClass {
+
+    public static void foo() {
+      throw new RuntimeException("FOO");
+    }
+
+    public static void bar() {
+      // Use twr twice to have javac generate a shared $closeResource helper.
+      try (MyClosable closable = new MyClosable()) {
+        foo();
+      }
+      try (MyClosable closable = new MyClosable()) {
+        foo();
+      }
+    }
+
+    public static void main(String[] args) {
+      try {
+        bar();
+      } catch (Exception e) {
+        Throwable[] suppressed = e.getSuppressed();
+        if (suppressed.length == 0) {
+          System.out.println("NONE");
+        } else {
+          for (Throwable throwable : suppressed) {
+            System.out.println(throwable.getMessage());
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/twr/TwrCloseResourceDuplicationTest.java b/src/test/java/com/android/tools/r8/desugar/twr/TwrCloseResourceDuplicationTest.java
index 1f15b61..10d15e9 100644
--- a/src/test/java/com/android/tools/r8/desugar/twr/TwrCloseResourceDuplicationTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/twr/TwrCloseResourceDuplicationTest.java
@@ -75,10 +75,14 @@
         .assertSuccessWithOutput(EXPECTED)
         .inspect(
             inspector -> {
-              // There should be exactly one synthetic class besides the three program classes.
+              // There should be two synthetic classes besides the three program classes.
+              // One for the desugar version of TWR $closeResource and one for the
+              // Throwable.addSuppressed that is still present in the original $closeResource.
+              // TODO(b/214329923): If the original $closeResource is pruned this will decrease.
+              // TODO(b/168568827): Once we support a nested addSuppressed this will increase.
               int expectedSynthetics =
                   parameters.getApiLevel().isLessThan(apiLevelWithTwrCloseResourceSupport())
-                      ? 1
+                      ? 2
                       : 0;
               assertEquals(INPUT_CLASSES + expectedSynthetics, inspector.allClasses().size());
             });
@@ -91,6 +95,7 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(TestClass.class)
         .addKeepClassAndMembersRules(Foo.class, Bar.class)
+        // TODO(b/214250388): Don't warn about synthetic code.
         .applyIf(
             parameters.getApiLevel().isLessThan(apiLevelWithTwrCloseResourceSupport()),
             builder -> builder.addDontWarn("java.lang.AutoCloseable"))
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/FailingMethodEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/FailingMethodEnumUnboxingTest.java
index ce3e3f9..0a00994 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/FailingMethodEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/FailingMethodEnumUnboxingTest.java
@@ -64,6 +64,8 @@
                         FailingParameterType.MyEnum.class))
             .enableInliningAnnotations()
             .enableNeverClassInliningAnnotations()
+            // TODO(b/173398086): uniqueMethodWithName() does not work with signature changes.
+            .noMinification()
             .setMinApi(parameters.getApiLevel())
             .compile()
             .inspect(this::assertEnumsAsExpected);
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/backports/CloseResourceMethod.java b/src/test/java/com/android/tools/r8/ir/desugar/backports/CloseResourceMethod.java
index 1167e5a..fb6a937 100644
--- a/src/test/java/com/android/tools/r8/ir/desugar/backports/CloseResourceMethod.java
+++ b/src/test/java/com/android/tools/r8/ir/desugar/backports/CloseResourceMethod.java
@@ -49,9 +49,18 @@
         }
       }
     } catch (Throwable e) {
-      // NOTE: we don't call addSuppressed(...) since the call will be removed
-      // by try-with-resource desugar anyways.
-      throw throwable != null ? throwable : e;
+      if (throwable != null) {
+        // TODO(b/168568827): Directly call Throwable.addSuppressed once fixed.
+        try {
+          Method method = Throwable.class.getDeclaredMethod("addSuppressed", Throwable.class);
+          method.invoke(throwable, e);
+        } catch (Exception ignore) {
+          // Don't add anything when not natively supported.
+        }
+        throw throwable;
+      } else {
+        throw e;
+      }
     }
   }
 }
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java b/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java
index ce75bd9..f955ca3 100644
--- a/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java
+++ b/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java
@@ -51,6 +51,7 @@
           ShortMethods.class,
           StreamMethods.class,
           StringMethods.class,
+          ThrowableMethods.class,
           UnsafeMethods.class);
 
   protected final TestParameters parameters;
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/backports/ThrowableMethods.java b/src/test/java/com/android/tools/r8/ir/desugar/backports/ThrowableMethods.java
new file mode 100644
index 0000000..ba17959
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/desugar/backports/ThrowableMethods.java
@@ -0,0 +1,29 @@
+// Copyright (c) 2022, 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.desugar.backports;
+
+import java.lang.reflect.Method;
+
+public final class ThrowableMethods {
+
+  public static void addSuppressed(Throwable receiver, Throwable suppressed) {
+    try {
+      Method method = Throwable.class.getDeclaredMethod("addSuppressed", Throwable.class);
+      method.invoke(receiver, suppressed);
+    } catch (Exception e) {
+      // Don't add anything when not natively supported.
+    }
+  }
+
+  public static Throwable[] getSuppressed(Throwable receiver) {
+    try {
+      Method method = Throwable.class.getDeclaredMethod("getSuppressed");
+      return (Throwable[]) method.invoke(receiver);
+    } catch (Exception e) {
+      // Don't return any when not natively supported.
+      return new Throwable[0];
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
index fba166a..62a44a1 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
@@ -326,7 +326,11 @@
     m = clazz.method("int", "inlinable", ImmutableList.of("inlining.A"));
     assertCounters(INLINABLE, INLINABLE, countInvokes(inspector, m));
 
-    m = clazz.method("int", "notInlinable", ImmutableList.of("inlining.A"));
+    m =
+        clazz.method(
+            "int",
+            "notInlinable",
+            ImmutableList.of("inlining." + (allowAccessModification ? "B" : "A")));
     assertCounters(INLINABLE, NEVER_INLINABLE, countInvokes(inspector, m));
 
     m = clazz.method("int", "notInlinableDueToMissingNpe", ImmutableList.of("inlining.A"));
@@ -338,14 +342,16 @@
         NEVER_INLINABLE,
         countInvokes(inspector, m));
 
-    m = clazz.method("int", "notInlinableOnThrow", ImmutableList.of("java.lang.Throwable"));
+    m =
+        clazz.method(
+            "int", "notInlinableOnThrow", ImmutableList.of("java.lang.IllegalArgumentException"));
     assertCounters(ALWAYS_INLINABLE, NEVER_INLINABLE, countInvokes(inspector, m));
 
     m =
         clazz.method(
             "int",
             "notInlinableDueToMissingNpeBeforeThrow",
-            ImmutableList.of("java.lang.Throwable"));
+            ImmutableList.of("java.lang.IllegalArgumentException"));
     assertCounters(ALWAYS_INLINABLE, NEVER_INLINABLE * 2, countInvokes(inspector, m));
   }
 
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectPositiveTest.java
index 5811471..0cd06dc 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectPositiveTest.java
@@ -9,6 +9,7 @@
 
 import com.android.tools.r8.NeverClassInline;
 import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoParameterTypeStrengthening;
 import com.android.tools.r8.NoVerticalClassMerging;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
@@ -43,6 +44,7 @@
     testForR8(parameters.getBackend())
         .addInnerClasses(InvokeDirectPositiveTest.class)
         .addKeepMainRule(MAIN)
+        .enableNoParameterTypeStrengtheningAnnotations()
         .enableNoVerticalClassMergingAnnotations()
         .enableNeverClassInliningAnnotations()
         .enableInliningAnnotations()
@@ -107,6 +109,7 @@
     }
 
     @NeverInline
+    @NoParameterTypeStrengthening
     private void test(Base arg) {
       if (arg instanceof Sub1) {
         System.out.println("Sub1");
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticPositiveTest.java
index a32daad..2dafd1b 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticPositiveTest.java
@@ -8,6 +8,7 @@
 import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoParameterTypeStrengthening;
 import com.android.tools.r8.NoVerticalClassMerging;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
@@ -42,6 +43,7 @@
     testForR8(parameters.getBackend())
         .addInnerClasses(InvokeStaticPositiveTest.class)
         .addKeepMainRule(MAIN)
+        .enableNoParameterTypeStrengtheningAnnotations()
         .enableNoVerticalClassMergingAnnotations()
         .enableInliningAnnotations()
         .addOptionsModification(
@@ -95,6 +97,7 @@
       test(new Sub1()); // calls test with Sub1.
     }
 
+    @NoParameterTypeStrengthening
     @NeverInline
     static void test(Base arg) {
       if (arg instanceof Sub1) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualPositiveTest.java
index 99a3ddf..382be79 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualPositiveTest.java
@@ -9,6 +9,7 @@
 
 import com.android.tools.r8.NeverClassInline;
 import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoParameterTypeStrengthening;
 import com.android.tools.r8.NoVerticalClassMerging;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
@@ -43,6 +44,7 @@
     testForR8(parameters.getBackend())
         .addInnerClasses(InvokeVirtualPositiveTest.class)
         .addKeepMainRule(MAIN)
+        .enableNoParameterTypeStrengtheningAnnotations()
         .enableNoVerticalClassMergingAnnotations()
         .enableNeverClassInliningAnnotations()
         .enableInliningAnnotations()
@@ -110,6 +112,7 @@
   @NoVerticalClassMerging
   @NeverClassInline
   static class A {
+    @NoParameterTypeStrengthening
     @NeverInline
     void m(Base arg) {
       if (arg instanceof Sub1) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeDirectPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeDirectPositiveTest.java
index fd605ac..11c44ad 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeDirectPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeDirectPositiveTest.java
@@ -57,7 +57,7 @@
         : "Unexpected revisit: " + method.toSourceString();
     CallSiteOptimizationInfo callSiteOptimizationInfo =
         method.getOptimizationInfo().getArgumentInfos();
-    assert callSiteOptimizationInfo.getDynamicType(1).isNotNullType();
+    assertTrue(callSiteOptimizationInfo.getDynamicType(1).getNullability().isDefinitelyNotNull());
   }
 
   private void inspect(CodeInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeStaticPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeStaticPositiveTest.java
index cf5cbe1..d181d3b 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeStaticPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeStaticPositiveTest.java
@@ -55,7 +55,7 @@
         : "Unexpected revisit: " + method.toSourceString();
     CallSiteOptimizationInfo callSiteOptimizationInfo =
         method.getOptimizationInfo().getArgumentInfos();
-    assert callSiteOptimizationInfo.getDynamicType(0).isNotNullType();
+    assertTrue(callSiteOptimizationInfo.getDynamicType(0).getNullability().isDefinitelyNotNull());
   }
 
   private void inspect(CodeInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/dynamictype/DynamicTypeOptimizationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/dynamictype/DynamicTypeOptimizationTest.java
index 52736da..f3db3a7 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/dynamictype/DynamicTypeOptimizationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/dynamictype/DynamicTypeOptimizationTest.java
@@ -10,6 +10,7 @@
 import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoReturnTypeStrengthening;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.utils.StringUtils;
@@ -43,6 +44,7 @@
         // Keep B to ensure that we will treat it as being instantiated.
         .addKeepClassRulesWithAllowObfuscation(B.class)
         .enableInliningAnnotations()
+        .enableNoReturnTypeStrengtheningAnnotations()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(this::inspect)
@@ -134,6 +136,7 @@
     }
 
     @NeverInline
+    @NoReturnTypeStrengthening
     private static I get() {
       return new A();
     }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsMixedTest.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsMixedTest.java
index 78a0882..a6f80c4 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsMixedTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsMixedTest.java
@@ -78,8 +78,11 @@
           Assert.assertTrue(
               method.getFinalName().equals("main")
                   || (method.getFinalSignature().parameters.length == 1
-                  && (method.getFinalSignature().parameters[0].equals("int")
-                      || method.getFinalSignature().parameters[0].equals("java.lang.Object"))));
+                      && (method.getFinalSignature().parameters[0].equals("int")
+                          || method
+                              .getFinalSignature()
+                              .parameters[0]
+                              .equals("java.lang.Integer"))));
         });
   }
 }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceWithDefaultMethodTest.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceWithDefaultMethodTest.java
index c10d1db..fdefaac 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceWithDefaultMethodTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceWithDefaultMethodTest.java
@@ -12,6 +12,7 @@
 import com.android.tools.r8.NeverClassInline;
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.NoParameterTypeStrengthening;
 import com.android.tools.r8.NoVerticalClassMerging;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
@@ -45,6 +46,7 @@
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .enableNoHorizontalClassMergingAnnotations()
+        .enableNoParameterTypeStrengtheningAnnotations()
         .enableNoVerticalClassMergingAnnotations()
         .setMinApi(parameters.getApiLevel())
         .compile()
@@ -82,6 +84,7 @@
     }
 
     @NeverInline
+    @NoParameterTypeStrengthening
     private static void indirection(I obj) {
       obj.m();
     }
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineChainTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineChainTest.java
index f72637c..f9e965c 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineChainTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineChainTest.java
@@ -78,7 +78,11 @@
               MethodSubject main = mainClass.mainMethod();
               long checkParameterIsNotNull = countCall(main, "checkParameterIsNotNull");
               long checkNotNullParameter = countCall(main, "checkNotNullParameter");
-              if (parameters.isDexRuntime()
+              if (kotlinc.getCompilerVersion().isGreaterThan(KotlinCompilerVersion.KOTLINC_1_6_0)) {
+                assertEquals(
+                    BooleanUtils.intValue(!allowAccessModification), checkNotNullParameter);
+                assertEquals(0, checkParameterIsNotNull);
+              } else if (parameters.isDexRuntime()
                   && parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.I)) {
                 assertEquals(0, checkNotNullParameter);
                 assertEquals(0, checkParameterIsNotNull);
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInCompanionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInCompanionTest.java
index eae75ac..472435a 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInCompanionTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInCompanionTest.java
@@ -128,6 +128,8 @@
             .addKeepRules("-keepclassmembers class **.B { *** Companion; }")
             // Keep the class of the companion class.
             .addKeepRules("-keep class **.*$Companion")
+            // TODO(b/173398086): uniqueMethodWithName() does not work with signature changes.
+            .addKeepRules("-noreturntypestrengthening class **.B { *** access$getElt1$cp(...); }")
             // No rule for Super, but will be kept and renamed.
             .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
             // To keep @JvmField annotation
@@ -135,6 +137,7 @@
             // To keep ...$Companion structure
             .addKeepAttributes(ProguardKeepAttributes.INNER_CLASSES)
             .addKeepAttributes(ProguardKeepAttributes.ENCLOSING_METHOD)
+            .enableProguardTestOptions()
             .compile()
             .inspect(codeInspector -> inspect(codeInspector, false))
             .writeToZip();
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexDevirtualizerTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexDevirtualizerTest.java
index ad0cf84..783b9f1 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexDevirtualizerTest.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexDevirtualizerTest.java
@@ -16,6 +16,7 @@
 
 import com.android.tools.r8.NeverClassInline;
 import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoReturnTypeStrengthening;
 import com.android.tools.r8.NoVerticalClassMerging;
 import com.android.tools.r8.R8FullTestBuilder;
 import com.android.tools.r8.TestBase;
@@ -108,6 +109,7 @@
     Box<String> mainDexStringList = new Box<>("");
     testForR8(parameters.getBackend())
         .addProgramClasses(I.class, Provider.class, A.class, Main.class)
+        .enableNoReturnTypeStrengtheningAnnotations()
         .enableNoVerticalClassMergingAnnotations()
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
@@ -156,6 +158,7 @@
 
   public static class Provider {
     @NeverInline
+    @NoReturnTypeStrengthening
     public static I getImpl() {
       return new A(); // <-- We will call-site optimize getImpl() to always return A.
     }
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveInterfaceBridgeWithSubTypeTest.java b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveInterfaceBridgeWithSubTypeTest.java
index f1fc86d..25674a2 100644
--- a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveInterfaceBridgeWithSubTypeTest.java
+++ b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveInterfaceBridgeWithSubTypeTest.java
@@ -11,6 +11,7 @@
 
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.NoParameterTypeStrengthening;
 import com.android.tools.r8.NoVerticalClassMerging;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
@@ -51,6 +52,7 @@
         .addKeepClassAndMembersRules(I.class)
         .enableInliningAnnotations()
         .enableNoHorizontalClassMergingAnnotations()
+        .enableNoParameterTypeStrengtheningAnnotations()
         .enableNoVerticalClassMergingAnnotations()
         .addHorizontallyMergedClassesInspector(
             HorizontallyMergedClassesInspector::assertNoClassesMerged)
@@ -94,6 +96,7 @@
     }
 
     @NeverInline
+    @NoParameterTypeStrengthening
     private static void callJ(A a) {
       a.foo();
     }
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveInterfaceDefaultBridgeTest.java b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveInterfaceDefaultBridgeTest.java
index e11bdfe..4036e4a 100644
--- a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveInterfaceDefaultBridgeTest.java
+++ b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveInterfaceDefaultBridgeTest.java
@@ -11,6 +11,7 @@
 
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.NoParameterTypeStrengthening;
 import com.android.tools.r8.NoVerticalClassMerging;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
@@ -49,6 +50,7 @@
         .addKeepClassAndMembersRules(I.class)
         .enableInliningAnnotations()
         .enableNoHorizontalClassMergingAnnotations()
+        .enableNoParameterTypeStrengtheningAnnotations()
         .enableNoVerticalClassMergingAnnotations()
         .run(parameters.getRuntime(), newMainTypeName)
         .assertSuccessWithOutputLines("I::foo")
@@ -85,6 +87,7 @@
     }
 
     @NeverInline
+    @NoParameterTypeStrengthening
     private static void callJ(J j) {
       j.foo();
     }
diff --git a/src/test/java/com/android/tools/r8/naming/overloadaggressively/B.java b/src/test/java/com/android/tools/r8/naming/overloadaggressively/B.java
index 298c597..c18b3d7 100644
--- a/src/test/java/com/android/tools/r8/naming/overloadaggressively/B.java
+++ b/src/test/java/com/android/tools/r8/naming/overloadaggressively/B.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8.naming.overloadaggressively;
 
 import com.android.tools.r8.NeverPropagateValue;
+import com.android.tools.r8.NoReturnTypeStrengthening;
 
 public class B {
   volatile int f1 = 8;
@@ -16,6 +17,7 @@
   }
 
   @NeverPropagateValue
+  @NoReturnTypeStrengthening
   public Object getF2() {
     return f2;
   }
diff --git a/src/test/java/com/android/tools/r8/naming/overloadaggressively/OverloadAggressivelyTest.java b/src/test/java/com/android/tools/r8/naming/overloadaggressively/OverloadAggressivelyTest.java
index cbd22a0..c54b51b 100644
--- a/src/test/java/com/android/tools/r8/naming/overloadaggressively/OverloadAggressivelyTest.java
+++ b/src/test/java/com/android/tools/r8/naming/overloadaggressively/OverloadAggressivelyTest.java
@@ -11,6 +11,8 @@
 
 import com.android.tools.r8.R8Command;
 import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.graph.DexEncodedField;
@@ -27,19 +29,18 @@
 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 OverloadAggressivelyTest extends TestBase {
 
-  private Backend backend;
+  @Parameter(0)
+  public TestParameters parameters;
 
-  @Parameterized.Parameters(name = "Backend: {0}")
-  public static Backend[] data() {
-    return ToolHelper.getBackends();
-  }
-
-  public OverloadAggressivelyTest(Backend backend) {
-    this.backend = backend;
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withCfRuntimes().build();
   }
 
   private AndroidApp runR8(AndroidApp app, Class<?> main, Path out, boolean overloadaggressively)
@@ -56,8 +57,8 @@
                     keepMainProguardConfiguration(main),
                     overloadaggressively ? "-overloadaggressively" : ""),
                 Origin.unknown())
-            .setOutput(out, outputMode(backend))
-            .addLibraryFiles(TestBase.runtimeJar(backend))
+            .setOutput(out, outputMode(parameters.getBackend()))
+            .addLibraryFiles(parameters.getDefaultRuntimeLibrary())
             .build();
     return ToolHelper.runR8(
         command,
@@ -68,10 +69,10 @@
   }
 
   private ProcessResult runRaw(AndroidApp app, String main) throws IOException {
-    if (backend == Backend.DEX) {
+    if (parameters.isDexRuntime()) {
       return runOnArtRaw(app, main);
     } else {
-      assert backend == Backend.CF;
+      assert parameters.isCfRuntime();
       return runOnJavaRaw(app, main, Collections.emptyList());
     }
   }
@@ -178,19 +179,20 @@
     String expected = StringUtils.lines("diff: 0", "d8 v.s. d8", "r8 v.s. r8");
     String expectedOverloadAggressively = StringUtils.lines("diff: 0", "d8 v.s. 8", "r8 v.s. 8");
 
-    if (backend.isCf()) {
+    if (parameters.isCfRuntime()) {
       testForJvm().addTestClasspath().run(MethodResolution.class).assertSuccessWithOutput(expected);
     }
 
-    testForR8Compat(backend)
+    testForR8Compat(parameters.getBackend())
         .addProgramClasses(MethodResolution.class, B.class)
         .addKeepMainRule(MethodResolution.class)
         .addOptionsModification(options -> options.inlinerOptions().enableInlining = false)
         .applyIf(overloadaggressively, builder -> builder.addKeepRules("-overloadaggressively"))
         .enableMemberValuePropagationAnnotations()
+        .enableNoReturnTypeStrengtheningAnnotations()
         .compile()
         .inspect(inspector -> inspect(inspector, overloadaggressively))
-        .run(MethodResolution.class)
+        .run(parameters.getRuntime(), MethodResolution.class)
         .applyIf(
             overloadaggressively,
             runResult -> runResult.assertSuccessWithOutput(expectedOverloadAggressively),
@@ -218,7 +220,7 @@
 
   @Test
   public void testMethodResolution_aggressively() throws Exception {
-    assumeTrue(backend == Backend.CF);
+    assumeTrue(parameters.isCfRuntime());
     methodResolution(true);
   }
 
diff --git a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/ParameterTypeStrengtheningTest.java b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/ParameterTypeStrengtheningTest.java
new file mode 100644
index 0000000..91abde9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/ParameterTypeStrengtheningTest.java
@@ -0,0 +1,124 @@
+// Copyright (c) 2022, 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.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
+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 ParameterTypeStrengtheningTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(Main.class)
+        .enableInliningAnnotations()
+        .enableNeverClassInliningAnnotations()
+        .enableNoHorizontalClassMergingAnnotations()
+        // TODO(b/173398086): uniqueMethodWithName() does not work with argument changes.
+        .noMinification()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .inspect(
+            inspector -> {
+              ClassSubject mainClassSubject = inspector.clazz(Main.class);
+              assertThat(mainClassSubject, isPresent());
+
+              ClassSubject aClassSubject = inspector.clazz(A.class);
+              assertThat(aClassSubject, isPresent());
+
+              ClassSubject bClassSubject = inspector.clazz(B.class);
+              assertThat(bClassSubject, isPresent());
+
+              // Method testA(I) should be rewritten to testA(A).
+              MethodSubject testAMethodSubject = mainClassSubject.uniqueMethodWithName("testA");
+              assertThat(testAMethodSubject, isPresent());
+              assertEquals(
+                  aClassSubject.getFinalName(),
+                  testAMethodSubject.getProgramMethod().getParameter(0).getTypeName());
+
+              // Method testB(I) should be rewritten to testB(B).
+              MethodSubject testBMethodSubject = mainClassSubject.uniqueMethodWithName("testB");
+              assertThat(testBMethodSubject, isPresent());
+              assertEquals(
+                  bClassSubject.getFinalName(),
+                  testBMethodSubject.getProgramMethod().getParameter(0).getTypeName());
+            })
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("A", "B");
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      testA(new A());
+      testB(getB());
+    }
+
+    @NeverInline
+    static void testA(I i) {
+      i.m();
+    }
+
+    @NeverInline
+    static void testB(I i) {
+      i.m();
+    }
+
+    @NeverInline
+    static I getB() {
+      return new B();
+    }
+  }
+
+  interface I {
+
+    void m();
+  }
+
+  @NeverClassInline
+  @NoHorizontalClassMerging
+  static class A implements I {
+
+    @Override
+    public void m() {
+      System.out.println("A");
+    }
+  }
+
+  @NeverClassInline
+  @NoHorizontalClassMerging
+  static class B implements I {
+
+    @Override
+    public void m() {
+      System.out.println("B");
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/ReturnTypeStrengtheningTest.java b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/ReturnTypeStrengtheningTest.java
new file mode 100644
index 0000000..89dccdb
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/ReturnTypeStrengtheningTest.java
@@ -0,0 +1,116 @@
+// Copyright (c) 2022, 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.NoVerticalClassMerging;
+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.FoundClassSubject;
+import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
+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 ReturnTypeStrengtheningTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(Main.class)
+        .enableInliningAnnotations()
+        .enableNoVerticalClassMergingAnnotations()
+        // TODO(b/173398086): uniqueMethodWithName() does not work with argument changes.
+        .noMinification()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .inspect(
+            inspector -> {
+              ClassSubject mainClassSubject = inspector.clazz(Main.class);
+              assertThat(mainClassSubject, isPresent());
+
+              ClassSubject aClassSubject = inspector.clazz(A.class);
+              assertThat(aClassSubject, isPresent());
+
+              // Return type of get() should be strengthened to A.
+              MethodSubject getMethodSubject = mainClassSubject.uniqueMethodWithName("get");
+              assertThat(getMethodSubject, isPresent());
+              assertEquals(
+                  aClassSubject.getFinalName(),
+                  getMethodSubject.getProgramMethod().getReturnType().getTypeName());
+
+              // Method consume(I) should be rewritten to consume(A).
+              MethodSubject testBMethodSubject = mainClassSubject.uniqueMethodWithName("consume");
+              assertThat(testBMethodSubject, isPresent());
+              assertEquals(
+                  aClassSubject.getFinalName(),
+                  testBMethodSubject.getProgramMethod().getParameter(0).getTypeName());
+
+              // There should be no casts in the application.
+              for (FoundClassSubject classSubject : inspector.allClasses()) {
+                for (FoundMethodSubject methodSubject : classSubject.allMethods()) {
+                  assertTrue(
+                      methodSubject
+                          .streamInstructions()
+                          .noneMatch(InstructionSubject::isCheckCast));
+                }
+              }
+            })
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("A");
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      consume(get());
+    }
+
+    @NeverInline
+    static I get() {
+      return new A();
+    }
+
+    @NeverInline
+    static void consume(I i) {
+      i.m();
+    }
+  }
+
+  @NoVerticalClassMerging
+  interface I {
+
+    void m();
+  }
+
+  static class A implements I {
+
+    @Override
+    public void m() {
+      System.out.println("A");
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithOverridesOfPackagePrivateMethodsTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithOverridesOfPackagePrivateMethodsTest.java
index 951a2b6..4eff919 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageWithOverridesOfPackagePrivateMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithOverridesOfPackagePrivateMethodsTest.java
@@ -8,6 +8,7 @@
 
 import com.android.tools.r8.NeverClassInline;
 import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoParameterTypeStrengthening;
 import com.android.tools.r8.NoVerticalClassMerging;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -31,6 +32,7 @@
         .apply(this::configureRepackaging)
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
+        .enableNoParameterTypeStrengtheningAnnotations()
         .enableNoVerticalClassMergingAnnotations()
         .setMinApi(parameters.getApiLevel())
         .compile()
@@ -52,11 +54,13 @@
     }
 
     @NeverInline
+    @NoParameterTypeStrengthening
     static void greet(HelloGreeterBase greeter) {
       greeter.greet();
     }
 
     @NeverInline
+    @NoParameterTypeStrengthening
     static void greet(WorldGreeterBase greeter) {
       greeter.greet();
     }
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithOverridesOfProtectedMethodsTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithOverridesOfProtectedMethodsTest.java
index 7747cbd..56f6a41 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageWithOverridesOfProtectedMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithOverridesOfProtectedMethodsTest.java
@@ -8,6 +8,7 @@
 
 import com.android.tools.r8.NeverClassInline;
 import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoParameterTypeStrengthening;
 import com.android.tools.r8.NoVerticalClassMerging;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -31,6 +32,7 @@
         .apply(this::configureRepackaging)
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
+        .enableNoParameterTypeStrengtheningAnnotations()
         .enableNoVerticalClassMergingAnnotations()
         .setMinApi(parameters.getApiLevel())
         .compile()
@@ -52,11 +54,13 @@
     }
 
     @NeverInline
+    @NoParameterTypeStrengthening
     static void greet(HelloGreeterBase greeter) {
       greeter.greet();
     }
 
     @NeverInline
+    @NoParameterTypeStrengthening
     static void greet(WorldGreeterBase greeter) {
       greeter.greet();
     }
diff --git a/src/test/java/com/android/tools/r8/shaking/ParameterTypeTest.java b/src/test/java/com/android/tools/r8/shaking/ParameterTypeTest.java
index 873b1e4..eeb91af 100644
--- a/src/test/java/com/android/tools/r8/shaking/ParameterTypeTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ParameterTypeTest.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
 import static org.hamcrest.CoreMatchers.containsString;
@@ -141,7 +142,11 @@
             .inspector();
 
     ClassSubject superInterface1 = inspector.clazz(B112452064SuperInterface1.class);
-    assertThat(superInterface1, isPresentAndRenamed());
+    if (enableUnusedInterfaceRemoval && enableVerticalClassMerging) {
+      assertThat(superInterface1, isAbsent());
+    } else {
+      assertThat(superInterface1, isPresentAndRenamed());
+    }
     MethodSubject foo = superInterface1.uniqueMethodWithName("foo");
     assertThat(foo, not(isPresent()));
     ClassSubject superInterface2 = inspector.clazz(B112452064SuperInterface2.class);
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/membervaluepropagation/IfWithMethodValuePropagationTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/membervaluepropagation/IfWithMethodValuePropagationTest.java
index e377809..93f8a34 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/membervaluepropagation/IfWithMethodValuePropagationTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/membervaluepropagation/IfWithMethodValuePropagationTest.java
@@ -23,7 +23,7 @@
 
   @Parameterized.Parameters(name = "{0}")
   public static TestParametersCollection data() {
-    return getTestParameters().withAllRuntimes().build();
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
   }
 
   public IfWithMethodValuePropagationTest(TestParameters parameters) {
@@ -41,8 +41,8 @@
             "}",
             "-keep class " + Layout.class.getTypeName())
         .addLibraryClasses(Library.class)
-        .addLibraryFiles(runtimeJar(parameters))
-        .setMinApi(parameters.getRuntime())
+        .addDefaultRuntimeLibrary(parameters)
+        .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(this::verifyOutput)
         .addRunClasspathFiles(
@@ -50,7 +50,7 @@
                 .addProgramClasses(Library.class)
                 .addClasspathClasses(Layout.class)
                 .addKeepAllClassesRule()
-                .setMinApi(parameters.getRuntime())
+                .setMinApi(parameters.getApiLevel())
                 .compile()
                 .writeToZip())
         .run(parameters.getRuntime(), TestClass.class)
@@ -58,7 +58,7 @@
   }
 
   private void verifyOutput(CodeInspector inspector) {
-    // R.ID has been inlined.
+    // R.ID has been removed by dead code removal after member value propagation.
     assertThat(inspector.clazz(R.class), not(isPresent()));
 
     // Layout is kept by the conditional rule.
diff --git a/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByReachableSubclassTest.java b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByReachableSubclassTest.java
index 528a4ac..b848b1c 100644
--- a/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByReachableSubclassTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByReachableSubclassTest.java
@@ -7,6 +7,7 @@
 import static org.junit.Assert.assertEquals;
 
 import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoReturnTypeStrengthening;
 import com.android.tools.r8.NoVerticalClassMerging;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
@@ -44,6 +45,7 @@
     GraphInspector inspector =
         testForR8(parameters.getBackend())
             .enableGraphInspector()
+            .enableNoReturnTypeStrengtheningAnnotations()
             .enableNoVerticalClassMergingAnnotations()
             .enableInliningAnnotations()
             .addProgramClasses(CLASS, A.class, B.class)
@@ -95,6 +97,7 @@
   public static class TestClass {
 
     @NeverInline
+    @NoReturnTypeStrengthening
     static A create() {
       return new B();
     }
diff --git a/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java b/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
index 6264a4b..bb46834 100644
--- a/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
+++ b/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
@@ -109,6 +109,11 @@
     return SyntheticNaming.isSynthetic(reference, Phase.EXTERNAL, SyntheticKind.TWR_CLOSE_RESOURCE);
   }
 
+  public static boolean isMaybeExternalSuppressedExceptionMethod(ClassReference reference) {
+    // The suppressed exception methods are grouped with the backports.
+    return SyntheticNaming.isSynthetic(reference, Phase.EXTERNAL, SyntheticKind.BACKPORT);
+  }
+
   public static boolean isExternalOutlineClass(ClassReference reference) {
     return SyntheticNaming.isSynthetic(reference, Phase.EXTERNAL, SyntheticKind.OUTLINE);
   }
diff --git a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
index 8644e9a..d8557a0 100644
--- a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
+++ b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
@@ -978,6 +978,7 @@
       String constantName,
       Class<?> bootstrapMethodHolder,
       String bootstrapMethodName,
+      boolean isInterfaceInvoke,
       String name,
       Class<?> type) {
     return addMethodTransformer(
@@ -996,7 +997,7 @@
                           DescriptorUtils.getClassBinaryName(bootstrapMethodHolder),
                           bootstrapMethodName,
                           bootstrapMethodSignature,
-                          bootstrapMethodHolder.isInterface()),
+                          isInterfaceInvoke),
                       new Object[] {}));
             } else {
               super.visitLdcInsn(value);
diff --git a/tools/git_sync_cl_chain.py b/tools/git_sync_cl_chain.py
index ea67f63..09da33e 100755
--- a/tools/git_sync_cl_chain.py
+++ b/tools/git_sync_cl_chain.py
@@ -44,6 +44,9 @@
                     help='Delete closed branches',
                     choices=['y', 'n', 'ask'],
                     default='ask')
+  result.add_option('--from_branch', '-f',
+                    help='Uppermost upstream to sync from',
+                    default='main')
   result.add_option('--leave_upstream', '--leave-upstream',
                     help='To not update the upstream of the first open branch',
                     action='store_true')
@@ -81,14 +84,14 @@
         break
     assert current_branch is not None
 
-    if current_branch.upstream == None:
+    if is_root_branch(current_branch, options):
       print('Nothing to sync')
       return
 
     stack = []
     while current_branch:
       stack.append(current_branch)
-      if current_branch.upstream is None:
+      if is_root_branch(current_branch, options):
         break
       current_branch = get_branch_with_name(current_branch.upstream, branches)
 
@@ -169,6 +172,9 @@
 def get_status_for_current_branch():
   return utils.RunCmd(['git', 'cl', 'status', '--field', 'status'], quiet=True)[0].strip()
 
+def is_root_branch(branch, options):
+  return branch == options.from_branch or branch.upstream is None
+
 def pull_for_current_branch(branch, options):
   if branch.name == 'main' and options.skip_main:
     return