Merge commit '68afbc60e854be93182435d71cd56d1710bff0d3' into dev-release
diff --git a/build.gradle b/build.gradle
index eac0f5c..919c260 100644
--- a/build.gradle
+++ b/build.gradle
@@ -333,7 +333,6 @@
                 "android_jar/lib-v30",
                 "android_jar/lib-v31",
                 "android_jar/lib-v32",
-                "android_jar/api-versions",
                 "api-outlining/simple-app-dump",
                 "binary_compatibility_tests/compiler_api_tests",
                 "core-lambda-stubs",
diff --git a/scripts/add-android-jar.sh b/scripts/add-android-jar.sh
index 4a0f57c..c83aa2e 100755
--- a/scripts/add-android-jar.sh
+++ b/scripts/add-android-jar.sh
@@ -23,46 +23,23 @@
 THIRD_PARTY_ANDROID_JAR=third_party/android_jar
 THIRD_PARTY_ANDROID_JAR_LIB=$THIRD_PARTY_ANDROID_JAR/lib-v$SDK_VERSION
 
-UPDATE_ANDROID_JAR="no"
-if [[ "$UPDATE_ANDROID_JAR" == "yes" ]]; then
-  rm -rf $THIRD_PARTY_ANDROID_JAR_LIB
-  rm -f ${THIRD_PARTY_ANDROID_JAR_LIB}.tar.gz
-  rm -f ${THIRD_PARTY_ANDROID_JAR_LIB}.tar.sha1
+rm -rf $THIRD_PARTY_ANDROID_JAR_LIB
+rm -f ${THIRD_PARTY_ANDROID_JAR_LIB}.tar.gz
+rm -f ${THIRD_PARTY_ANDROID_JAR_LIB}.tar.sha1
 
-  mkdir -p $THIRD_PARTY_ANDROID_JAR_LIB/optional
-  cp $SDK_DIR/android.jar $THIRD_PARTY_ANDROID_JAR_LIB/android.jar
-  cp $SDK_DIR/optional/*.jar $THIRD_PARTY_ANDROID_JAR_LIB/optional
-  cp $SDK_DIR/optional/optional.json $THIRD_PARTY_ANDROID_JAR_LIB/optional
-  cp $THIRD_PARTY_ANDROID_JAR/lib-v31/README.google $THIRD_PARTY_ANDROID_JAR_LIB
-  vi $THIRD_PARTY_ANDROID_JAR_LIB/README.google
+mkdir -p $THIRD_PARTY_ANDROID_JAR_LIB/optional
+cp $SDK_DIR/android.jar $THIRD_PARTY_ANDROID_JAR_LIB/android.jar
+cp $SDK_DIR/data/api-versions.xml $THIRD_PARTY_ANDROID_JAR_LIB/api-versions.xml
+cp $SDK_DIR/optional/*.jar $THIRD_PARTY_ANDROID_JAR_LIB/optional
+cp $SDK_DIR/optional/optional.json $THIRD_PARTY_ANDROID_JAR_LIB/optional
+cp $THIRD_PARTY_ANDROID_JAR/lib-v31/README.google $THIRD_PARTY_ANDROID_JAR_LIB
+vi $THIRD_PARTY_ANDROID_JAR_LIB/README.google
 
-  (cd $THIRD_PARTY_ANDROID_JAR \
-      && upload_to_google_storage.py -a --bucket r8-deps lib-v$SDK_VERSION)
-  rm -rf $THIRD_PARTY_ANDROID_JAR_LIB
-  rm ${THIRD_PARTY_ANDROID_JAR_LIB}.tar.gz
-  git add ${THIRD_PARTY_ANDROID_JAR_LIB}.tar.gz.sha1
-fi
-
-UPDATE_API_DATABASE="no"
-if [[ "$UPDATE_API_DATABASE" == "yes" ]]; then
-  rm -rf $THIRD_PARTY_ANDROID_JAR/api-versions
-  rm -f $THIRD_PARTY_ANDROID_JAR/api-versions.tar.gz
-  rm -f $THIRD_PARTY_ANDROID_JAR/api-versions.tar.gz.sha1
-  mkdir -p $THIRD_PARTY_ANDROID_JAR/api-versions
-  cp $SDK_DIR/data/api-versions.xml $THIRD_PARTY_ANDROID_JAR/api-versions
-  (cd $THIRD_PARTY_ANDROID_JAR \
-      && upload_to_google_storage.py -a --bucket r8-deps api-versions)
-  tools/gradle.py r8NoManifestWithoutDeps testJar repackageTestDeps
-  java -cp build/libs/r8_no_manifest_without_deps.jar:build/libs/deps_all.jar:build/libs/r8tests.jar:build/libs/test_deps_all.jar \
-      com.android.tools.r8.apimodel.AndroidApiHashingDatabaseBuilderGeneratorTest
-
-  rm -rf $THIRD_PARTY_ANDROID_JAR/api-versions
-  rm -f $THIRD_PARTY_ANDROID_JAR/api-versions.tar.gz
-  git add $THIRD_PARTY_ANDROID_JAR/api-versions.tar.gz.sha1
-  git add src/main/resources/api_database/api_database_ambiguous.txt
-  git add src/main/resources/api_database/api_database_api_level.ser
-  git add src/main/resources/api_database/api_database_hash_lookup.ser
-fi
+(cd $THIRD_PARTY_ANDROID_JAR \
+    && upload_to_google_storage.py -a --bucket r8-deps lib-v$SDK_VERSION)
+rm -rf $THIRD_PARTY_ANDROID_JAR_LIB
+rm ${THIRD_PARTY_ANDROID_JAR_LIB}.tar.gz
+git add ${THIRD_PARTY_ANDROID_JAR_LIB}.tar.gz.sha1
 
 echo "Update build.gradle with this new cloud dependency, " \
     "and verify with tools/gradle.py downloadDeps"
diff --git a/src/library_desugar/jdk11/chm_only_desugar_jdk_libs.json b/src/library_desugar/jdk11/chm_only_desugar_jdk_libs.json
index 565ffd9..f3a4daa 100644
--- a/src/library_desugar/jdk11/chm_only_desugar_jdk_libs.json
+++ b/src/library_desugar/jdk11/chm_only_desugar_jdk_libs.json
@@ -3,7 +3,7 @@
   "group_id" : "com.tools.android",
   "artifact_id" : "chm_only_desugar_jdk_libs",
   "version": "1.0.12",
-  "required_compilation_api_level": 31,
+  "required_compilation_api_level": 30,
   "synthesized_library_classes_package_prefix": "j$.",
   "support_all_callbacks_from_library": false,
   "common_flags": [
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs.json b/src/library_desugar/jdk11/desugar_jdk_libs.json
index fb03dbb..b075745 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs.json
@@ -3,7 +3,7 @@
   "group_id" : "com.tools.android",
   "artifact_id" : "desugar_jdk_libs",
   "version": "2.0.0",
-  "required_compilation_api_level": 31,
+  "required_compilation_api_level": 30,
   "synthesized_library_classes_package_prefix": "j$.",
   "support_all_callbacks_from_library": true,
   "common_flags": [
@@ -205,7 +205,7 @@
         "java.nio.charset.StandardCharsets": "j$.nio.charset.StandardCharsets"
       },
       "retarget_lib_member": {
-        "java.lang.Character#isBmpCodePoint": "j$.lang.DesugarCharacter"
+        "java.lang.Character#isBmpCodePoint": "java.lang.DesugarCharacter"
       }
     }
   ],
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs_alternative_3.json b/src/library_desugar/jdk11/desugar_jdk_libs_alternative_3.json
index fac2ec0..1482909 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs_alternative_3.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs_alternative_3.json
@@ -3,7 +3,7 @@
   "group_id" : "com.tools.android",
   "artifact_id" : "desugar_jdk_libs_alternative_3",
   "version": "2.0.0",
-  "required_compilation_api_level": 31,
+  "required_compilation_api_level": 30,
   "synthesized_library_classes_package_prefix": "j$.",
   "support_all_callbacks_from_library": false,
   "common_flags": [
@@ -209,7 +209,7 @@
         "java.nio.charset.StandardCharsets": "j$.nio.charset.StandardCharsets"
       },
       "retarget_lib_member": {
-        "java.lang.Character#isBmpCodePoint": "j$.lang.DesugarCharacter"
+        "java.lang.Character#isBmpCodePoint": "java.lang.DesugarCharacter"
       }
     }
   ],
diff --git a/src/main/java/com/android/tools/r8/AssertionsConfiguration.java b/src/main/java/com/android/tools/r8/AssertionsConfiguration.java
index 12e7b48..2f119ca 100644
--- a/src/main/java/com/android/tools/r8/AssertionsConfiguration.java
+++ b/src/main/java/com/android/tools/r8/AssertionsConfiguration.java
@@ -170,8 +170,11 @@
     /**
      * Rewrite the throwing of <code>java.lang.AssertionError</code> to call the supplied method
      * <code>assertionHandler</code>. The method must be a reference to a static method taking one
-     * argument of type <code>java.lang.AssertionError</code>. After the assertion handler as been
-     * called, the code continues as if assertions where disabled.
+     * argument. The type of the argument should be <code>java.lang.Throwable</code> as kotlinc will
+     * generate code where the assertion error is thrown as <code>java.lang.Throwable</code>. If all
+     * code is generated by javac then the type of the argument can be <code>
+     * java.lang.AssertionError</code>. After the assertion handler as been called, the code
+     * continues as if assertions where disabled.
      */
     public AssertionsConfiguration.Builder setAssertionHandler(MethodReference assertionHandler) {
       this.transformation = null;
diff --git a/src/main/java/com/android/tools/r8/BackportedMethodListCommand.java b/src/main/java/com/android/tools/r8/BackportedMethodListCommand.java
index bce73f0..7ef1569 100644
--- a/src/main/java/com/android/tools/r8/BackportedMethodListCommand.java
+++ b/src/main/java/com/android/tools/r8/BackportedMethodListCommand.java
@@ -105,7 +105,7 @@
   InternalOptions getInternalOptions() {
     InternalOptions options = new InternalOptions(factory, getReporter());
     options.setMinApiLevel(AndroidApiLevel.getAndroidApiLevel(minApiLevel));
-    options.desugaredLibrarySpecification = desugaredLibrarySpecification;
+    options.setDesugaredLibrarySpecification(desugaredLibrarySpecification, getInputApp());
     return options;
   }
 
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index cd0bc2f..9232563 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -204,7 +204,7 @@
         // enabling code.
         ClassInitializerAssertionEnablingAnalysis analysis =
             new ClassInitializerAssertionEnablingAnalysis(
-                appView.dexItemFactory(), OptimizationFeedbackSimple.getInstance());
+                appView, OptimizationFeedbackSimple.getInstance());
         ThreadUtils.processItems(
             appView.appInfo().classes(),
             clazz -> {
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index 8dd34a8..0967f55 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -498,8 +498,7 @@
     internal.encodeChecksums = getIncludeClassesChecksum();
     internal.dexClassChecksumFilter = getDexClassChecksumFilter();
     internal.enableInheritanceClassInDexDistributor = isOptimizeMultidexForLinearAlloc();
-
-    internal.desugaredLibrarySpecification = desugaredLibrarySpecification;
+    internal.setDesugaredLibrarySpecification(desugaredLibrarySpecification, getInputApp());
     internal.synthesizedClassPrefix = synthesizedClassPrefix;
     internal.desugaredLibraryKeepRuleConsumer = desugaredLibraryKeepRuleConsumer;
 
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java
index 26c99ce..6da92e2 100644
--- a/src/main/java/com/android/tools/r8/L8Command.java
+++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -194,7 +194,7 @@
     internal.enableInheritanceClassInDexDistributor = false;
 
     assert desugaredLibrarySpecification != null;
-    internal.desugaredLibrarySpecification = desugaredLibrarySpecification;
+    internal.setDesugaredLibrarySpecification(desugaredLibrarySpecification, getInputApp());
     internal.synthesizedClassPrefix =
         desugaredLibrarySpecification.getSynthesizedLibraryClassesPackagePrefix();
 
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index b96eaef..85c05f6 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -75,6 +75,7 @@
 import com.android.tools.r8.optimize.MemberRebindingIdentityLensFactory;
 import com.android.tools.r8.optimize.VisibilityBridgeRemover;
 import com.android.tools.r8.optimize.bridgehoisting.BridgeHoisting;
+import com.android.tools.r8.optimize.proto.ProtoNormalizer;
 import com.android.tools.r8.origin.CommandLineOrigin;
 import com.android.tools.r8.repackaging.Repackaging;
 import com.android.tools.r8.repackaging.RepackagingLens;
@@ -310,7 +311,7 @@
       if (!options.mainDexKeepRules.isEmpty()) {
         MainDexListBuilder.checkForAssumedLibraryTypes(appView.appInfo());
       }
-      if (!options.desugaredLibrarySpecification.getRetargetCoreLibMember().isEmpty()) {
+      if (options.machineDesugaredLibrarySpecification.hasRetargeting()) {
         DesugaredLibraryRetargeterLibraryTypeSynthesizer.checkForAssumedLibraryTypes(appView);
         DesugaredLibraryRetargeterLibraryTypeSynthesizer.amendLibraryWithRetargetedMembers(appView);
       }
@@ -517,6 +518,8 @@
             .runIfNecessary(runtimeTypeCheckInfo, executorService, timing);
       }
 
+      new ProtoNormalizer(appViewWithLiveness).run(executorService, timing);
+
       // Clear traced methods roots to not hold on to the main dex live method set.
       appView.appInfo().getMainDexInfo().clearTracedMethodRoots();
 
@@ -1005,9 +1008,11 @@
       enqueuer.registerAnalysis(new InitializedClassesInInstanceMethodsAnalysis(appView));
     }
     if (AssertionsRewriter.isEnabled(appView.options())) {
-      enqueuer.registerAnalysis(
+      ClassInitializerAssertionEnablingAnalysis analysis =
           new ClassInitializerAssertionEnablingAnalysis(
-              appView.dexItemFactory(), OptimizationFeedbackSimple.getInstance()));
+              appView, OptimizationFeedbackSimple.getInstance());
+      enqueuer.registerAnalysis(analysis);
+      enqueuer.registerFieldAccessAnalysis(analysis);
     }
 
     if (options.isClassMergingExtensionRequired(enqueuer.getMode())) {
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index bf04680..5bc9ddd 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -943,7 +943,7 @@
 
     internal.enableInheritanceClassInDexDistributor = isOptimizeMultidexForLinearAlloc();
 
-    internal.desugaredLibrarySpecification = desugaredLibrarySpecification;
+    internal.setDesugaredLibrarySpecification(desugaredLibrarySpecification, getInputApp());
     internal.synthesizedClassPrefix = synthesizedClassPrefix;
     internal.desugaredLibraryKeepRuleConsumer = desugaredLibraryKeepRuleConsumer;
 
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
index 2e2aa7a..cce0852 100644
--- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
@@ -9,7 +9,6 @@
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.ConsumerUtils;
 import com.google.common.collect.ImmutableList;
@@ -18,7 +17,6 @@
 
 public class AndroidApiReferenceLevelCache {
 
-  private final LegacyDesugaredLibrarySpecification desugaredLibrarySpecification;
   private final AndroidApiLevelCompute apiLevelCompute;
   private final AndroidApiLevelDatabase androidApiLevelDatabase;
   private final AppView<?> appView;
@@ -33,7 +31,6 @@
     factory = appView.dexItemFactory();
     androidApiLevelDatabase =
         new AndroidApiLevelHashingDatabaseImpl(predefinedApiTypeLookupForHashing);
-    desugaredLibrarySpecification = appView.options().desugaredLibrarySpecification;
   }
 
   public static AndroidApiReferenceLevelCache create(
@@ -75,7 +72,7 @@
     if (reference.getContextType() == factory.objectType) {
       return appView.computedMinApiLevel();
     }
-    if (desugaredLibrarySpecification.isSupported(reference, appView)) {
+    if (appView.options().machineDesugaredLibrarySpecification.isSupported(reference)) {
       // If we end up desugaring the reference, the library classes is bridged by j$ which is part
       // of the program.
       return appView.computedMinApiLevel();
diff --git a/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java b/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
index f9b13a4..ef86f73 100644
--- a/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
+++ b/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
@@ -19,7 +19,6 @@
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.ThrowExceptionCode;
 import com.android.tools.r8.graph.UseRegistry;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.synthesis.CommittedItems;
 import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
@@ -112,12 +111,10 @@
       new ConcurrentHashMap<>();
   private final Set<DexType> seenTypes = Sets.newConcurrentHashSet();
   private final AndroidApiLevelCompute apiLevelCompute;
-  private final LegacyDesugaredLibrarySpecification desugaredLibraryConfiguration;
 
   public ApiReferenceStubber(AppView<? extends AppInfoWithClassHierarchy> appView) {
     this.appView = appView;
     apiLevelCompute = appView.apiLevelCompute();
-    desugaredLibraryConfiguration = appView.options().desugaredLibrarySpecification;
   }
 
   public void run(ExecutorService executorService) throws ExecutionException {
@@ -220,7 +217,10 @@
         || libraryClass.getType().toDescriptorString().startsWith("Ljava/")) {
       return;
     }
-    if (desugaredLibraryConfiguration.isSupported(libraryClass.getType(), appView)) {
+    if (appView
+        .options()
+        .machineDesugaredLibrarySpecification
+        .isSupported(libraryClass.getType())) {
       return;
     }
     appView
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java b/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
index 2585329..783a837 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
@@ -88,6 +88,10 @@
     return opcode == Opcodes.GETFIELD || opcode == Opcodes.GETSTATIC;
   }
 
+  public boolean isStaticFieldGet() {
+    return opcode == Opcodes.GETSTATIC;
+  }
+
   @Override
   public CfFieldInstruction asFieldInstruction() {
     return this;
diff --git a/src/main/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryKeepRuleGenerator.java b/src/main/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryKeepRuleGenerator.java
index a720021..eaec5a0 100644
--- a/src/main/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryKeepRuleGenerator.java
+++ b/src/main/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryKeepRuleGenerator.java
@@ -13,7 +13,7 @@
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.references.ArrayReference;
 import com.android.tools.r8.references.ClassReference;
@@ -32,13 +32,11 @@
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.NopDiagnosticsHandler;
-import com.android.tools.r8.utils.SetUtils;
 import com.android.tools.r8.utils.Timing;
 import com.android.tools.r8.utils.TypeReferenceUtils;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Predicate;
 
@@ -71,7 +69,7 @@
       return false;
     }
     return namingLens.hasPrefixRewritingLogic()
-        || options.desugaredLibrarySpecification.hasEmulatedLibraryInterfaces();
+        || options.machineDesugaredLibrarySpecification.hasEmulatedInterfaces();
   }
 
   private void run() {
@@ -80,18 +78,15 @@
   }
 
   private Predicate<DexType> createTargetPredicate() {
-    LegacyDesugaredLibrarySpecification desugaredLibrarySpecification =
-        options.desugaredLibrarySpecification;
-    Set<DexType> potentialTypesToKeep =
-        SetUtils.newIdentityHashSet(
-            desugaredLibrarySpecification.getCustomConversions().values(),
-            desugaredLibrarySpecification.getEmulateLibraryInterface().values());
+    MachineDesugaredLibrarySpecification desugaredLibrarySpecification =
+        options.machineDesugaredLibrarySpecification;
     byte[] synthesizedLibraryClassesPackageDescriptorPrefix =
         DexString.encodeToMutf8(
             "L" + desugaredLibrarySpecification.getSynthesizedLibraryClassesPackagePrefix());
     return type ->
         namingLens.prefixRewrittenType(type) != null
-            || potentialTypesToKeep.contains(type)
+            || desugaredLibrarySpecification.isEmulatedInterfaceRewrittenType(type)
+            || desugaredLibrarySpecification.isCustomConversionRewrittenType(type)
             || type.getDescriptor().startsWith(synthesizedLibraryClassesPackageDescriptorPrefix);
   }
 
diff --git a/src/main/java/com/android/tools/r8/dex/CodeToKeep.java b/src/main/java/com/android/tools/r8/dex/CodeToKeep.java
index cc05c31..3e33ca8 100644
--- a/src/main/java/com/android/tools/r8/dex/CodeToKeep.java
+++ b/src/main/java/com/android/tools/r8/dex/CodeToKeep.java
@@ -25,7 +25,7 @@
 
   static CodeToKeep createCodeToKeep(InternalOptions options, NamingLens namingLens) {
     if ((!namingLens.hasPrefixRewritingLogic()
-            && !options.desugaredLibrarySpecification.hasEmulatedLibraryInterfaces())
+            && !options.machineDesugaredLibrarySpecification.hasEmulatedInterfaces())
         || options.isDesugaredLibraryCompilation()
         || options.testing.enableExperimentalDesugaredLibraryKeepRuleGenerator) {
       return new NopCodeToKeep();
@@ -57,27 +57,23 @@
     }
 
     private final NamingLens namingLens;
-    private final Set<DexType> potentialTypesToKeep = Sets.newIdentityHashSet();
     private final Map<DexType, KeepStruct> toKeep = new ConcurrentHashMap<>();
     private final InternalOptions options;
 
     public DesugaredLibraryCodeToKeep(NamingLens namingLens, InternalOptions options) {
       this.namingLens = namingLens;
       this.options = options;
-      potentialTypesToKeep.addAll(
-          options.desugaredLibrarySpecification.getEmulateLibraryInterface().values());
-      potentialTypesToKeep.addAll(
-          options.desugaredLibrarySpecification.getCustomConversions().values());
     }
 
     private boolean shouldKeep(DexType type) {
       return namingLens.prefixRewrittenType(type) != null
-          || potentialTypesToKeep.contains(type)
+          || options.machineDesugaredLibrarySpecification.isCustomConversionRewrittenType(type)
+          || options.machineDesugaredLibrarySpecification.isEmulatedInterfaceRewrittenType(type)
           // TODO(b/158632510): This should prefix match on DexString.
           || type.toDescriptorString()
               .startsWith(
                   "L"
-                      + options.desugaredLibrarySpecification
+                      + options.machineDesugaredLibrarySpecification
                           .getSynthesizedLibraryClassesPackagePrefix());
     }
 
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
index c928847..f32be7e 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
@@ -8,7 +8,6 @@
 import static com.android.tools.r8.utils.TraversalContinuation.CONTINUE;
 
 import com.android.tools.r8.features.ClassToFeatureSplitMap;
-import com.android.tools.r8.graph.FieldResolutionResult.SuccessfulFieldResolutionResult;
 import com.android.tools.r8.graph.MethodResolutionResult.ArrayCloneMethodResult;
 import com.android.tools.r8.graph.MethodResolutionResult.ClassNotFoundResult;
 import com.android.tools.r8.graph.MethodResolutionResult.IllegalAccessOrNoSuchMethodResult;
@@ -25,7 +24,6 @@
 import com.android.tools.r8.utils.BooleanBox;
 import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.Pair;
-import com.android.tools.r8.utils.SetUtils;
 import com.android.tools.r8.utils.TraversalContinuation;
 import com.android.tools.r8.utils.TriConsumer;
 import com.android.tools.r8.utils.TriFunction;
@@ -1013,81 +1011,20 @@
   /** Intentionally drops {@param context} since this is only needed in D8. */
   @Override
   public FieldResolutionResult resolveFieldOn(DexType type, DexField field, ProgramMethod context) {
+    assert checkIfObsolete();
     return resolveFieldOn(type, field);
   }
 
-  /**
-   * Implements resolution of a field descriptor against a type.
-   *
-   * <p>See <a href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html#jvms-5.4.3.2">
-   * Section 5.4.3.2 of the JVM Spec</a>.
-   */
+  // Keep as instance methods to ensure that one needs AppInfoWithClassHierarchy to do resolution.
   public FieldResolutionResult resolveFieldOn(DexType type, DexField field) {
     assert checkIfObsolete();
-    DexClass holder = definitionFor(type);
-    return holder != null ? resolveFieldOn(holder, field) : FieldResolutionResult.failure();
+    return new FieldResolution(this).resolveFieldOn(type, field);
   }
 
-  public FieldResolutionResult resolveFieldOn(DexClass holder, DexField field) {
+  // Keep as instance methods to ensure that one needs AppInfoWithClassHierarchy to do resolution.
+  public FieldResolutionResult resolveFieldOn(DexClass clazz, DexField field) {
     assert checkIfObsolete();
-    assert holder != null;
-    return resolveFieldOn(holder, field, holder, SetUtils.newIdentityHashSet(8));
-  }
-
-  private FieldResolutionResult resolveFieldOn(
-      DexClass holder,
-      DexField field,
-      DexClass initialResolutionHolder,
-      Set<DexType> visitedInterfaces) {
-    assert checkIfObsolete();
-    assert holder != null;
-    // Step 1: Class declares the field.
-    DexEncodedField definition = holder.lookupField(field);
-    if (definition != null) {
-      return new SuccessfulFieldResolutionResult(initialResolutionHolder, holder, definition);
-    }
-    // Step 2: Apply recursively to direct superinterfaces. First match succeeds.
-    DexClassAndField result = resolveFieldOnDirectInterfaces(holder, field, visitedInterfaces);
-    if (result != null) {
-      return new SuccessfulFieldResolutionResult(
-          initialResolutionHolder, result.getHolder(), result.getDefinition());
-    }
-    // Step 3: Apply recursively to superclass.
-    if (holder.superType != null) {
-      DexClass superClass = definitionFor(holder.superType);
-      if (superClass != null) {
-        return resolveFieldOn(superClass, field, initialResolutionHolder, visitedInterfaces);
-      }
-    }
-    return FieldResolutionResult.failure();
-  }
-
-  private DexClassAndField resolveFieldOnDirectInterfaces(
-      DexClass clazz, DexField field, Set<DexType> visitedInterfaces) {
-    for (DexType interfaceType : clazz.interfaces.values) {
-      if (visitedInterfaces.add(interfaceType)) {
-        DexClass interfaceClass = definitionFor(interfaceType);
-        if (interfaceClass != null) {
-          DexClassAndField result =
-              resolveFieldOnInterface(interfaceClass, field, visitedInterfaces);
-          if (result != null) {
-            return result;
-          }
-        }
-      }
-    }
-    return null;
-  }
-
-  private DexClassAndField resolveFieldOnInterface(
-      DexClass interfaceClass, DexField field, Set<DexType> visitedInterfaces) {
-    // Step 1: Class declares the field.
-    DexEncodedField definition = interfaceClass.lookupField(field);
-    if (definition != null) {
-      return DexClassAndField.create(interfaceClass, definition);
-    }
-    // Step 2: Apply recursively to direct superinterfaces. First match succeeds.
-    return resolveFieldOnDirectInterfaces(interfaceClass, field, visitedInterfaces);
+    return new FieldResolution(this).resolveFieldOn(clazz, field);
   }
 
   private static class MaximallySpecificMethodsBuilder {
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index 14b0ded..24c47b4 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -515,7 +515,6 @@
   }
 
   public void setRootSet(RootSet rootSet) {
-    assert this.rootSet == null : "Root set should never be recomputed";
     this.rootSet = rootSet;
   }
 
@@ -806,9 +805,12 @@
             appView.setProguardCompatibilityActions(
                 appView.getProguardCompatibilityActions().rewrittenWithLens(lens));
           }
-          if (appView.getMainDexRootSet() != null) {
+          if (appView.hasMainDexRootSet()) {
             appView.setMainDexRootSet(appView.getMainDexRootSet().rewrittenWithLens(lens));
           }
+          if (appView.hasRootSet()) {
+            appView.setRootSet(appView.rootSet().rewrittenWithLens(lens));
+          }
         });
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java b/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java
index d0324ee..fd28cde 100644
--- a/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.graph;
 
 import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
+import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
 import com.android.tools.r8.utils.MapUtils;
 import com.android.tools.r8.utils.collections.BidirectionalManyToOneRepresentativeHashMap;
 import com.android.tools.r8.utils.collections.MutableBidirectionalManyToOneRepresentativeMap;
diff --git a/src/main/java/com/android/tools/r8/graph/CfCode.java b/src/main/java/com/android/tools/r8/graph/CfCode.java
index f35751b..9ab5faa 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -23,10 +23,11 @@
 import com.android.tools.r8.errors.InvalidDebugInfoException;
 import com.android.tools.r8.errors.Unimplemented;
 import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfo;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
 import com.android.tools.r8.graph.bytecodemetadata.BytecodeInstructionMetadata;
 import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadata;
+import com.android.tools.r8.graph.proto.ArgumentInfo;
+import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
+import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.MemberType;
 import com.android.tools.r8.ir.code.NumberGenerator;
diff --git a/src/main/java/com/android/tools/r8/graph/Code.java b/src/main/java/com/android/tools/r8/graph/Code.java
index f8d5917..7a06b3d 100644
--- a/src/main/java/com/android/tools/r8/graph/Code.java
+++ b/src/main/java/com/android/tools/r8/graph/Code.java
@@ -8,6 +8,7 @@
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.bytecodemetadata.BytecodeInstructionMetadata;
 import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadata;
+import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.NumberGenerator;
 import com.android.tools.r8.ir.code.Position;
diff --git a/src/main/java/com/android/tools/r8/graph/DefaultInstanceInitializerCode.java b/src/main/java/com/android/tools/r8/graph/DefaultInstanceInitializerCode.java
index 8803346..a544337 100644
--- a/src/main/java/com/android/tools/r8/graph/DefaultInstanceInitializerCode.java
+++ b/src/main/java/com/android/tools/r8/graph/DefaultInstanceInitializerCode.java
@@ -16,6 +16,7 @@
 import com.android.tools.r8.dex.MixedSectionCollection;
 import com.android.tools.r8.graph.DexCode.Try;
 import com.android.tools.r8.graph.DexCode.TryHandler;
+import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.NumberGenerator;
 import com.android.tools.r8.ir.code.Position;
diff --git a/src/main/java/com/android/tools/r8/graph/DexCode.java b/src/main/java/com/android/tools/r8/graph/DexCode.java
index a44fc1d..3c3ce77 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCode.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCode.java
@@ -18,6 +18,7 @@
 import com.android.tools.r8.graph.DexDebugInfo.EventBasedDebugInfo;
 import com.android.tools.r8.graph.bytecodemetadata.BytecodeInstructionMetadata;
 import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadata;
+import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.NumberGenerator;
 import com.android.tools.r8.ir.code.Position;
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index 1f58402..c326dc0 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -43,8 +43,6 @@
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DexAnnotation.AnnotatedKind;
 import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.RemovedArgumentInfo;
 import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadataProvider;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.NumericType;
@@ -411,6 +409,11 @@
   }
 
   public static ProgramMethod asProgramMethodOrNull(
+      DexEncodedMethod method, DexProgramClass holder) {
+    return method != null ? method.asProgramMethod(holder) : null;
+  }
+
+  public static ProgramMethod asProgramMethodOrNull(
       DexEncodedMethod method, DexDefinitionSupplier definitions) {
     return method != null ? method.asProgramMethod(definitions) : null;
   }
@@ -1208,26 +1211,6 @@
         .build();
   }
 
-  public DexEncodedMethod toStaticMethodWithoutThis(AppView<AppInfoWithLiveness> appView) {
-    checkIfObsolete();
-    assert !accessFlags.isStatic();
-
-    ArgumentInfoCollection prototypeChanges =
-        ArgumentInfoCollection.builder()
-            .addArgumentInfo(0, RemovedArgumentInfo.builder().setType(getHolderType()).build())
-            .build();
-    Builder builder =
-        builder(this)
-            .promoteToStatic()
-            .withoutThisParameter(appView.dexItemFactory())
-            .fixupOptimizationInfo(appView, prototypeChanges.createMethodOptimizationInfoFixer())
-            .setGenericSignature(MethodTypeSignature.noSignature());
-    DexEncodedMethod method = builder.build();
-    method.copyMetadata(appView, this);
-    setObsolete();
-    return method;
-  }
-
   public String codeToString() {
     checkIfObsolete();
     return code == null ? "<no code>" : code.toString(this, null);
@@ -1516,21 +1499,6 @@
               newNumberOfMissingParameterAnnotations));
     }
 
-    public Builder promoteToStatic() {
-      this.accessFlags.promoteToStatic();
-      return this;
-    }
-
-    public Builder withoutThisParameter(DexItemFactory factory) {
-      assert code != null;
-      if (code.isDexCode()) {
-        code = code.asDexCode().withoutThisParameter(factory);
-      } else {
-        throw new Unreachable("Code " + code.getClass().getSimpleName() + " is not supported.");
-      }
-      return this;
-    }
-
     public Builder setOptimizationInfo(MethodOptimizationInfo optimizationInfo) {
       this.optimizationInfo = optimizationInfo;
       return this;
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 5421ba6..686f124 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -32,6 +32,7 @@
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.ir.desugar.LambdaClass;
 import com.android.tools.r8.kotlin.Kotlin;
+import com.android.tools.r8.references.ClassReference;
 import com.android.tools.r8.references.FieldReference;
 import com.android.tools.r8.references.MethodReference;
 import com.android.tools.r8.utils.ArrayUtils;
@@ -469,6 +470,7 @@
   public final DexType retentionType =
       createStaticallyKnownType("Ljava/lang/annotation/Retention;");
   public final DexType runtimeExceptionType = createStaticallyKnownType(runtimeExceptionDescriptor);
+  public final DexType assertionErrorType = createStaticallyKnownType(assertionErrorDescriptor);
   public final DexType throwableType = createStaticallyKnownType(throwableDescriptor);
   public final DexType illegalAccessErrorType =
       createStaticallyKnownType(illegalAccessErrorDescriptor);
@@ -2530,6 +2532,10 @@
     return createType(createString(descriptor));
   }
 
+  public DexType createType(ClassReference clazz) {
+    return createType(clazz.getDescriptor());
+  }
+
   public DexType lookupType(DexString descriptor) {
     return types.get(descriptor);
   }
@@ -2556,6 +2562,10 @@
         fieldReference.getFieldName());
   }
 
+  public DexProto createProto(DexType returnType, DexTypeList parameters) {
+    return createProto(returnType, parameters, createShorty(returnType, parameters.getBacking()));
+  }
+
   public DexProto createProto(DexType returnType, DexTypeList parameters, DexString shorty) {
     assert !sorted;
     DexProto proto = new DexProto(shorty, returnType, parameters);
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethodSignature.java b/src/main/java/com/android/tools/r8/graph/DexMethodSignature.java
index 8cc4fa7..e756d78 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethodSignature.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethodSignature.java
@@ -33,6 +33,10 @@
     return getProto().getParameter(index);
   }
 
+  public DexTypeList getParameters() {
+    return getProto().getParameters();
+  }
+
   public DexType getReturnType() {
     return getProto().getReturnType();
   }
diff --git a/src/main/java/com/android/tools/r8/graph/FieldResolution.java b/src/main/java/com/android/tools/r8/graph/FieldResolution.java
new file mode 100644
index 0000000..120bee0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/FieldResolution.java
@@ -0,0 +1,89 @@
+// 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.graph;
+
+import com.android.tools.r8.graph.FieldResolutionResult.SuccessfulFieldResolutionResult;
+import com.android.tools.r8.utils.SetUtils;
+import java.util.Set;
+
+/**
+ * Implements resolution of a field descriptor against a type.
+ *
+ * <p>See <a href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html#jvms-5.4.3.2">
+ * Section 5.4.3.2 of the JVM Spec</a>.
+ */
+public class FieldResolution {
+
+  private final DexDefinitionSupplier definitionFor;
+
+  public FieldResolution(DexDefinitionSupplier definitionFor) {
+    this.definitionFor = definitionFor;
+  }
+
+  public FieldResolutionResult resolveFieldOn(DexType type, DexField field) {
+    DexClass holder = definitionFor.contextIndependentDefinitionFor(type);
+    return holder != null ? resolveFieldOn(holder, field) : FieldResolutionResult.failure();
+  }
+
+  public FieldResolutionResult resolveFieldOn(DexClass holder, DexField field) {
+    assert holder != null;
+    return resolveFieldOn(holder, field, holder, SetUtils.newIdentityHashSet(8));
+  }
+
+  private FieldResolutionResult resolveFieldOn(
+      DexClass holder,
+      DexField field,
+      DexClass initialResolutionHolder,
+      Set<DexType> visitedInterfaces) {
+    assert holder != null;
+    // Step 1: Class declares the field.
+    DexEncodedField definition = holder.lookupField(field);
+    if (definition != null) {
+      return new SuccessfulFieldResolutionResult(initialResolutionHolder, holder, definition);
+    }
+    // Step 2: Apply recursively to direct superinterfaces. First match succeeds.
+    DexClassAndField result = resolveFieldOnDirectInterfaces(holder, field, visitedInterfaces);
+    if (result != null) {
+      return new SuccessfulFieldResolutionResult(
+          initialResolutionHolder, result.getHolder(), result.getDefinition());
+    }
+    // Step 3: Apply recursively to superclass.
+    if (holder.superType != null) {
+      DexClass superClass = definitionFor.contextIndependentDefinitionFor(holder.superType);
+      if (superClass != null) {
+        return resolveFieldOn(superClass, field, initialResolutionHolder, visitedInterfaces);
+      }
+    }
+    return FieldResolutionResult.failure();
+  }
+
+  private DexClassAndField resolveFieldOnDirectInterfaces(
+      DexClass clazz, DexField field, Set<DexType> visitedInterfaces) {
+    for (DexType interfaceType : clazz.interfaces.values) {
+      if (visitedInterfaces.add(interfaceType)) {
+        DexClass interfaceClass = definitionFor.contextIndependentDefinitionFor(interfaceType);
+        if (interfaceClass != null) {
+          DexClassAndField result =
+              resolveFieldOnInterface(interfaceClass, field, visitedInterfaces);
+          if (result != null) {
+            return result;
+          }
+        }
+      }
+    }
+    return null;
+  }
+
+  private DexClassAndField resolveFieldOnInterface(
+      DexClass interfaceClass, DexField field, Set<DexType> visitedInterfaces) {
+    // Step 1: Class declares the field.
+    DexEncodedField definition = interfaceClass.lookupField(field);
+    if (definition != null) {
+      return DexClassAndField.create(interfaceClass, definition);
+    }
+    // Step 2: Apply recursively to direct superinterfaces. First match succeeds.
+    return resolveFieldOnDirectInterfaces(interfaceClass, field, visitedInterfaces);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLens.java b/src/main/java/com/android/tools/r8/graph/GraphLens.java
index 3e235b4..3041ccf 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLens.java
@@ -6,6 +6,7 @@
 import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
 
 import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
 import com.android.tools.r8.ir.code.Invoke.Type;
 import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
 import com.android.tools.r8.optimize.MemberRebindingIdentityLens;
diff --git a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
index 457a6f7..b2efb1b 100644
--- a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
@@ -51,6 +51,7 @@
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.CfCode.LocalVariableInfo;
 import com.android.tools.r8.graph.JarClassFileReader.ReparseContext;
+import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.If;
 import com.android.tools.r8.ir.code.MemberType;
diff --git a/src/main/java/com/android/tools/r8/graph/MethodCollection.java b/src/main/java/com/android/tools/r8/graph/MethodCollection.java
index 3dc2b58..4a403c4 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodCollection.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodCollection.java
@@ -167,6 +167,10 @@
     return backing.getMethod(methodProto, methodName);
   }
 
+  public final DexEncodedMethod getMethod(DexMethodSignature method) {
+    return getMethod(method.getProto(), method.getName());
+  }
+
   public DexEncodedMethod getMethod(Predicate<DexEncodedMethod> predicate) {
     DexEncodedMethod result = backing.getDirectMethod(predicate);
     return result != null ? result : backing.getVirtualMethod(predicate);
diff --git a/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java b/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java
index 1f845ed..113c31c 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java
@@ -361,6 +361,10 @@
         return null;
       }
 
+      if (getResolvedHolder().isInterface() && getResolvedMethod().isPrivate()) {
+        return getResolutionPair();
+      }
+
       // The symbolic reference is the holder type that resolution was initiated at.
       DexClass symbolicReference = initialResolutionHolder;
 
diff --git a/src/main/java/com/android/tools/r8/graph/NestedGraphLens.java b/src/main/java/com/android/tools/r8/graph/NestedGraphLens.java
index 6ab93ed..87e55e9 100644
--- a/src/main/java/com/android/tools/r8/graph/NestedGraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/NestedGraphLens.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.graph;
 
 import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
+import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
 import com.android.tools.r8.ir.code.Invoke.Type;
 import com.android.tools.r8.utils.IterableUtils;
 import com.android.tools.r8.utils.collections.BidirectionalManyToManyRepresentativeMap;
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramMethod.java b/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
index 9bd0c4a..37acbad 100644
--- a/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
@@ -6,6 +6,7 @@
 import static com.android.tools.r8.ir.optimize.info.OptimizationFeedback.getSimpleFeedback;
 
 import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.NumberGenerator;
 import com.android.tools.r8.ir.code.Position;
diff --git a/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java b/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java
deleted file mode 100644
index e6a6cba..0000000
--- a/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java
+++ /dev/null
@@ -1,882 +0,0 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.graph;
-
-import com.android.tools.r8.ir.analysis.value.SingleValue;
-import com.android.tools.r8.ir.code.ConstInstruction;
-import com.android.tools.r8.ir.code.IRCode;
-import com.android.tools.r8.ir.code.Instruction;
-import com.android.tools.r8.ir.code.Position;
-import com.android.tools.r8.ir.code.TypeAndLocalInfoSupplier;
-import com.android.tools.r8.ir.conversion.ExtraParameter;
-import com.android.tools.r8.ir.conversion.ExtraUnusedNullParameter;
-import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfoFixer;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.utils.BooleanUtils;
-import com.android.tools.r8.utils.ConsumerUtils;
-import com.android.tools.r8.utils.IntObjConsumer;
-import com.android.tools.r8.utils.IteratorUtils;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Ordering;
-import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
-import it.unimi.dsi.fastutil.ints.Int2ObjectMap.Entry;
-import it.unimi.dsi.fastutil.ints.Int2ObjectRBTreeMap;
-import it.unimi.dsi.fastutil.ints.Int2ObjectSortedMap;
-import it.unimi.dsi.fastutil.ints.IntArrayList;
-import it.unimi.dsi.fastutil.ints.IntBidirectionalIterator;
-import it.unimi.dsi.fastutil.ints.IntCollection;
-import it.unimi.dsi.fastutil.ints.IntList;
-import it.unimi.dsi.fastutil.ints.IntLists;
-import it.unimi.dsi.fastutil.ints.IntSortedSet;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Objects;
-import java.util.function.Consumer;
-
-public class RewrittenPrototypeDescription {
-
-  public abstract static class ArgumentInfo {
-
-    static final ArgumentInfo NO_INFO =
-        new ArgumentInfo() {
-
-          @Override
-          public ArgumentInfo combine(ArgumentInfo info) {
-            assert false : "ArgumentInfo NO_INFO should not be combined";
-            return info;
-          }
-
-          @Override
-          public boolean isNone() {
-            return true;
-          }
-
-          @Override
-          public ArgumentInfo rewrittenWithLens(
-              AppView<AppInfoWithLiveness> appView, GraphLens graphLens, GraphLens codeLens) {
-            return this;
-          }
-
-          @Override
-          public boolean equals(Object obj) {
-            return obj == this;
-          }
-
-          @Override
-          public int hashCode() {
-            return System.identityHashCode(this);
-          }
-        };
-
-    @SuppressWarnings("ConstantConditions")
-    public static ArgumentInfo combine(ArgumentInfo arg1, ArgumentInfo arg2) {
-      if (arg1 == null) {
-        assert arg2 != null;
-        return arg2;
-      }
-      if (arg2 == null) {
-        assert arg1 != null;
-        return arg1;
-      }
-      return arg1.combine(arg2);
-    }
-
-    public boolean isNone() {
-      return false;
-    }
-
-    public boolean isRemovedArgumentInfo() {
-      return false;
-    }
-
-    public RemovedArgumentInfo asRemovedArgumentInfo() {
-      return null;
-    }
-
-    public boolean isRewrittenTypeInfo() {
-      return false;
-    }
-
-    public RewrittenTypeInfo asRewrittenTypeInfo() {
-      return null;
-    }
-
-    // ArgumentInfo are combined with `this` first, and the `info` argument second.
-    public abstract ArgumentInfo combine(ArgumentInfo info);
-
-    public abstract ArgumentInfo rewrittenWithLens(
-        AppView<AppInfoWithLiveness> appView, GraphLens graphLens, GraphLens codeLens);
-
-    @Override
-    public abstract boolean equals(Object obj);
-
-    @Override
-    public abstract int hashCode();
-  }
-
-  public static class RemovedArgumentInfo extends ArgumentInfo {
-
-    public static class Builder {
-
-      private boolean checkNullOrZero;
-      private SingleValue singleValue;
-      private DexType type;
-
-      public Builder setCheckNullOrZero(boolean checkNullOrZero) {
-        this.checkNullOrZero = checkNullOrZero;
-        return this;
-      }
-
-      public Builder setSingleValue(SingleValue singleValue) {
-        this.singleValue = singleValue;
-        return this;
-      }
-
-      public Builder setType(DexType type) {
-        this.type = type;
-        return this;
-      }
-
-      public RemovedArgumentInfo build() {
-        assert type != null;
-        return new RemovedArgumentInfo(checkNullOrZero, singleValue, type);
-      }
-    }
-
-    private final boolean checkNullOrZero;
-    private final SingleValue singleValue;
-    private final DexType type;
-
-    private RemovedArgumentInfo(boolean checkNullOrZero, SingleValue singleValue, DexType type) {
-      this.checkNullOrZero = checkNullOrZero;
-      this.singleValue = singleValue;
-      this.type = type;
-    }
-
-    public static Builder builder() {
-      return new Builder();
-    }
-
-    public boolean hasSingleValue() {
-      return singleValue != null;
-    }
-
-    public SingleValue getSingleValue() {
-      return singleValue;
-    }
-
-    public DexType getType() {
-      return type;
-    }
-
-    public boolean isCheckNullOrZeroSet() {
-      return checkNullOrZero;
-    }
-
-    @Override
-    public boolean isRemovedArgumentInfo() {
-      return true;
-    }
-
-    @Override
-    public RemovedArgumentInfo asRemovedArgumentInfo() {
-      return this;
-    }
-
-    @Override
-    public ArgumentInfo combine(ArgumentInfo info) {
-      assert false : "Once the argument is removed one cannot modify it any further.";
-      return this;
-    }
-
-    @Override
-    public RemovedArgumentInfo rewrittenWithLens(
-        AppView<AppInfoWithLiveness> appView, GraphLens graphLens, GraphLens codeLens) {
-      SingleValue rewrittenSingleValue =
-          hasSingleValue() ? singleValue.rewrittenWithLens(appView, graphLens, codeLens) : null;
-      DexType rewrittenType = graphLens.lookupType(type, codeLens);
-      if (rewrittenSingleValue != singleValue || rewrittenType != type) {
-        return new RemovedArgumentInfo(checkNullOrZero, rewrittenSingleValue, rewrittenType);
-      }
-      return this;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-      if (obj == null || getClass() != obj.getClass()) {
-        return false;
-      }
-      RemovedArgumentInfo other = (RemovedArgumentInfo) obj;
-      return checkNullOrZero == other.checkNullOrZero
-          && type == other.type
-          && Objects.equals(singleValue, other.singleValue);
-    }
-
-    @Override
-    public int hashCode() {
-      return Objects.hash(checkNullOrZero, singleValue, type);
-    }
-  }
-
-  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();
-    }
-
-    private RewrittenTypeInfo(
-        DexType oldType, DexType newType, DexType castType, SingleValue singleValue) {
-      this.castType = castType;
-      this.oldType = oldType;
-      this.newType = newType;
-      this.singleValue = singleValue;
-      assert !newType.isVoidType() || singleValue != null;
-    }
-
-    public RewrittenTypeInfo combine(RewrittenPrototypeDescription other) {
-      return other.hasRewrittenReturnInfo() ? combine(other.getRewrittenReturnInfo()) : this;
-    }
-
-    public DexType getCastType() {
-      return castType;
-    }
-
-    public DexType getNewType() {
-      return newType;
-    }
-
-    public DexType getOldType() {
-      return oldType;
-    }
-
-    public SingleValue getSingleValue() {
-      return singleValue;
-    }
-
-    boolean hasBeenChangedToReturnVoid() {
-      return newType.isVoidType();
-    }
-
-    public boolean hasCastType() {
-      return castType != null;
-    }
-
-    public boolean hasSingleValue() {
-      return singleValue != null;
-    }
-
-    @Override
-    public boolean isRewrittenTypeInfo() {
-      return true;
-    }
-
-    @Override
-    public RewrittenTypeInfo asRewrittenTypeInfo() {
-      return this;
-    }
-
-    @Override
-    public ArgumentInfo combine(ArgumentInfo info) {
-      if (info.isRemovedArgumentInfo()) {
-        return info;
-      }
-      assert info.isRewrittenTypeInfo();
-      return combine(info.asRewrittenTypeInfo());
-    }
-
-    public RewrittenTypeInfo combine(RewrittenTypeInfo other) {
-      assert !getNewType().isVoidType();
-      assert getNewType() == other.getOldType();
-      return new RewrittenTypeInfo(
-          getOldType(), other.getNewType(), getCastType(), other.getSingleValue());
-    }
-
-    @Override
-    public RewrittenTypeInfo rewrittenWithLens(
-        AppView<AppInfoWithLiveness> appView, GraphLens graphLens, GraphLens codeLens) {
-      DexType rewrittenCastType =
-          castType != null ? graphLens.lookupType(castType, codeLens) : null;
-      DexType rewrittenNewType = graphLens.lookupType(newType, codeLens);
-      SingleValue rewrittenSingleValue =
-          hasSingleValue()
-              ? getSingleValue().rewrittenWithLens(appView, graphLens, codeLens)
-              : null;
-      if (rewrittenCastType != castType
-          || rewrittenNewType != newType
-          || rewrittenSingleValue != singleValue) {
-        // The old type is intentionally not rewritten.
-        return new RewrittenTypeInfo(
-            oldType, rewrittenNewType, rewrittenCastType, rewrittenSingleValue);
-      }
-      return this;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-      if (obj == null || getClass() != obj.getClass()) {
-        return false;
-      }
-      RewrittenTypeInfo other = (RewrittenTypeInfo) obj;
-      return oldType == other.oldType
-          && newType == other.newType
-          && Objects.equals(singleValue, other.singleValue);
-    }
-
-    @Override
-    public int hashCode() {
-      return Objects.hash(oldType, newType, singleValue);
-    }
-
-    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 {
-
-    private static final ArgumentInfoCollection EMPTY = new ArgumentInfoCollection();
-
-    private final Int2ObjectSortedMap<ArgumentInfo> argumentInfos;
-
-    // Specific constructor for empty.
-    private ArgumentInfoCollection() {
-      this.argumentInfos = new Int2ObjectRBTreeMap<>();
-    }
-
-    private ArgumentInfoCollection(Int2ObjectSortedMap<ArgumentInfo> argumentInfos) {
-      assert argumentInfos != null : "should use empty.";
-      assert !argumentInfos.isEmpty() : "should use empty.";
-      this.argumentInfos = argumentInfos;
-    }
-
-    public static ArgumentInfoCollection empty() {
-      return EMPTY;
-    }
-
-    public void forEach(IntObjConsumer<ArgumentInfo> consumer) {
-      for (Entry<ArgumentInfo> entry : argumentInfos.int2ObjectEntrySet()) {
-        consumer.accept(entry.getIntKey(), entry.getValue());
-      }
-    }
-
-    public IntSortedSet getKeys() {
-      return argumentInfos.keySet();
-    }
-
-    public IntCollection getRemovedParameterIndices() {
-      int numberOfRemovedArguments = numberOfRemovedArguments();
-      if (numberOfRemovedArguments == 0) {
-        return IntLists.EMPTY_LIST;
-      }
-      if (numberOfRemovedArguments == argumentInfos.size()) {
-        return getKeys();
-      }
-      IntList removedParameterIndices = new IntArrayList(numberOfRemovedArguments);
-      Iterator<Entry<ArgumentInfo>> iterator = iterator();
-      while (iterator.hasNext()) {
-        Entry<ArgumentInfo> entry = iterator.next();
-        if (entry.getValue().isRemovedArgumentInfo()) {
-          removedParameterIndices.add(entry.getIntKey());
-        }
-      }
-      return removedParameterIndices;
-    }
-
-    public boolean isArgumentRemoved(int argumentIndex) {
-      return getArgumentInfo(argumentIndex).isRemovedArgumentInfo();
-    }
-
-    public boolean isEmpty() {
-      return this == EMPTY;
-    }
-
-    public Iterator<Entry<ArgumentInfo>> iterator() {
-      return argumentInfos.int2ObjectEntrySet().iterator();
-    }
-
-    public boolean hasRemovedArguments() {
-      for (ArgumentInfo value : argumentInfos.values()) {
-        if (value.isRemovedArgumentInfo()) {
-          return true;
-        }
-      }
-      return false;
-    }
-
-    public int numberOfRemovedArguments() {
-      int removed = 0;
-      for (ArgumentInfo value : argumentInfos.values()) {
-        if (value.isRemovedArgumentInfo()) {
-          removed++;
-        }
-      }
-      return removed;
-    }
-
-    public int numberOfRemovedNonReceiverArguments(ProgramMethod method) {
-      return numberOfRemovedArguments()
-          - BooleanUtils.intValue(method.getDefinition().isInstance() && isArgumentRemoved(0));
-    }
-
-    public boolean hasArgumentInfo(int argumentIndex) {
-      return argumentInfos.containsKey(argumentIndex);
-    }
-
-    public ArgumentInfo getArgumentInfo(int argumentIndex) {
-      return argumentInfos.getOrDefault(argumentIndex, ArgumentInfo.NO_INFO);
-    }
-
-    public int size() {
-      return argumentInfos.size();
-    }
-
-    public ArgumentInfoCollection rewrittenWithLens(
-        AppView<AppInfoWithLiveness> appView, GraphLens graphLens, GraphLens codeLens) {
-      Int2ObjectSortedMap<ArgumentInfo> rewrittenArgumentInfos = new Int2ObjectRBTreeMap<>();
-      for (Int2ObjectMap.Entry<ArgumentInfo> entry : argumentInfos.int2ObjectEntrySet()) {
-        ArgumentInfo argumentInfo = entry.getValue();
-        ArgumentInfo rewrittenArgumentInfo =
-            argumentInfo.rewrittenWithLens(appView, graphLens, codeLens);
-        if (rewrittenArgumentInfo != argumentInfo) {
-          rewrittenArgumentInfos.put(entry.getIntKey(), rewrittenArgumentInfo);
-        }
-      }
-      if (!rewrittenArgumentInfos.isEmpty()) {
-        for (Int2ObjectMap.Entry<ArgumentInfo> entry : argumentInfos.int2ObjectEntrySet()) {
-          int key = entry.getIntKey();
-          if (!rewrittenArgumentInfos.containsKey(key)) {
-            rewrittenArgumentInfos.put(key, entry.getValue());
-          }
-        }
-        return new ArgumentInfoCollection(rewrittenArgumentInfos);
-      }
-      return this;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-      if (obj == null || getClass() != obj.getClass()) {
-        return false;
-      }
-      ArgumentInfoCollection other = (ArgumentInfoCollection) obj;
-      return argumentInfos.equals(other.argumentInfos);
-    }
-
-    @Override
-    public int hashCode() {
-      return argumentInfos.hashCode();
-    }
-
-    public static Builder builder() {
-      return new Builder();
-    }
-
-    public static class Builder {
-
-      private Int2ObjectSortedMap<ArgumentInfo> argumentInfos;
-
-      public Builder addArgumentInfo(int argIndex, ArgumentInfo argInfo) {
-        if (argumentInfos == null) {
-          argumentInfos = new Int2ObjectRBTreeMap<>();
-        }
-        assert !argumentInfos.containsKey(argIndex);
-        argumentInfos.put(argIndex, argInfo);
-        return this;
-      }
-
-      public ArgumentInfoCollection build() {
-        if (argumentInfos == null || argumentInfos.isEmpty()) {
-          return EMPTY;
-        }
-        return new ArgumentInfoCollection(argumentInfos);
-      }
-    }
-
-    public ArgumentInfoCollection combine(ArgumentInfoCollection info) {
-      if (isEmpty()) {
-        return info;
-      } else {
-        if (info.isEmpty()) {
-          return this;
-        }
-      }
-
-      Int2ObjectSortedMap<ArgumentInfo> newArgInfos = new Int2ObjectRBTreeMap<>();
-      newArgInfos.putAll(argumentInfos);
-      IntBidirectionalIterator iterator = argumentInfos.keySet().iterator();
-      int offset = 0;
-      int nextArgIndex;
-      for (int pendingArgIndex : info.argumentInfos.keySet()) {
-        nextArgIndex = peekNextOrMax(iterator);
-        while (nextArgIndex <= pendingArgIndex + offset) {
-          iterator.nextInt();
-          ArgumentInfo argumentInfo = argumentInfos.get(nextArgIndex);
-          nextArgIndex = peekNextOrMax(iterator);
-          if (argumentInfo.isRemovedArgumentInfo()) {
-            offset++;
-          }
-        }
-        ArgumentInfo newArgInfo =
-            nextArgIndex == pendingArgIndex + offset
-                ? ArgumentInfo.combine(
-                    argumentInfos.get(nextArgIndex), info.argumentInfos.get(pendingArgIndex))
-                : info.argumentInfos.get(pendingArgIndex);
-        newArgInfos.put(pendingArgIndex + offset, newArgInfo);
-      }
-      assert Ordering.natural().isOrdered(newArgInfos.keySet());
-      return new ArgumentInfoCollection(newArgInfos);
-    }
-
-    static int peekNextOrMax(IntBidirectionalIterator iterator) {
-      if (iterator.hasNext()) {
-        int i = iterator.nextInt();
-        iterator.previousInt();
-        return i;
-      }
-      return Integer.MAX_VALUE;
-    }
-
-    public MethodOptimizationInfoFixer createMethodOptimizationInfoFixer() {
-      RewrittenPrototypeDescription prototypeChanges =
-          RewrittenPrototypeDescription.create(Collections.emptyList(), null, this);
-      return prototypeChanges.createMethodOptimizationInfoFixer();
-    }
-
-    /**
-     * Returns a function for rewriting the parameter annotations on a method info after prototype
-     * changes were made.
-     */
-    public Consumer<DexEncodedMethod.Builder> createParameterAnnotationsRemover(
-        DexEncodedMethod method) {
-      if (numberOfRemovedArguments() > 0 && !method.parameterAnnotationsList.isEmpty()) {
-        return builder -> {
-          int firstArgumentIndex = method.getFirstNonReceiverArgumentIndex();
-          builder.removeParameterAnnotations(
-              oldIndex -> getArgumentInfo(oldIndex + firstArgumentIndex).isRemovedArgumentInfo());
-        };
-      }
-      return ConsumerUtils.emptyConsumer();
-    }
-
-    public int getNewArgumentIndex(int argumentIndex) {
-      int numberOfArgumentsRemovedBeforeArgument = 0;
-      Iterator<Entry<ArgumentInfo>> iterator = iterator();
-      while (iterator.hasNext()) {
-        Entry<ArgumentInfo> entry = iterator.next();
-        int argumentIndexForInfo = entry.getIntKey();
-        if (argumentIndexForInfo >= argumentIndex) {
-          break;
-        }
-        ArgumentInfo argumentInfo = entry.getValue();
-        if (argumentInfo.isRemovedArgumentInfo()) {
-          numberOfArgumentsRemovedBeforeArgument++;
-        }
-      }
-      assert IteratorUtils.allRemainingMatchDestructive(
-          iterator, entry -> entry.getIntKey() >= argumentIndex);
-      return argumentIndex - numberOfArgumentsRemovedBeforeArgument;
-    }
-  }
-
-  private static final RewrittenPrototypeDescription NONE = new RewrittenPrototypeDescription();
-
-  private final List<ExtraParameter> extraParameters;
-  private final ArgumentInfoCollection argumentInfoCollection;
-  private final RewrittenTypeInfo rewrittenReturnInfo;
-
-  private RewrittenPrototypeDescription() {
-    this.extraParameters = Collections.emptyList();
-    this.rewrittenReturnInfo = null;
-    this.argumentInfoCollection = ArgumentInfoCollection.empty();
-  }
-
-  private RewrittenPrototypeDescription(
-      List<ExtraParameter> extraParameters,
-      RewrittenTypeInfo rewrittenReturnInfo,
-      ArgumentInfoCollection argumentsInfo) {
-    assert argumentsInfo != null;
-    this.extraParameters = extraParameters;
-    this.rewrittenReturnInfo = rewrittenReturnInfo;
-    this.argumentInfoCollection = argumentsInfo;
-    assert !isEmpty();
-  }
-
-  public static RewrittenPrototypeDescription create(
-      List<ExtraParameter> extraParameters,
-      RewrittenTypeInfo rewrittenReturnInfo,
-      ArgumentInfoCollection argumentsInfo) {
-    return extraParameters.isEmpty() && rewrittenReturnInfo == null && argumentsInfo.isEmpty()
-        ? none()
-        : new RewrittenPrototypeDescription(extraParameters, rewrittenReturnInfo, argumentsInfo);
-  }
-
-  public static RewrittenPrototypeDescription createForRewrittenTypes(
-      RewrittenTypeInfo returnInfo, ArgumentInfoCollection rewrittenArgumentsInfo) {
-    return create(Collections.emptyList(), returnInfo, rewrittenArgumentsInfo);
-  }
-
-  public static RewrittenPrototypeDescription none() {
-    return NONE;
-  }
-
-  public Consumer<DexEncodedMethod.Builder> createParameterAnnotationsRemover(
-      DexEncodedMethod method) {
-    return getArgumentInfoCollection().createParameterAnnotationsRemover(method);
-  }
-
-  public MethodOptimizationInfoFixer createMethodOptimizationInfoFixer() {
-    return new RewrittenPrototypeDescriptionMethodOptimizationInfoFixer(this);
-  }
-
-  public RewrittenPrototypeDescription combine(RewrittenPrototypeDescription other) {
-    if (isEmpty()) {
-      return other;
-    }
-    if (other.isEmpty()) {
-      return this;
-    }
-    // We currently don't have any passes that remove extra parameters inserted by previous passes.
-    // If the input prototype changes have removed some of the extra parameters, we would need to
-    // adapt the merging of prototype changes below.
-    List<ExtraParameter> newExtraParameters =
-        ImmutableList.<ExtraParameter>builder()
-            .addAll(getExtraParameters())
-            .addAll(other.getExtraParameters())
-            .build();
-    RewrittenTypeInfo newRewrittenTypeInfo =
-        hasRewrittenReturnInfo()
-            ? getRewrittenReturnInfo().combine(other)
-            : other.getRewrittenReturnInfo();
-    ArgumentInfoCollection newArgumentInfoCollection =
-        getArgumentInfoCollection().combine(other.getArgumentInfoCollection());
-    return new RewrittenPrototypeDescription(
-        newExtraParameters, newRewrittenTypeInfo, newArgumentInfoCollection);
-  }
-
-  public boolean isEmpty() {
-    return extraParameters.isEmpty()
-        && rewrittenReturnInfo == null
-        && argumentInfoCollection.isEmpty();
-  }
-
-  public boolean hasExtraParameters() {
-    return !extraParameters.isEmpty();
-  }
-
-  public List<ExtraParameter> getExtraParameters() {
-    return extraParameters;
-  }
-
-  public int numberOfExtraParameters() {
-    return extraParameters.size();
-  }
-
-  public boolean hasBeenChangedToReturnVoid() {
-    return rewrittenReturnInfo != null && rewrittenReturnInfo.hasBeenChangedToReturnVoid();
-  }
-
-  public ArgumentInfoCollection getArgumentInfoCollection() {
-    return argumentInfoCollection;
-  }
-
-  public boolean hasRewrittenReturnInfo() {
-    return rewrittenReturnInfo != null;
-  }
-
-  public boolean requiresRewritingAtCallSite() {
-    return hasRewrittenReturnInfo()
-        || numberOfExtraParameters() > 0
-        || argumentInfoCollection.numberOfRemovedArguments() > 0;
-  }
-
-  public RewrittenTypeInfo getRewrittenReturnInfo() {
-    return rewrittenReturnInfo;
-  }
-
-  /**
-   * Returns the {@link ConstInstruction} that should be used to materialize the result of
-   * invocations to the method represented by this {@link RewrittenPrototypeDescription}.
-   *
-   * <p>This method should only be used for methods that return a constant value and whose return
-   * type has been changed to void.
-   *
-   * <p>Note that the current implementation always returns null at this point.
-   */
-  public Instruction getConstantReturn(
-      AppView<AppInfoWithLiveness> appView,
-      IRCode code,
-      Position position,
-      TypeAndLocalInfoSupplier info) {
-    assert rewrittenReturnInfo != null;
-    assert rewrittenReturnInfo.hasSingleValue();
-    Instruction instruction =
-        rewrittenReturnInfo.getSingleValue().createMaterializingInstruction(appView, code, info);
-    instruction.setPosition(position);
-    return instruction;
-  }
-
-  public boolean verifyConstantReturnAccessibleInContext(
-      AppView<AppInfoWithLiveness> appView, ProgramMethod method, GraphLens codeLens) {
-    SingleValue rewrittenSingleValue =
-        rewrittenReturnInfo
-            .getSingleValue()
-            .rewrittenWithLens(appView, appView.graphLens(), codeLens);
-    assert rewrittenSingleValue.isMaterializableInContext(appView, method);
-    return true;
-  }
-
-  public DexMethod rewriteMethod(ProgramMethod method, DexItemFactory dexItemFactory) {
-    if (isEmpty()) {
-      return method.getReference();
-    }
-    DexProto rewrittenProto = rewriteProto(method, dexItemFactory);
-    return method.getReference().withProto(rewrittenProto, dexItemFactory);
-  }
-
-  public DexProto rewriteProto(ProgramMethod method, DexItemFactory dexItemFactory) {
-    if (isEmpty()) {
-      return method.getProto();
-    }
-    DexType newReturnType =
-        rewrittenReturnInfo != null ? rewrittenReturnInfo.getNewType() : method.getReturnType();
-    DexType[] newParameters = rewriteParameters(method, dexItemFactory);
-    return dexItemFactory.createProto(newReturnType, newParameters);
-  }
-
-  public DexType[] rewriteParameters(ProgramMethod method, DexItemFactory dexItemFactory) {
-    DexType[] params = method.getParameters().values;
-    if (isEmpty()) {
-      return params;
-    }
-    DexType[] newParams =
-        new DexType
-            [params.length
-                - argumentInfoCollection.numberOfRemovedNonReceiverArguments(method)
-                + extraParameters.size()];
-    int offset = method.getDefinition().getFirstNonReceiverArgumentIndex();
-    int newParamIndex = 0;
-    for (int oldParamIndex = 0; oldParamIndex < params.length; oldParamIndex++) {
-      ArgumentInfo argInfo = argumentInfoCollection.getArgumentInfo(oldParamIndex + offset);
-      if (argInfo.isNone()) {
-        newParams[newParamIndex++] = params[oldParamIndex];
-      } else if (argInfo.isRewrittenTypeInfo()) {
-        RewrittenTypeInfo rewrittenTypeInfo = argInfo.asRewrittenTypeInfo();
-        assert params[oldParamIndex] == rewrittenTypeInfo.oldType;
-        newParams[newParamIndex++] = rewrittenTypeInfo.newType;
-      }
-    }
-    for (ExtraParameter extraParameter : extraParameters) {
-      newParams[newParamIndex++] = extraParameter.getType(dexItemFactory);
-    }
-    return newParams;
-  }
-
-  public RewrittenPrototypeDescription rewrittenWithLens(
-      AppView<AppInfoWithLiveness> appView, GraphLens graphLens, GraphLens codeLens) {
-    ArgumentInfoCollection newArgumentInfoCollection =
-        argumentInfoCollection.rewrittenWithLens(appView, graphLens, codeLens);
-    RewrittenTypeInfo newRewrittenReturnInfo =
-        hasRewrittenReturnInfo()
-            ? rewrittenReturnInfo.rewrittenWithLens(appView, graphLens, codeLens)
-            : null;
-    if (newArgumentInfoCollection != argumentInfoCollection
-        || newRewrittenReturnInfo != rewrittenReturnInfo) {
-      return new RewrittenPrototypeDescription(
-          extraParameters, newRewrittenReturnInfo, newArgumentInfoCollection);
-    }
-    return this;
-  }
-
-  public RewrittenPrototypeDescription withRewrittenReturnInfo(
-      RewrittenTypeInfo newRewrittenReturnInfo) {
-    if (Objects.equals(rewrittenReturnInfo, newRewrittenReturnInfo)) {
-      return this;
-    }
-    return new RewrittenPrototypeDescription(
-        extraParameters, newRewrittenReturnInfo, argumentInfoCollection);
-  }
-
-  public RewrittenPrototypeDescription withExtraUnusedNullParameters(
-      int numberOfExtraUnusedNullParameters) {
-    List<ExtraParameter> parameters =
-        Collections.nCopies(numberOfExtraUnusedNullParameters, new ExtraUnusedNullParameter());
-    return withExtraParameters(parameters);
-  }
-
-  public RewrittenPrototypeDescription withExtraParameters(ExtraParameter... parameters) {
-    return withExtraParameters(Arrays.asList(parameters));
-  }
-
-  public RewrittenPrototypeDescription withExtraParameters(List<ExtraParameter> parameters) {
-    if (parameters.isEmpty()) {
-      return this;
-    }
-    List<ExtraParameter> newExtraParameters =
-        new ArrayList<>(extraParameters.size() + parameters.size());
-    newExtraParameters.addAll(extraParameters);
-    newExtraParameters.addAll(parameters);
-    return new RewrittenPrototypeDescription(
-        newExtraParameters, rewrittenReturnInfo, argumentInfoCollection);
-  }
-
-  @Override
-  public boolean equals(Object obj) {
-    if (obj == null || getClass() != obj.getClass()) {
-      return false;
-    }
-    RewrittenPrototypeDescription other = (RewrittenPrototypeDescription) obj;
-    return extraParameters.equals(other.extraParameters)
-        && Objects.equals(rewrittenReturnInfo, other.rewrittenReturnInfo)
-        && argumentInfoCollection.equals(other.argumentInfoCollection);
-  }
-
-  @Override
-  public int hashCode() {
-    return Objects.hash(extraParameters, rewrittenReturnInfo, argumentInfoCollection);
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescriptionMethodOptimizationInfoFixer.java b/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescriptionMethodOptimizationInfoFixer.java
index e25d391..f949105 100644
--- a/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescriptionMethodOptimizationInfoFixer.java
+++ b/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescriptionMethodOptimizationInfoFixer.java
@@ -4,8 +4,9 @@
 
 package com.android.tools.r8.graph;
 
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfo;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
+import com.android.tools.r8.graph.proto.ArgumentInfo;
+import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
+import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
 import com.android.tools.r8.ir.analysis.inlining.SimpleInliningConstraint;
 import com.android.tools.r8.ir.analysis.inlining.SimpleInliningConstraintFactory;
 import com.android.tools.r8.ir.optimize.classinliner.constraint.ClassInlinerMethodConstraint;
diff --git a/src/main/java/com/android/tools/r8/graph/ThrowExceptionCode.java b/src/main/java/com/android/tools/r8/graph/ThrowExceptionCode.java
index 8928361..3420b1b 100644
--- a/src/main/java/com/android/tools/r8/graph/ThrowExceptionCode.java
+++ b/src/main/java/com/android/tools/r8/graph/ThrowExceptionCode.java
@@ -13,6 +13,7 @@
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DexCode.Try;
 import com.android.tools.r8.graph.DexCode.TryHandler;
+import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.NumberGenerator;
 import com.android.tools.r8.ir.code.Position;
diff --git a/src/main/java/com/android/tools/r8/graph/ThrowNullCode.java b/src/main/java/com/android/tools/r8/graph/ThrowNullCode.java
index c573130..03b811f 100644
--- a/src/main/java/com/android/tools/r8/graph/ThrowNullCode.java
+++ b/src/main/java/com/android/tools/r8/graph/ThrowNullCode.java
@@ -12,6 +12,7 @@
 import com.android.tools.r8.dex.MixedSectionCollection;
 import com.android.tools.r8.graph.DexCode.Try;
 import com.android.tools.r8.graph.DexCode.TryHandler;
+import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.NumberGenerator;
 import com.android.tools.r8.ir.code.Position;
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/ClassInitializerAssertionEnablingAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/ClassInitializerAssertionEnablingAnalysis.java
index a6045e4..d110049 100644
--- a/src/main/java/com/android/tools/r8/graph/analysis/ClassInitializerAssertionEnablingAnalysis.java
+++ b/src/main/java/com/android/tools/r8/graph/analysis/ClassInitializerAssertionEnablingAnalysis.java
@@ -14,42 +14,78 @@
 import com.android.tools.r8.cf.code.CfLoad;
 import com.android.tools.r8.cf.code.CfLogicalBinop;
 import com.android.tools.r8.cf.code.CfStaticFieldWrite;
+import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.CfCode;
-import com.android.tools.r8.graph.Code;
 import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.FieldResolutionResult;
 import com.android.tools.r8.graph.ProgramDefinition;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
 import com.android.tools.r8.shaking.EnqueuerWorklist;
+import com.android.tools.r8.utils.AssertionConfigurationWithDefault;
 import com.google.common.collect.ImmutableList;
 import java.util.List;
+import java.util.stream.Collectors;
 import org.objectweb.asm.Opcodes;
 
-public class ClassInitializerAssertionEnablingAnalysis extends EnqueuerAnalysis {
+public class ClassInitializerAssertionEnablingAnalysis extends EnqueuerAnalysis
+    implements EnqueuerFieldAccessAnalysis {
   private final DexItemFactory dexItemFactory;
   private final OptimizationFeedback feedback;
   private final DexString kotlinAssertionsEnabled;
+  private final AssertionConfigurationWithDefault assertionsConfiguration;
+  private final List<DexMethod> assertionHandlers;
 
   public ClassInitializerAssertionEnablingAnalysis(
-      DexItemFactory dexItemFactory, OptimizationFeedback feedback) {
-    this.dexItemFactory = dexItemFactory;
+      AppView<?> appView, OptimizationFeedback feedback) {
+    this.dexItemFactory = appView.dexItemFactory();
     this.feedback = feedback;
     this.kotlinAssertionsEnabled = dexItemFactory.createString("ENABLED");
+    this.assertionsConfiguration = appView.options().assertionsConfiguration;
+    this.assertionHandlers =
+        assertionsConfiguration.getAllAssertionHandlers().stream()
+            .map(dexItemFactory::createMethod)
+            .collect(Collectors.toList());
+  }
+
+  private boolean isUsingJavaAssertionsDisabledField(DexField field) {
+    // This does not check the holder, as for inner classes the field is read from the outer class
+    // and not the class itself.
+    return field.getName() == dexItemFactory.assertionsDisabled
+        && field.getType() == dexItemFactory.booleanType;
+  }
+
+  private boolean isUsingKotlinAssertionsEnabledField(DexField field) {
+    return field == dexItemFactory.kotlin.assertions.enabledField;
+  }
+
+  @Override
+  public void traceStaticFieldRead(
+      DexField field,
+      FieldResolutionResult resolutionResult,
+      ProgramMethod context,
+      EnqueuerWorklist worklist) {
+    if (isUsingJavaAssertionsDisabledField(field) || isUsingKotlinAssertionsEnabledField(field)) {
+      assertionHandlers.forEach(
+          assertionHandler -> worklist.enqueueTraceInvokeStaticAction(assertionHandler, context));
+    }
   }
 
   @Override
   public void processNewlyLiveMethod(
       ProgramMethod method, ProgramDefinition context, EnqueuerWorklist worklist) {
     DexEncodedMethod definition = method.getDefinition();
-    if (definition.isClassInitializer()) {
-      Code code = definition.getCode();
-      if (code.isCfCode()) {
-        if (hasJavacClinitAssertionCode(code.asCfCode()) || hasKotlincClinitAssertionCode(method)) {
-          feedback.setInitializerEnablingJavaVmAssertions(definition);
-        }
-      }
+    if (!definition.hasCode() || !definition.getCode().isCfCode()) {
+      return;
+    }
+    CfCode code = definition.getCode().asCfCode();
+    if (definition.isClassInitializer()
+        && (hasJavacClinitAssertionCode(code) || hasKotlincClinitAssertionCode(method))) {
+      feedback.setInitializerEnablingJavaVmAssertions(definition);
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerFieldAccessAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerFieldAccessAnalysis.java
index eee1f34..d8dadc5 100644
--- a/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerFieldAccessAnalysis.java
+++ b/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerFieldAccessAnalysis.java
@@ -7,18 +7,35 @@
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.FieldResolutionResult;
 import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.shaking.EnqueuerWorklist;
 
 public interface EnqueuerFieldAccessAnalysis {
 
-  void traceInstanceFieldRead(
-      DexField field, FieldResolutionResult resolutionResult, ProgramMethod context);
+  default void traceInstanceFieldRead(
+      DexField field,
+      FieldResolutionResult resolutionResult,
+      ProgramMethod context,
+      EnqueuerWorklist worklist) {}
+  ;
 
-  void traceInstanceFieldWrite(
-      DexField field, FieldResolutionResult resolutionResult, ProgramMethod context);
+  default void traceInstanceFieldWrite(
+      DexField field,
+      FieldResolutionResult resolutionResult,
+      ProgramMethod context,
+      EnqueuerWorklist worklist) {}
+  ;
 
-  void traceStaticFieldRead(
-      DexField field, FieldResolutionResult resolutionResult, ProgramMethod context);
+  default void traceStaticFieldRead(
+      DexField field,
+      FieldResolutionResult resolutionResult,
+      ProgramMethod context,
+      EnqueuerWorklist worklist) {}
+  ;
 
-  void traceStaticFieldWrite(
-      DexField field, FieldResolutionResult resolutionResult, ProgramMethod context);
+  default void traceStaticFieldWrite(
+      DexField field,
+      FieldResolutionResult resolutionResult,
+      ProgramMethod context,
+      EnqueuerWorklist worklist) {}
+  ;
 }
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/GetArrayOfMissingTypeVerifyErrorWorkaround.java b/src/main/java/com/android/tools/r8/graph/analysis/GetArrayOfMissingTypeVerifyErrorWorkaround.java
index 74817d0..48ca34d 100644
--- a/src/main/java/com/android/tools/r8/graph/analysis/GetArrayOfMissingTypeVerifyErrorWorkaround.java
+++ b/src/main/java/com/android/tools/r8/graph/analysis/GetArrayOfMissingTypeVerifyErrorWorkaround.java
@@ -14,6 +14,7 @@
 import com.android.tools.r8.graph.FieldResolutionResult;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.shaking.Enqueuer;
+import com.android.tools.r8.shaking.EnqueuerWorklist;
 import com.android.tools.r8.shaking.KeepInfo.Joiner;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.InternalOptions;
@@ -61,7 +62,10 @@
 
   @Override
   public void traceInstanceFieldRead(
-      DexField field, FieldResolutionResult resolutionResult, ProgramMethod context) {
+      DexField field,
+      FieldResolutionResult resolutionResult,
+      ProgramMethod context,
+      EnqueuerWorklist worklist) {
     if (isUnsafeToUseFieldOnDalvik(field)) {
       enqueuer.getKeepInfo().joinMethod(context, Joiner::disallowOptimization);
     }
@@ -69,7 +73,10 @@
 
   @Override
   public void traceStaticFieldRead(
-      DexField field, FieldResolutionResult resolutionResult, ProgramMethod context) {
+      DexField field,
+      FieldResolutionResult resolutionResult,
+      ProgramMethod context,
+      EnqueuerWorklist worklist) {
     if (isUnsafeToUseFieldOnDalvik(field)) {
       enqueuer.getKeepInfo().joinMethod(context, Joiner::disallowOptimization);
     }
@@ -95,13 +102,19 @@
 
   @Override
   public void traceInstanceFieldWrite(
-      DexField field, FieldResolutionResult resolutionResult, ProgramMethod context) {
+      DexField field,
+      FieldResolutionResult resolutionResult,
+      ProgramMethod context,
+      EnqueuerWorklist worklist) {
     // Intentionally empty.
   }
 
   @Override
   public void traceStaticFieldWrite(
-      DexField field, FieldResolutionResult resolutionResult, ProgramMethod context) {
+      DexField field,
+      FieldResolutionResult resolutionResult,
+      ProgramMethod context,
+      EnqueuerWorklist worklist) {
     // Intentionally empty.
   }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/proto/ArgumentInfo.java b/src/main/java/com/android/tools/r8/graph/proto/ArgumentInfo.java
new file mode 100644
index 0000000..26cf24f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/proto/ArgumentInfo.java
@@ -0,0 +1,74 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.graph.proto;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+
+public abstract class ArgumentInfo {
+
+  static final ArgumentInfo NO_INFO =
+      new ArgumentInfo() {
+
+        @Override
+        public ArgumentInfo combine(ArgumentInfo info) {
+          return info;
+        }
+
+        @Override
+        public boolean isNone() {
+          return true;
+        }
+
+        @Override
+        public ArgumentInfo rewrittenWithLens(
+            AppView<AppInfoWithLiveness> appView, GraphLens graphLens, GraphLens codeLens) {
+          return this;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+          return obj == this;
+        }
+
+        @Override
+        public int hashCode() {
+          return System.identityHashCode(this);
+        }
+      };
+
+  public boolean isNone() {
+    return false;
+  }
+
+  public boolean isRemovedArgumentInfo() {
+    return false;
+  }
+
+  public RemovedArgumentInfo asRemovedArgumentInfo() {
+    return null;
+  }
+
+  public boolean isRewrittenTypeInfo() {
+    return false;
+  }
+
+  public RewrittenTypeInfo asRewrittenTypeInfo() {
+    return null;
+  }
+
+  // ArgumentInfo are combined with `this` first, and the `info` argument second.
+  public abstract ArgumentInfo combine(ArgumentInfo info);
+
+  public abstract ArgumentInfo rewrittenWithLens(
+      AppView<AppInfoWithLiveness> appView, GraphLens graphLens, GraphLens codeLens);
+
+  @Override
+  public abstract boolean equals(Object obj);
+
+  @Override
+  public abstract int hashCode();
+}
diff --git a/src/main/java/com/android/tools/r8/graph/proto/ArgumentInfoCollection.java b/src/main/java/com/android/tools/r8/graph/proto/ArgumentInfoCollection.java
new file mode 100644
index 0000000..0a9fcbf
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/proto/ArgumentInfoCollection.java
@@ -0,0 +1,356 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.graph.proto;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfoFixer;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.ConsumerUtils;
+import com.android.tools.r8.utils.IntObjConsumer;
+import it.unimi.dsi.fastutil.ints.Int2ObjectMap.Entry;
+import it.unimi.dsi.fastutil.ints.Int2ObjectRBTreeMap;
+import it.unimi.dsi.fastutil.ints.Int2ObjectSortedMap;
+import it.unimi.dsi.fastutil.ints.IntArrayList;
+import it.unimi.dsi.fastutil.ints.IntCollection;
+import it.unimi.dsi.fastutil.ints.IntList;
+import it.unimi.dsi.fastutil.ints.IntLists;
+import it.unimi.dsi.fastutil.ints.IntSortedSet;
+import it.unimi.dsi.fastutil.objects.ObjectBidirectionalIterator;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Objects;
+import java.util.function.Consumer;
+
+public class ArgumentInfoCollection {
+
+  private static final Int2ObjectRBTreeMap<ArgumentInfo> EMPTY_MAP = new Int2ObjectRBTreeMap<>();
+  private static final ArgumentInfoCollection EMPTY = new ArgumentInfoCollection();
+
+  private final Int2ObjectSortedMap<ArgumentInfo> argumentInfos;
+  private final int argumentInfosSize;
+  private final ArgumentPermutation argumentPermutation;
+
+  // Specific constructor for empty.
+  private ArgumentInfoCollection() {
+    this.argumentInfos = EMPTY_MAP;
+    this.argumentInfosSize = -1;
+    this.argumentPermutation = ArgumentPermutation.getDefault();
+  }
+
+  private ArgumentInfoCollection(
+      Int2ObjectSortedMap<ArgumentInfo> argumentInfos,
+      int argumentInfosSize,
+      ArgumentPermutation argumentPermutation) {
+    assert argumentInfos != null;
+    assert argumentPermutation != null;
+    assert !argumentInfos.isEmpty() || argumentInfos == EMPTY_MAP;
+    assert !argumentInfos.isEmpty() || !argumentPermutation.isDefault() : "should use empty.";
+    assert argumentInfosSize >= 0;
+    this.argumentInfos = argumentInfos;
+    this.argumentInfosSize = argumentInfosSize;
+    this.argumentPermutation = argumentPermutation;
+  }
+
+  public static ArgumentInfoCollection empty() {
+    return EMPTY;
+  }
+
+  public void forEach(IntObjConsumer<ArgumentInfo> consumer) {
+    for (Entry<ArgumentInfo> entry : argumentInfos.int2ObjectEntrySet()) {
+      consumer.accept(entry.getIntKey(), entry.getValue());
+    }
+  }
+
+  public IntSortedSet getKeys() {
+    return argumentInfos.keySet();
+  }
+
+  public IntCollection getRemovedParameterIndices() {
+    int numberOfRemovedArguments = numberOfRemovedArguments();
+    if (numberOfRemovedArguments == 0) {
+      return IntLists.EMPTY_LIST;
+    }
+    if (numberOfRemovedArguments == argumentInfos.size()) {
+      return getKeys();
+    }
+    IntList removedParameterIndices = new IntArrayList(numberOfRemovedArguments);
+    Iterator<Entry<ArgumentInfo>> iterator = iterator();
+    while (iterator.hasNext()) {
+      Entry<ArgumentInfo> entry = iterator.next();
+      if (entry.getValue().isRemovedArgumentInfo()) {
+        removedParameterIndices.add(entry.getIntKey());
+      }
+    }
+    return removedParameterIndices;
+  }
+
+  public boolean isArgumentRemoved(int argumentIndex) {
+    return getArgumentInfo(argumentIndex).isRemovedArgumentInfo();
+  }
+
+  public boolean isEmpty() {
+    return this == EMPTY;
+  }
+
+  public Iterator<Entry<ArgumentInfo>> iterator() {
+    return argumentInfos.int2ObjectEntrySet().iterator();
+  }
+
+  public boolean hasRemovedArguments() {
+    for (ArgumentInfo value : argumentInfos.values()) {
+      if (value.isRemovedArgumentInfo()) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  public int numberOfRemovedArguments() {
+    return getNumberOfRemovedArgumentsBefore(Integer.MAX_VALUE, argumentInfos);
+  }
+
+  public int getNumberOfRemovedArgumentsBefore(int index) {
+    return getNumberOfRemovedArgumentsBefore(index, argumentInfos);
+  }
+
+  private static int getNumberOfRemovedArgumentsBefore(
+      int index, Int2ObjectSortedMap<ArgumentInfo> argumentInfos) {
+    int removed = 0;
+    for (Entry<ArgumentInfo> entry : argumentInfos.int2ObjectEntrySet()) {
+      int argumentIndex = entry.getIntKey();
+      ArgumentInfo argumentInfo = entry.getValue();
+      if (argumentIndex >= index) {
+        assert argumentIndex > index || !argumentInfo.isRemovedArgumentInfo();
+        break;
+      }
+      if (argumentInfo.isRemovedArgumentInfo()) {
+        removed++;
+      }
+    }
+    return removed;
+  }
+
+  public int numberOfRemovedNonReceiverArguments(ProgramMethod method) {
+    return numberOfRemovedArguments()
+        - BooleanUtils.intValue(method.getDefinition().isInstance() && isArgumentRemoved(0));
+  }
+
+  public boolean hasArgumentInfo(int argumentIndex) {
+    return argumentInfos.containsKey(argumentIndex);
+  }
+
+  public boolean hasArgumentPermutation() {
+    return !argumentPermutation.isDefault();
+  }
+
+  public ArgumentInfo getArgumentInfo(int argumentIndex) {
+    return argumentInfos.getOrDefault(argumentIndex, ArgumentInfo.NO_INFO);
+  }
+
+  public int getNewArgumentIndex(int argumentIndex) {
+    return getNewArgumentIndex(argumentIndex, getNumberOfRemovedArgumentsBefore(argumentIndex));
+  }
+
+  public int getNewArgumentIndex(int argumentIndex, int numberOfRemovedArgumentsBefore) {
+    int intermediateArgumentIndex = argumentIndex - numberOfRemovedArgumentsBefore;
+    return argumentPermutation.getNewArgumentIndex(intermediateArgumentIndex);
+  }
+
+  public int size() {
+    assert !isEmpty();
+    return argumentInfosSize;
+  }
+
+  public ArgumentInfoCollection rewrittenWithLens(
+      AppView<AppInfoWithLiveness> appView, GraphLens graphLens, GraphLens codeLens) {
+    if (isEmpty()) {
+      return this;
+    }
+    Builder builder = builder();
+    forEach(
+        (argumentIndex, argumentInfo) -> {
+          ArgumentInfo rewrittenArgumentInfo =
+              argumentInfo.rewrittenWithLens(appView, graphLens, codeLens);
+          if (rewrittenArgumentInfo != argumentInfo) {
+            builder.addArgumentInfo(argumentIndex, rewrittenArgumentInfo);
+          }
+        });
+    if (!builder.isEmpty()) {
+      forEach(
+          (argumentIndex, argumentInfo) -> {
+            if (!builder.hasArgumentInfo(argumentIndex)) {
+              builder.addArgumentInfo(argumentIndex, argumentInfo);
+            }
+          });
+      return builder.setArgumentInfosSize(argumentInfosSize).build();
+    }
+    return this;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (this == obj) {
+      return true;
+    }
+    if (obj == null || getClass() != obj.getClass()) {
+      return false;
+    }
+    ArgumentInfoCollection other = (ArgumentInfoCollection) obj;
+    return argumentInfos.equals(other.argumentInfos)
+        && argumentPermutation.equals(other.argumentPermutation)
+        && argumentInfosSize == other.argumentInfosSize;
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(argumentInfos, argumentPermutation, argumentInfosSize);
+  }
+
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  public static class Builder {
+
+    private Int2ObjectSortedMap<ArgumentInfo> argumentInfos = new Int2ObjectRBTreeMap<>();
+    private int argumentInfosSize = -1;
+    private ArgumentPermutation argumentPermutation = ArgumentPermutation.getDefault();
+
+    public Builder addArgumentInfo(int argumentIndex, ArgumentInfo argInfo) {
+      argumentInfos.put(argumentIndex, argInfo);
+      return this;
+    }
+
+    public Builder addArgumentInfos(ArgumentInfoCollection argumentInfoCollection) {
+      argumentInfoCollection.forEach(this::addArgumentInfo);
+      return this;
+    }
+
+    public int getNumberOfRemovedArgumentsBefore(int index) {
+      return ArgumentInfoCollection.getNumberOfRemovedArgumentsBefore(index, argumentInfos);
+    }
+
+    public boolean hasArgumentInfo(int argumentIndex) {
+      return argumentInfos.containsKey(argumentIndex);
+    }
+
+    public boolean isEmpty() {
+      return argumentInfos.isEmpty() && argumentPermutation.isDefault();
+    }
+
+    public Builder setArgumentInfosSize(int argumentInfosSize) {
+      this.argumentInfosSize = argumentInfosSize;
+      return this;
+    }
+
+    public Builder setArgumentPermutation(ArgumentPermutation argumentPermutation) {
+      this.argumentPermutation = argumentPermutation;
+      return this;
+    }
+
+    public ArgumentInfoCollection build() {
+      if (isEmpty()) {
+        return empty();
+      }
+      Int2ObjectSortedMap<ArgumentInfo> argumentInfosOrEmpty =
+          argumentInfos.isEmpty() ? EMPTY_MAP : argumentInfos;
+      return new ArgumentInfoCollection(
+          argumentInfosOrEmpty, argumentInfosSize, argumentPermutation);
+    }
+  }
+
+  public ArgumentInfoCollection combine(ArgumentInfoCollection other) {
+    if (isEmpty()) {
+      return other;
+    }
+    if (other.isEmpty()) {
+      return this;
+    }
+    Builder builder = builder().addArgumentInfos(this);
+    ObjectBidirectionalIterator<Entry<ArgumentInfo>> iterator =
+        argumentInfos.int2ObjectEntrySet().iterator();
+    int offset = 0;
+    for (Entry<ArgumentInfo> entry : other.argumentInfos.int2ObjectEntrySet()) {
+      int pendingArgumentIndex = entry.getIntKey();
+      ArgumentInfo pendingArgumentInfo = entry.getValue();
+      Entry<ArgumentInfo> nextCommittedEntry = peekNext(iterator);
+      while (nextCommittedEntry != null
+          && nextCommittedEntry.getIntKey() <= pendingArgumentIndex + offset) {
+        Entry<ArgumentInfo> committedEntry = iterator.next();
+        ArgumentInfo committedArgumentInfo = committedEntry.getValue();
+        if (committedArgumentInfo.isRemovedArgumentInfo()) {
+          offset++;
+        }
+        nextCommittedEntry = peekNext(iterator);
+      }
+      if (nextCommittedEntry != null
+          && nextCommittedEntry.getIntKey() == pendingArgumentIndex + offset) {
+        ArgumentInfo committedArgumentInfo = nextCommittedEntry.getValue();
+        assert !committedArgumentInfo.isRemovedArgumentInfo();
+        pendingArgumentInfo = committedArgumentInfo.combine(pendingArgumentInfo);
+      }
+      builder.addArgumentInfo(pendingArgumentIndex + offset, pendingArgumentInfo);
+    }
+    // TODO(b/195112263): Double-check the size of the permutation in presence of extra and removed
+    //  arguments.
+    ArgumentPermutation.Builder argumentPermutationBuilder =
+        ArgumentPermutation.builder(argumentInfosSize);
+    for (int argumentIndex = 0; argumentIndex < argumentInfosSize; argumentIndex++) {
+      if (isArgumentRemoved(argumentIndex)) {
+        continue;
+      }
+      int intermediateArgumentIndex = getNewArgumentIndex(argumentIndex);
+      if (other.isArgumentRemoved(intermediateArgumentIndex)) {
+        continue;
+      }
+      int newArgumentIndex = other.getNewArgumentIndex(intermediateArgumentIndex);
+      int defaultNewArgumentIndex =
+          argumentIndex - builder.getNumberOfRemovedArgumentsBefore(argumentIndex);
+      if (newArgumentIndex != defaultNewArgumentIndex) {
+        argumentPermutationBuilder.setNewArgumentIndex(argumentIndex, newArgumentIndex);
+      }
+    }
+    return builder
+        .setArgumentInfosSize(argumentInfosSize)
+        .setArgumentPermutation(argumentPermutationBuilder.build())
+        .build();
+  }
+
+  private static Entry<ArgumentInfo> peekNext(
+      ObjectBidirectionalIterator<Entry<ArgumentInfo>> iterator) {
+    if (iterator.hasNext()) {
+      Entry<ArgumentInfo> entry = iterator.next();
+      iterator.previous();
+      return entry;
+    }
+    return null;
+  }
+
+  public MethodOptimizationInfoFixer createMethodOptimizationInfoFixer() {
+    RewrittenPrototypeDescription prototypeChanges =
+        RewrittenPrototypeDescription.create(Collections.emptyList(), null, this);
+    return prototypeChanges.createMethodOptimizationInfoFixer();
+  }
+
+  /**
+   * Returns a function for rewriting the parameter annotations on a method info after prototype
+   * changes were made.
+   */
+  public Consumer<DexEncodedMethod.Builder> createParameterAnnotationsRemover(
+      DexEncodedMethod method) {
+    if (numberOfRemovedArguments() > 0 && !method.parameterAnnotationsList.isEmpty()) {
+      return builder -> {
+        int firstArgumentIndex = method.getFirstNonReceiverArgumentIndex();
+        builder.removeParameterAnnotations(
+            oldIndex -> getArgumentInfo(oldIndex + firstArgumentIndex).isRemovedArgumentInfo());
+      };
+    }
+    return ConsumerUtils.emptyConsumer();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/proto/ArgumentPermutation.java b/src/main/java/com/android/tools/r8/graph/proto/ArgumentPermutation.java
new file mode 100644
index 0000000..6dc9ddc
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/proto/ArgumentPermutation.java
@@ -0,0 +1,57 @@
+// 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.graph.proto;
+
+import it.unimi.dsi.fastutil.ints.Int2IntArrayMap;
+import it.unimi.dsi.fastutil.ints.Int2IntMap;
+import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
+
+public abstract class ArgumentPermutation {
+
+  public static Builder builder(int size) {
+    return new Builder(size);
+  }
+
+  public static DefaultArgumentPermutation getDefault() {
+    return DefaultArgumentPermutation.get();
+  }
+
+  public abstract int getNewArgumentIndex(int argumentIndex);
+
+  public boolean isDefault() {
+    return false;
+  }
+
+  public static class Builder {
+
+    private final Int2IntMap newArgumentIndices;
+
+    private Builder(int size) {
+      Int2IntMap newArgumentIndices = size <= 30 ? new Int2IntArrayMap() : new Int2IntOpenHashMap();
+      newArgumentIndices.defaultReturnValue(-1);
+      this.newArgumentIndices = newArgumentIndices;
+    }
+
+    public boolean isDefault() {
+      return newArgumentIndices.isEmpty();
+    }
+
+    public Builder setNewArgumentIndex(int argumentIndex, int newArgumentIndex) {
+      if (argumentIndex != newArgumentIndex) {
+        newArgumentIndices.put(argumentIndex, newArgumentIndex);
+      } else {
+        newArgumentIndices.remove(argumentIndex);
+      }
+      return this;
+    }
+
+    public ArgumentPermutation build() {
+      if (isDefault()) {
+        return getDefault();
+      }
+      return new ArgumentPermutationMap(newArgumentIndices);
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/proto/ArgumentPermutationMap.java b/src/main/java/com/android/tools/r8/graph/proto/ArgumentPermutationMap.java
new file mode 100644
index 0000000..15ddfd2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/proto/ArgumentPermutationMap.java
@@ -0,0 +1,23 @@
+// 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.graph.proto;
+
+import it.unimi.dsi.fastutil.ints.Int2IntMap;
+
+public class ArgumentPermutationMap extends ArgumentPermutation {
+
+  private final Int2IntMap newArgumentIndices;
+
+  public ArgumentPermutationMap(Int2IntMap newArgumentIndices) {
+    assert newArgumentIndices.defaultReturnValue() == -1;
+    this.newArgumentIndices = newArgumentIndices;
+  }
+
+  @Override
+  public int getNewArgumentIndex(int argumentIndex) {
+    int newArgumentIndex = newArgumentIndices.get(argumentIndex);
+    return newArgumentIndex >= 0 ? newArgumentIndex : argumentIndex;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/proto/DefaultArgumentPermutation.java b/src/main/java/com/android/tools/r8/graph/proto/DefaultArgumentPermutation.java
new file mode 100644
index 0000000..29e3799
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/proto/DefaultArgumentPermutation.java
@@ -0,0 +1,26 @@
+// 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.graph.proto;
+
+public class DefaultArgumentPermutation extends ArgumentPermutation {
+
+  private static final DefaultArgumentPermutation INSTANCE = new DefaultArgumentPermutation();
+
+  private DefaultArgumentPermutation() {}
+
+  public static DefaultArgumentPermutation get() {
+    return INSTANCE;
+  }
+
+  @Override
+  public int getNewArgumentIndex(int argumentIndex) {
+    return argumentIndex;
+  }
+
+  @Override
+  public boolean isDefault() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/proto/RemovedArgumentInfo.java b/src/main/java/com/android/tools/r8/graph/proto/RemovedArgumentInfo.java
new file mode 100644
index 0000000..953cedd
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/proto/RemovedArgumentInfo.java
@@ -0,0 +1,116 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.graph.proto;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.ir.analysis.value.SingleValue;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import java.util.Objects;
+
+public class RemovedArgumentInfo extends ArgumentInfo {
+
+  public static class Builder {
+
+    private boolean checkNullOrZero;
+    private SingleValue singleValue;
+    private DexType type;
+
+    public Builder setCheckNullOrZero(boolean checkNullOrZero) {
+      this.checkNullOrZero = checkNullOrZero;
+      return this;
+    }
+
+    public Builder setSingleValue(SingleValue singleValue) {
+      this.singleValue = singleValue;
+      return this;
+    }
+
+    public Builder setType(DexType type) {
+      this.type = type;
+      return this;
+    }
+
+    public RemovedArgumentInfo build() {
+      assert type != null;
+      return new RemovedArgumentInfo(checkNullOrZero, singleValue, type);
+    }
+  }
+
+  private final boolean checkNullOrZero;
+  private final SingleValue singleValue;
+  private final DexType type;
+
+  private RemovedArgumentInfo(boolean checkNullOrZero, SingleValue singleValue, DexType type) {
+    this.checkNullOrZero = checkNullOrZero;
+    this.singleValue = singleValue;
+    this.type = type;
+  }
+
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  public boolean hasSingleValue() {
+    return singleValue != null;
+  }
+
+  public SingleValue getSingleValue() {
+    return singleValue;
+  }
+
+  public DexType getType() {
+    return type;
+  }
+
+  public boolean isCheckNullOrZeroSet() {
+    return checkNullOrZero;
+  }
+
+  @Override
+  public boolean isRemovedArgumentInfo() {
+    return true;
+  }
+
+  @Override
+  public RemovedArgumentInfo asRemovedArgumentInfo() {
+    return this;
+  }
+
+  @Override
+  public ArgumentInfo combine(ArgumentInfo info) {
+    assert false : "Once the argument is removed one cannot modify it any further.";
+    return this;
+  }
+
+  @Override
+  public RemovedArgumentInfo rewrittenWithLens(
+      AppView<AppInfoWithLiveness> appView, GraphLens graphLens, GraphLens codeLens) {
+    SingleValue rewrittenSingleValue =
+        hasSingleValue() ? singleValue.rewrittenWithLens(appView, graphLens, codeLens) : null;
+    DexType rewrittenType = graphLens.lookupType(type, codeLens);
+    if (rewrittenSingleValue != singleValue || rewrittenType != type) {
+      return new RemovedArgumentInfo(checkNullOrZero, rewrittenSingleValue, rewrittenType);
+    }
+    return this;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj == null || getClass() != obj.getClass()) {
+      return false;
+    }
+    RemovedArgumentInfo other = (RemovedArgumentInfo) obj;
+    return checkNullOrZero == other.checkNullOrZero
+        && type == other.type
+        && Objects.equals(singleValue, other.singleValue);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(checkNullOrZero, singleValue, type);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/proto/RewrittenPrototypeDescription.java b/src/main/java/com/android/tools/r8/graph/proto/RewrittenPrototypeDescription.java
new file mode 100644
index 0000000..8459d85
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/proto/RewrittenPrototypeDescription.java
@@ -0,0 +1,293 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.graph.proto;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.RewrittenPrototypeDescriptionMethodOptimizationInfoFixer;
+import com.android.tools.r8.ir.analysis.value.SingleValue;
+import com.android.tools.r8.ir.code.ConstInstruction;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.Position;
+import com.android.tools.r8.ir.code.TypeAndLocalInfoSupplier;
+import com.android.tools.r8.ir.conversion.ExtraParameter;
+import com.android.tools.r8.ir.conversion.ExtraUnusedNullParameter;
+import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfoFixer;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Consumer;
+
+public class RewrittenPrototypeDescription {
+
+  private static final RewrittenPrototypeDescription NONE = new RewrittenPrototypeDescription();
+
+  private final List<ExtraParameter> extraParameters;
+  private final ArgumentInfoCollection argumentInfoCollection;
+  private final RewrittenTypeInfo rewrittenReturnInfo;
+
+  private RewrittenPrototypeDescription() {
+    this.extraParameters = Collections.emptyList();
+    this.rewrittenReturnInfo = null;
+    this.argumentInfoCollection = ArgumentInfoCollection.empty();
+  }
+
+  private RewrittenPrototypeDescription(
+      List<ExtraParameter> extraParameters,
+      RewrittenTypeInfo rewrittenReturnInfo,
+      ArgumentInfoCollection argumentsInfo) {
+    assert argumentsInfo != null;
+    this.extraParameters = extraParameters;
+    this.rewrittenReturnInfo = rewrittenReturnInfo;
+    this.argumentInfoCollection = argumentsInfo;
+    assert !isEmpty();
+  }
+
+  public static RewrittenPrototypeDescription create(
+      List<ExtraParameter> extraParameters,
+      RewrittenTypeInfo rewrittenReturnInfo,
+      ArgumentInfoCollection argumentsInfo) {
+    return extraParameters.isEmpty() && rewrittenReturnInfo == null && argumentsInfo.isEmpty()
+        ? none()
+        : new RewrittenPrototypeDescription(extraParameters, rewrittenReturnInfo, argumentsInfo);
+  }
+
+  public static RewrittenPrototypeDescription createForRewrittenTypes(
+      RewrittenTypeInfo returnInfo, ArgumentInfoCollection rewrittenArgumentsInfo) {
+    return create(Collections.emptyList(), returnInfo, rewrittenArgumentsInfo);
+  }
+
+  public static RewrittenPrototypeDescription none() {
+    return NONE;
+  }
+
+  public Consumer<DexEncodedMethod.Builder> createParameterAnnotationsRemover(
+      DexEncodedMethod method) {
+    return getArgumentInfoCollection().createParameterAnnotationsRemover(method);
+  }
+
+  public MethodOptimizationInfoFixer createMethodOptimizationInfoFixer() {
+    return new RewrittenPrototypeDescriptionMethodOptimizationInfoFixer(this);
+  }
+
+  public RewrittenPrototypeDescription combine(RewrittenPrototypeDescription other) {
+    if (isEmpty()) {
+      return other;
+    }
+    if (other.isEmpty()) {
+      return this;
+    }
+    // We currently don't have any passes that remove extra parameters inserted by previous passes.
+    // If the input prototype changes have removed some of the extra parameters, we would need to
+    // adapt the merging of prototype changes below.
+    List<ExtraParameter> newExtraParameters =
+        ImmutableList.<ExtraParameter>builder()
+            .addAll(getExtraParameters())
+            .addAll(other.getExtraParameters())
+            .build();
+    RewrittenTypeInfo newRewrittenTypeInfo =
+        hasRewrittenReturnInfo()
+            ? getRewrittenReturnInfo().combine(other)
+            : other.getRewrittenReturnInfo();
+    ArgumentInfoCollection newArgumentInfoCollection =
+        getArgumentInfoCollection().combine(other.getArgumentInfoCollection());
+    return new RewrittenPrototypeDescription(
+        newExtraParameters, newRewrittenTypeInfo, newArgumentInfoCollection);
+  }
+
+  public boolean isEmpty() {
+    return extraParameters.isEmpty()
+        && rewrittenReturnInfo == null
+        && argumentInfoCollection.isEmpty();
+  }
+
+  public boolean hasExtraParameters() {
+    return !extraParameters.isEmpty();
+  }
+
+  public List<ExtraParameter> getExtraParameters() {
+    return extraParameters;
+  }
+
+  public int numberOfExtraParameters() {
+    return extraParameters.size();
+  }
+
+  public boolean hasBeenChangedToReturnVoid() {
+    return rewrittenReturnInfo != null && rewrittenReturnInfo.hasBeenChangedToReturnVoid();
+  }
+
+  public ArgumentInfoCollection getArgumentInfoCollection() {
+    return argumentInfoCollection;
+  }
+
+  public boolean hasRewrittenReturnInfo() {
+    return rewrittenReturnInfo != null;
+  }
+
+  public boolean requiresRewritingAtCallSite() {
+    return hasRewrittenReturnInfo()
+        || numberOfExtraParameters() > 0
+        || argumentInfoCollection.hasArgumentPermutation()
+        || argumentInfoCollection.numberOfRemovedArguments() > 0;
+  }
+
+  public RewrittenTypeInfo getRewrittenReturnInfo() {
+    return rewrittenReturnInfo;
+  }
+
+  /**
+   * Returns the {@link ConstInstruction} that should be used to materialize the result of
+   * invocations to the method represented by this {@link RewrittenPrototypeDescription}.
+   *
+   * <p>This method should only be used for methods that return a constant value and whose return
+   * type has been changed to void.
+   *
+   * <p>Note that the current implementation always returns null at this point.
+   */
+  public Instruction getConstantReturn(
+      AppView<AppInfoWithLiveness> appView,
+      IRCode code,
+      Position position,
+      TypeAndLocalInfoSupplier info) {
+    assert rewrittenReturnInfo != null;
+    assert rewrittenReturnInfo.hasSingleValue();
+    Instruction instruction =
+        rewrittenReturnInfo.getSingleValue().createMaterializingInstruction(appView, code, info);
+    instruction.setPosition(position);
+    return instruction;
+  }
+
+  public boolean verifyConstantReturnAccessibleInContext(
+      AppView<AppInfoWithLiveness> appView, ProgramMethod method, GraphLens codeLens) {
+    SingleValue rewrittenSingleValue =
+        rewrittenReturnInfo
+            .getSingleValue()
+            .rewrittenWithLens(appView, appView.graphLens(), codeLens);
+    assert rewrittenSingleValue.isMaterializableInContext(appView, method);
+    return true;
+  }
+
+  public DexMethod rewriteMethod(ProgramMethod method, DexItemFactory dexItemFactory) {
+    if (isEmpty()) {
+      return method.getReference();
+    }
+    DexProto rewrittenProto = rewriteProto(method, dexItemFactory);
+    return method.getReference().withProto(rewrittenProto, dexItemFactory);
+  }
+
+  public DexProto rewriteProto(ProgramMethod method, DexItemFactory dexItemFactory) {
+    if (isEmpty()) {
+      return method.getProto();
+    }
+    DexType newReturnType =
+        rewrittenReturnInfo != null ? rewrittenReturnInfo.getNewType() : method.getReturnType();
+    DexType[] newParameters = rewriteParameters(method, dexItemFactory);
+    return dexItemFactory.createProto(newReturnType, newParameters);
+  }
+
+  public DexType[] rewriteParameters(ProgramMethod method, DexItemFactory dexItemFactory) {
+    DexType[] params = method.getParameters().values;
+    if (isEmpty()) {
+      return params;
+    }
+    DexType[] newParams =
+        new DexType
+            [params.length
+                - argumentInfoCollection.numberOfRemovedNonReceiverArguments(method)
+                + extraParameters.size()];
+    int offset = method.getDefinition().getFirstNonReceiverArgumentIndex();
+    int newParamIndex = 0;
+    for (int oldParamIndex = 0; oldParamIndex < params.length; oldParamIndex++) {
+      ArgumentInfo argInfo = argumentInfoCollection.getArgumentInfo(oldParamIndex + offset);
+      if (argInfo.isNone()) {
+        newParams[newParamIndex++] = params[oldParamIndex];
+      } else if (argInfo.isRewrittenTypeInfo()) {
+        RewrittenTypeInfo rewrittenTypeInfo = argInfo.asRewrittenTypeInfo();
+        assert params[oldParamIndex] == rewrittenTypeInfo.getOldType();
+        newParams[newParamIndex++] = rewrittenTypeInfo.getNewType();
+      }
+    }
+    for (ExtraParameter extraParameter : extraParameters) {
+      newParams[newParamIndex++] = extraParameter.getType(dexItemFactory);
+    }
+    return newParams;
+  }
+
+  public RewrittenPrototypeDescription rewrittenWithLens(
+      AppView<AppInfoWithLiveness> appView, GraphLens graphLens, GraphLens codeLens) {
+    ArgumentInfoCollection newArgumentInfoCollection =
+        argumentInfoCollection.rewrittenWithLens(appView, graphLens, codeLens);
+    RewrittenTypeInfo newRewrittenReturnInfo =
+        hasRewrittenReturnInfo()
+            ? rewrittenReturnInfo.rewrittenWithLens(appView, graphLens, codeLens)
+            : null;
+    if (newArgumentInfoCollection != argumentInfoCollection
+        || newRewrittenReturnInfo != rewrittenReturnInfo) {
+      return new RewrittenPrototypeDescription(
+          extraParameters, newRewrittenReturnInfo, newArgumentInfoCollection);
+    }
+    return this;
+  }
+
+  public RewrittenPrototypeDescription withRewrittenReturnInfo(
+      RewrittenTypeInfo newRewrittenReturnInfo) {
+    if (Objects.equals(rewrittenReturnInfo, newRewrittenReturnInfo)) {
+      return this;
+    }
+    return new RewrittenPrototypeDescription(
+        extraParameters, newRewrittenReturnInfo, argumentInfoCollection);
+  }
+
+  public RewrittenPrototypeDescription withExtraUnusedNullParameters(
+      int numberOfExtraUnusedNullParameters) {
+    List<ExtraParameter> parameters =
+        Collections.nCopies(numberOfExtraUnusedNullParameters, new ExtraUnusedNullParameter());
+    return withExtraParameters(parameters);
+  }
+
+  public RewrittenPrototypeDescription withExtraParameters(ExtraParameter... parameters) {
+    return withExtraParameters(Arrays.asList(parameters));
+  }
+
+  public RewrittenPrototypeDescription withExtraParameters(List<ExtraParameter> parameters) {
+    if (parameters.isEmpty()) {
+      return this;
+    }
+    List<ExtraParameter> newExtraParameters =
+        new ArrayList<>(extraParameters.size() + parameters.size());
+    newExtraParameters.addAll(extraParameters);
+    newExtraParameters.addAll(parameters);
+    return new RewrittenPrototypeDescription(
+        newExtraParameters, rewrittenReturnInfo, argumentInfoCollection);
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj == null || getClass() != obj.getClass()) {
+      return false;
+    }
+    RewrittenPrototypeDescription other = (RewrittenPrototypeDescription) obj;
+    return extraParameters.equals(other.extraParameters)
+        && Objects.equals(rewrittenReturnInfo, other.rewrittenReturnInfo)
+        && argumentInfoCollection.equals(other.argumentInfoCollection);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(extraParameters, rewrittenReturnInfo, argumentInfoCollection);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/proto/RewrittenTypeInfo.java b/src/main/java/com/android/tools/r8/graph/proto/RewrittenTypeInfo.java
new file mode 100644
index 0000000..0f0c49f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/proto/RewrittenTypeInfo.java
@@ -0,0 +1,163 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.graph.proto;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.ir.analysis.value.SingleValue;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import java.util.Objects;
+import java.util.function.Consumer;
+
+public 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();
+  }
+
+  private RewrittenTypeInfo(
+      DexType oldType, DexType newType, DexType castType, SingleValue singleValue) {
+    this.castType = castType;
+    this.oldType = oldType;
+    this.newType = newType;
+    this.singleValue = singleValue;
+  }
+
+  public RewrittenTypeInfo combine(RewrittenPrototypeDescription other) {
+    return other.hasRewrittenReturnInfo() ? combine(other.getRewrittenReturnInfo()) : this;
+  }
+
+  public DexType getCastType() {
+    return castType;
+  }
+
+  public DexType getNewType() {
+    return newType;
+  }
+
+  public DexType getOldType() {
+    return oldType;
+  }
+
+  public SingleValue getSingleValue() {
+    return singleValue;
+  }
+
+  boolean hasBeenChangedToReturnVoid() {
+    return newType.isVoidType();
+  }
+
+  public boolean hasCastType() {
+    return castType != null;
+  }
+
+  public boolean hasSingleValue() {
+    return singleValue != null;
+  }
+
+  @Override
+  public boolean isRewrittenTypeInfo() {
+    return true;
+  }
+
+  @Override
+  public RewrittenTypeInfo asRewrittenTypeInfo() {
+    return this;
+  }
+
+  @Override
+  public ArgumentInfo combine(ArgumentInfo info) {
+    if (info.isRemovedArgumentInfo()) {
+      return info;
+    }
+    assert info.isRewrittenTypeInfo();
+    return combine(info.asRewrittenTypeInfo());
+  }
+
+  public RewrittenTypeInfo combine(RewrittenTypeInfo other) {
+    assert !getNewType().isVoidType();
+    assert getNewType() == other.getOldType();
+    return new RewrittenTypeInfo(
+        getOldType(), other.getNewType(), getCastType(), other.getSingleValue());
+  }
+
+  @Override
+  public RewrittenTypeInfo rewrittenWithLens(
+      AppView<AppInfoWithLiveness> appView, GraphLens graphLens, GraphLens codeLens) {
+    DexType rewrittenCastType = castType != null ? graphLens.lookupType(castType, codeLens) : null;
+    DexType rewrittenNewType = graphLens.lookupType(newType, codeLens);
+    SingleValue rewrittenSingleValue =
+        hasSingleValue() ? getSingleValue().rewrittenWithLens(appView, graphLens, codeLens) : null;
+    if (rewrittenCastType != castType
+        || rewrittenNewType != newType
+        || rewrittenSingleValue != singleValue) {
+      // The old type is intentionally not rewritten.
+      return new RewrittenTypeInfo(
+          oldType, rewrittenNewType, rewrittenCastType, rewrittenSingleValue);
+    }
+    return this;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj == null || getClass() != obj.getClass()) {
+      return false;
+    }
+    RewrittenTypeInfo other = (RewrittenTypeInfo) obj;
+    return oldType == other.oldType
+        && newType == other.newType
+        && Objects.equals(singleValue, other.singleValue);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(oldType, newType, singleValue);
+  }
+
+  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);
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ClassInitializerMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ClassInitializerMerger.java
index bb8f046..fcae6d5 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ClassInitializerMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ClassInitializerMerger.java
@@ -22,8 +22,8 @@
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription;
 import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
 import com.android.tools.r8.horizontalclassmerging.MergeGroup;
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.IRCode;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java
index f13bd76..ef8d041 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java
@@ -178,7 +178,7 @@
         executorService);
     processFieldsNeverRead(appInfo);
     processFieldsNeverWritten(appInfo);
-    postMethodProcessorBuilder.put(methodsToReprocess);
+    postMethodProcessorBuilder.rewrittenWithLens(appView.graphLens()).put(methodsToReprocess);
   }
 
   private void processClass(DexProgramClass clazz) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/AbstractFieldSet.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/AbstractFieldSet.java
index 58033b1..ab29351 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/AbstractFieldSet.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/AbstractFieldSet.java
@@ -8,7 +8,7 @@
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.PrunedItems;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
+import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 
 /**
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/ConcreteMutableFieldSet.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/ConcreteMutableFieldSet.java
index 84208bb..d62a1d6 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/ConcreteMutableFieldSet.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/ConcreteMutableFieldSet.java
@@ -10,7 +10,7 @@
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.PrunedItems;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
+import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.SetUtils;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/EmptyFieldSet.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/EmptyFieldSet.java
index 65e424f..9d2e36a 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/EmptyFieldSet.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/EmptyFieldSet.java
@@ -8,8 +8,8 @@
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.PrunedItems;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.RemovedArgumentInfo;
+import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
+import com.android.tools.r8.graph.proto.RemovedArgumentInfo;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 
 public class EmptyFieldSet extends AbstractFieldSet implements KnownFieldSet {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/UnknownFieldSet.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/UnknownFieldSet.java
index c9dcd7a..43823c3 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/UnknownFieldSet.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/UnknownFieldSet.java
@@ -8,7 +8,7 @@
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.PrunedItems;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
+import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 
 public class UnknownFieldSet extends AbstractFieldSet {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/inlining/AlwaysSimpleInliningConstraint.java b/src/main/java/com/android/tools/r8/ir/analysis/inlining/AlwaysSimpleInliningConstraint.java
index 9180568..730d980 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/inlining/AlwaysSimpleInliningConstraint.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/inlining/AlwaysSimpleInliningConstraint.java
@@ -5,7 +5,7 @@
 package com.android.tools.r8.ir.analysis.inlining;
 
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
+import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
 import com.android.tools.r8.ir.code.InvokeMethod;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/inlining/EqualToBooleanSimpleInliningConstraint.java b/src/main/java/com/android/tools/r8/ir/analysis/inlining/EqualToBooleanSimpleInliningConstraint.java
index 9a400ae..c83e52e 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/inlining/EqualToBooleanSimpleInliningConstraint.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/inlining/EqualToBooleanSimpleInliningConstraint.java
@@ -5,8 +5,8 @@
 package com.android.tools.r8.ir.analysis.inlining;
 
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.RemovedArgumentInfo;
+import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
+import com.android.tools.r8.graph.proto.RemovedArgumentInfo;
 import com.android.tools.r8.ir.analysis.value.SingleValue;
 import com.android.tools.r8.ir.code.Instruction;
 import com.android.tools.r8.ir.code.InvokeMethod;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/inlining/NeverSimpleInliningConstraint.java b/src/main/java/com/android/tools/r8/ir/analysis/inlining/NeverSimpleInliningConstraint.java
index d786e67..15f3051 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/inlining/NeverSimpleInliningConstraint.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/inlining/NeverSimpleInliningConstraint.java
@@ -5,7 +5,7 @@
 package com.android.tools.r8.ir.analysis.inlining;
 
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
+import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
 import com.android.tools.r8.ir.code.InvokeMethod;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 
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 10af841..2c0448a 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
@@ -5,10 +5,10 @@
 package com.android.tools.r8.ir.analysis.inlining;
 
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfo;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.RemovedArgumentInfo;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.RewrittenTypeInfo;
+import com.android.tools.r8.graph.proto.ArgumentInfo;
+import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
+import com.android.tools.r8.graph.proto.RemovedArgumentInfo;
+import com.android.tools.r8.graph.proto.RewrittenTypeInfo;
 import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.code.Instruction;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/inlining/NumberSimpleInliningConstraint.java b/src/main/java/com/android/tools/r8/ir/analysis/inlining/NumberSimpleInliningConstraint.java
index 23b6233..629ed68 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/inlining/NumberSimpleInliningConstraint.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/inlining/NumberSimpleInliningConstraint.java
@@ -5,8 +5,8 @@
 package com.android.tools.r8.ir.analysis.inlining;
 
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.RemovedArgumentInfo;
+import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
+import com.android.tools.r8.graph.proto.RemovedArgumentInfo;
 import com.android.tools.r8.ir.analysis.value.SingleValue;
 import com.android.tools.r8.ir.code.Instruction;
 import com.android.tools.r8.ir.code.InvokeMethod;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraint.java b/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraint.java
index 64e6f44..a207c8d 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraint.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraint.java
@@ -5,7 +5,7 @@
 package com.android.tools.r8.ir.analysis.inlining;
 
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
+import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
 import com.android.tools.r8.ir.code.InvokeMethod;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.google.common.collect.ImmutableList;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintConjunction.java b/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintConjunction.java
index 55da1c0..6147cd8 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintConjunction.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintConjunction.java
@@ -5,7 +5,7 @@
 package com.android.tools.r8.ir.analysis.inlining;
 
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
+import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
 import com.android.tools.r8.ir.code.InvokeMethod;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.ListUtils;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintDisjunction.java b/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintDisjunction.java
index 0ac0ced..02c0b01 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintDisjunction.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintDisjunction.java
@@ -5,7 +5,7 @@
 package com.android.tools.r8.ir.analysis.inlining;
 
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
+import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
 import com.android.tools.r8.ir.code.InvokeMethod;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.ListUtils;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleConstClassValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleConstClassValue.java
index ad8b4d5..d090011 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleConstClassValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleConstClassValue.java
@@ -16,7 +16,7 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
+import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.code.ConstClass;
 import com.android.tools.r8.ir.code.Instruction;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleDexItemBasedStringValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleDexItemBasedStringValue.java
index 4f4f7cd..8a69170 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleDexItemBasedStringValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleDexItemBasedStringValue.java
@@ -13,7 +13,7 @@
 import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
+import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.code.DexItemBasedConstString;
 import com.android.tools.r8.ir.code.Instruction;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java
index cbc7e66..ebbfffe 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java
@@ -17,7 +17,7 @@
 import com.android.tools.r8.graph.FieldResolutionResult.SuccessfulFieldResolutionResult;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
+import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
 import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.analysis.value.objectstate.ObjectState;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleNumberValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleNumberValue.java
index b3259b0..c9b7f62 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleNumberValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleNumberValue.java
@@ -9,7 +9,7 @@
 import com.android.tools.r8.graph.DebugLocalInfo;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
+import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.code.ConstNumber;
 import com.android.tools.r8.ir.code.Instruction;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleStringValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleStringValue.java
index 57cd2e1..6e69356 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleStringValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleStringValue.java
@@ -13,7 +13,7 @@
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
+import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.code.ConstString;
 import com.android.tools.r8.ir.code.Instruction;
diff --git a/src/main/java/com/android/tools/r8/ir/code/If.java b/src/main/java/com/android/tools/r8/ir/code/If.java
index ce75f73..9269104 100644
--- a/src/main/java/com/android/tools/r8/ir/code/If.java
+++ b/src/main/java/com/android/tools/r8/ir/code/If.java
@@ -13,6 +13,7 @@
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.conversion.CfBuilder;
 import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.CfgPrinter;
 import com.android.tools.r8.utils.InternalOutputMode;
 import java.util.List;
@@ -205,6 +206,19 @@
         && o.type == type;
   }
 
+  public BasicBlock targetFromTrue() {
+    return targetFromBoolean(true);
+  }
+
+  public BasicBlock targetFromFalse() {
+    return targetFromBoolean(false);
+  }
+
+  public BasicBlock targetFromBoolean(boolean cond) {
+    assert isZeroTest();
+    return targetFromCondition(BooleanUtils.intValue(cond));
+  }
+
   public BasicBlock targetFromCondition(ConstNumber value) {
     assert isZeroTest();
     assert verifyTypeCompatible(value.getOutType(), type);
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 133023f..29bee42 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
@@ -36,11 +36,11 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfo;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.RemovedArgumentInfo;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.RewrittenTypeInfo;
+import com.android.tools.r8.graph.proto.ArgumentInfo;
+import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
+import com.android.tools.r8.graph.proto.RemovedArgumentInfo;
+import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
+import com.android.tools.r8.graph.proto.RewrittenTypeInfo;
 import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.PrimitiveTypeElement;
 import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
@@ -543,6 +543,7 @@
     int argumentIndex = 0;
 
     if (!method.isStatic()) {
+      assert argumentsInfo.getNewArgumentIndex(0) == 0;
       writeCallback.accept(register, method.getHolderType());
       addThisArgument(register);
       argumentIndex++;
@@ -550,12 +551,12 @@
     }
 
     int originalNumberOfArguments =
-        method.getReference().proto.parameters.values.length
+        method.getParameters().size()
             + argumentsInfo.numberOfRemovedArguments()
             + method.getFirstNonReceiverArgumentIndex()
             - prototypeChanges.numberOfExtraParameters();
 
-    int usedArgumentIndex = 0;
+    int numberOfRemovedArguments = 0;
     while (argumentIndex < originalNumberOfArguments) {
       TypeElement type;
       ArgumentInfo argumentInfo = argumentsInfo.getArgumentInfo(argumentIndex);
@@ -566,20 +567,21 @@
             TypeElement.fromDexType(
                 removedArgumentInfo.getType(), Nullability.maybeNull(), appView);
         addNonThisArgument(register, type);
+        numberOfRemovedArguments++;
       } else {
         DexType argType;
+        int newArgumentIndex =
+            argumentsInfo.getNewArgumentIndex(argumentIndex, numberOfRemovedArguments);
         if (argumentInfo.isRewrittenTypeInfo()) {
           RewrittenTypeInfo argumentRewrittenTypeInfo = argumentInfo.asRewrittenTypeInfo();
-          assert method.getReference().proto.getParameter(usedArgumentIndex)
-              == argumentRewrittenTypeInfo.getNewType();
+          assert method.getArgumentType(newArgumentIndex) == argumentRewrittenTypeInfo.getNewType();
           // The old type is used to prevent that a changed value from reference to primitive
           // type breaks IR building. Rewriting from the old to the new type will be done in the
           // IRConverter (typically through the lensCodeRewriter).
           argType = argumentRewrittenTypeInfo.getOldType();
         } else {
-          argType = method.getReference().proto.getParameter(usedArgumentIndex);
+          argType = method.getArgumentType(newArgumentIndex);
         }
-        usedArgumentIndex++;
         writeCallback.accept(register, argType);
         type = TypeElement.fromDexType(argType, Nullability.maybeNull(), appView);
         if (argType.isBooleanType()) {
@@ -588,20 +590,21 @@
           addNonThisArgument(register, type);
         }
       }
-      register += type.requiredRegisters();
       argumentIndex++;
+      register += type.requiredRegisters();
     }
 
     for (ExtraParameter extraParameter : prototypeChanges.getExtraParameters()) {
-      DexType argType = method.getReference().proto.getParameter(usedArgumentIndex);
-      TypeElement type = extraParameter.getTypeElement(appView, argType);
-      register += type.requiredRegisters();
-      usedArgumentIndex++;
+      int newArgumentIndex =
+          argumentsInfo.getNewArgumentIndex(argumentIndex, numberOfRemovedArguments);
+      DexType extraArgumentType = method.getArgumentType(newArgumentIndex);
       if (extraParameter instanceof ExtraUnusedNullParameter) {
-        addExtraUnusedArgument(register, argType);
+        addExtraUnusedArgument(extraArgumentType);
       } else {
-        addNonThisArgument(register, type);
+        addNonThisArgument(register, extraParameter.getTypeElement(appView, extraArgumentType));
       }
+      argumentIndex++;
+      register += extraArgumentType.getRequiredRegisters();
     }
   }
 
@@ -988,7 +991,7 @@
     value.markAsThis();
   }
 
-  private void addExtraUnusedArgument(int register, DexType type) {
+  private void addExtraUnusedArgument(DexType type) {
     // Extra unused null arguments should bypass the register check, they may use registers
     // beyond the limit of what the method can use. They don't have debug information and are
     // always null.
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 efd8593..145ffec 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
@@ -690,6 +690,9 @@
 
     // Analyze the data collected by the argument propagator, use the analysis result to update
     // the parameter optimization infos, and rewrite the application.
+    // TODO(b/199237357): Automatically rewrite state when lens changes.
+    enumUnboxer.rewriteWithLens();
+    outliner.rewriteWithLens();
     appView.withArgumentPropagator(
         argumentPropagator ->
             argumentPropagator.tearDownCodeScanner(
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 8bcdea8..ba22a02 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
@@ -4,7 +4,6 @@
 package com.android.tools.r8.ir.conversion;
 
 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;
@@ -53,12 +52,12 @@
 import com.android.tools.r8.graph.GraphLens.MethodLookupResult;
 import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
 import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfo;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.RemovedArgumentInfo;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.RewrittenTypeInfo;
 import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
+import com.android.tools.r8.graph.proto.ArgumentInfo;
+import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
+import com.android.tools.r8.graph.proto.RemovedArgumentInfo;
+import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
+import com.android.tools.r8.graph.proto.RewrittenTypeInfo;
 import com.android.tools.r8.ir.analysis.type.DestructivePhiTypeUpdater;
 import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
@@ -101,7 +100,6 @@
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.ir.code.ValueType;
 import com.android.tools.r8.ir.optimize.enums.EnumUnboxer;
-import com.android.tools.r8.logging.Log;
 import com.android.tools.r8.optimize.MemberRebindingAnalysis;
 import com.android.tools.r8.optimize.argumentpropagation.lenscoderewriter.NullCheckInserter;
 import com.android.tools.r8.utils.InternalOptions;
@@ -110,10 +108,13 @@
 import com.google.common.collect.Sets;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Deque;
 import java.util.HashSet;
 import java.util.IdentityHashMap;
+import java.util.LinkedList;
 import java.util.List;
+import java.util.ListIterator;
 import java.util.Map;
 import java.util.Set;
 import java.util.function.BiFunction;
@@ -337,6 +338,8 @@
                       invokedMethod, method.getReference(), invoke.getType(), codeLens);
               DexMethod actualTarget = lensLookup.getReference();
               Invoke.Type actualInvokeType = lensLookup.getType();
+              int numberOfArguments =
+                  actualTarget.getNumberOfArguments(actualInvokeType.isStatic());
 
               iterator =
                   insertCastsForInvokeArgumentsIfNeeded(code, blocks, iterator, invoke, lensLookup);
@@ -350,74 +353,81 @@
                     prototypeChanges.getArgumentInfoCollection();
                 if (argumentInfoCollection.isEmpty()) {
                   if (prototypeChanges.hasExtraParameters()) {
-                    newInValues = new ArrayList<>(invoke.inValues());
+                    newInValues = new ArrayList<>(numberOfArguments);
+                    newInValues.addAll(invoke.arguments());
+                    prototypeChanges.getExtraParameters().forEach(ignore -> newInValues.add(null));
                   } else {
-                    newInValues = invoke.inValues();
+                    newInValues = invoke.arguments();
                   }
                 } else {
-                  if (argumentInfoCollection.hasRemovedArguments()) {
-                    if (Log.ENABLED) {
-                      Log.info(
-                          getClass(),
-                          "Invoked method "
-                              + invokedMethod.toSourceString()
-                              + " with "
-                              + argumentInfoCollection.numberOfRemovedArguments()
-                              + " arguments removed");
+                  newInValues = Arrays.asList(new Value[numberOfArguments]);
+                  int numberOfRemovedArguments = 0;
+                  for (int argumentIndex = 0;
+                      argumentIndex < invoke.arguments().size();
+                      argumentIndex++) {
+                    ArgumentInfo argumentInfo =
+                        argumentInfoCollection.getArgumentInfo(argumentIndex);
+                    if (argumentInfo.isRemovedArgumentInfo()) {
+                      numberOfRemovedArguments++;
+                      continue;
                     }
-                  }
-                  newInValues = new ArrayList<>(actualTarget.proto.parameters.size());
-                  for (int i = 0; i < invoke.inValues().size(); i++) {
-                    ArgumentInfo argumentInfo = argumentInfoCollection.getArgumentInfo(i);
+                    int newArgumentIndex =
+                        argumentInfoCollection.getNewArgumentIndex(
+                            argumentIndex, numberOfRemovedArguments);
+                    Value newArgument;
                     if (argumentInfo.isRewrittenTypeInfo()) {
                       RewrittenTypeInfo argInfo = argumentInfo.asRewrittenTypeInfo();
-                      Value rewrittenValue =
+                      newArgument =
                           rewriteValueIfDefault(
                               code,
                               iterator,
                               argInfo.getOldType(),
                               argInfo.getNewType(),
-                              invoke.inValues().get(i));
-                      newInValues.add(rewrittenValue);
-                    } else if (!argumentInfo.isRemovedArgumentInfo()) {
-                      newInValues.add(invoke.inValues().get(i));
+                              invoke.getArgument(argumentIndex));
+                    } else {
+                      newArgument = invoke.getArgument(argumentIndex);
                     }
+                    newInValues.set(newArgumentIndex, newArgument);
                   }
                 }
 
                 Instruction constantReturnMaterializingInstruction = null;
-                if (prototypeChanges.hasBeenChangedToReturnVoid() && invoke.hasOutValue()) {
-                  TypeAndLocalInfoSupplier typeAndLocalInfo =
-                      new TypeAndLocalInfoSupplier() {
-                        @Override
-                        public DebugLocalInfo getLocalInfo() {
-                          return invoke.getLocalInfo();
-                        }
+                if (invoke.hasOutValue()) {
+                  if (invoke.hasUnusedOutValue()) {
+                    invoke.clearOutValue();
+                  } else if (prototypeChanges.hasBeenChangedToReturnVoid()) {
+                    TypeAndLocalInfoSupplier typeAndLocalInfo =
+                        new TypeAndLocalInfoSupplier() {
+                          @Override
+                          public DebugLocalInfo getLocalInfo() {
+                            return invoke.getLocalInfo();
+                          }
 
-                        @Override
-                        public TypeElement getOutType() {
-                          return graphLens
-                              .lookupType(invokedMethod.getReturnType(), codeLens)
-                              .toTypeElement(appView);
-                        }
-                      };
-                  prototypeChanges.verifyConstantReturnAccessibleInContext(
-                      appView.withLiveness(), method, graphLens);
-                  constantReturnMaterializingInstruction =
-                      prototypeChanges.getConstantReturn(
-                          appView.withLiveness(),
-                          code,
-                          invoke.getPosition(),
-                          typeAndLocalInfo);
-                  if (invoke.outValue().hasLocalInfo()) {
-                    constantReturnMaterializingInstruction
+                          @Override
+                          public TypeElement getOutType() {
+                            return graphLens
+                                .lookupType(invokedMethod.getReturnType(), codeLens)
+                                .toTypeElement(appView);
+                          }
+                        };
+                    assert prototypeChanges.verifyConstantReturnAccessibleInContext(
+                        appView.withLiveness(), method, graphLens);
+                    constantReturnMaterializingInstruction =
+                        prototypeChanges.getConstantReturn(
+                            appView.withLiveness(), code, invoke.getPosition(), typeAndLocalInfo);
+                    if (invoke.outValue().hasLocalInfo()) {
+                      constantReturnMaterializingInstruction
+                          .outValue()
+                          .setLocalInfo(invoke.outValue().getLocalInfo());
+                    }
+                    invoke
                         .outValue()
-                        .setLocalInfo(invoke.outValue().getLocalInfo());
-                  }
-                  invoke.outValue().replaceUsers(constantReturnMaterializingInstruction.outValue());
-                  if (invoke.getOutType() != constantReturnMaterializingInstruction.getOutType()) {
-                    affectedPhis.addAll(
-                        constantReturnMaterializingInstruction.outValue().uniquePhiUsers());
+                        .replaceUsers(constantReturnMaterializingInstruction.outValue());
+                    if (invoke.getOutType()
+                        != constantReturnMaterializingInstruction.getOutType()) {
+                      affectedPhis.addAll(
+                          constantReturnMaterializingInstruction.outValue().uniquePhiUsers());
+                    }
                   }
                 }
 
@@ -440,9 +450,14 @@
 
                 Map<SingleNumberValue, Map<DexType, Value>> parameterMap = new IdentityHashMap<>();
 
-                int parameterIndex = newInValues.size() - (actualInvokeType == STATIC ? 0 : 1);
+                int extraArgumentIndex =
+                    numberOfArguments - prototypeChanges.getExtraParameters().size();
                 for (ExtraParameter parameter : prototypeChanges.getExtraParameters()) {
-                  DexType type = actualTarget.proto.getParameter(parameterIndex++);
+                  int newExtraArgumentIndex =
+                      argumentInfoCollection.getNewArgumentIndex(extraArgumentIndex, 0);
+                  DexType extraArgumentType =
+                      actualTarget.getArgumentType(
+                          newExtraArgumentIndex, actualInvokeType.isStatic());
 
                   SingleNumberValue numberValue = parameter.getValue(appView);
 
@@ -452,7 +467,7 @@
                       parameterMap
                           .computeIfAbsent(numberValue, ignore -> new IdentityHashMap<>())
                           .computeIfAbsent(
-                              type,
+                              extraArgumentType,
                               ignore -> {
                                 finalIterator.previous();
                                 Instruction instruction =
@@ -460,7 +475,8 @@
                                         appView,
                                         code,
                                         TypeAndLocalInfoSupplier.create(
-                                            parameter.getTypeElement(appView, type), null));
+                                            parameter.getTypeElement(appView, extraArgumentType),
+                                            null));
                                 assert !instruction.instructionTypeCanThrow();
                                 instruction.setPosition(
                                     options.debug ? invoke.getPosition() : Position.none());
@@ -468,8 +484,7 @@
                                 finalIterator.next();
                                 return instruction.outValue();
                               });
-
-                  newInValues.add(value);
+                  newInValues.set(newExtraArgumentIndex, value);
 
                   // TODO(b/164901008): Fix when the number of arguments overflows.
                   if (newInValues.size() > 255) {
@@ -478,10 +493,9 @@
                             + " the number of arguments of the method "
                             + actualTarget);
                   }
-                }
 
-                assert newInValues.size()
-                    == actualTarget.proto.parameters.size() + (actualInvokeType == STATIC ? 0 : 1);
+                  extraArgumentIndex++;
+                }
 
                 // TODO(b/157111832): This bit should be part of the graph lens lookup result.
                 boolean isInterface =
@@ -809,9 +823,11 @@
       RewrittenPrototypeDescription prototypeChanges,
       Set<Phi> affectedPhis,
       Set<UnusedArgument> unusedArguments) {
-    List<Instruction> argumentPostlude = new ArrayList<>();
+    ArgumentInfoCollection argumentInfoCollection = prototypeChanges.getArgumentInfoCollection();
+    List<Instruction> argumentPostlude = new LinkedList<>();
     int oldArgumentIndex = 0;
-    int newArgumentIndex = 0;
+    int nextArgumentIndex = 0;
+    int numberOfRemovedArguments = 0;
     InstructionListIterator instructionIterator = code.entryBlock().listIterator(code);
     while (instructionIterator.hasNext()) {
       Instruction instruction = instructionIterator.next();
@@ -820,8 +836,7 @@
       }
 
       Argument argument = instruction.asArgument();
-      ArgumentInfo argumentInfo =
-          prototypeChanges.getArgumentInfoCollection().getArgumentInfo(oldArgumentIndex);
+      ArgumentInfo argumentInfo = argumentInfoCollection.getArgumentInfo(oldArgumentIndex);
       if (argumentInfo.isRemovedArgumentInfo()) {
         rewriteRemovedArgument(
             code,
@@ -832,24 +847,51 @@
             affectedPhis,
             argumentPostlude,
             unusedArguments);
+        numberOfRemovedArguments++;
       } else {
+        int newArgumentIndex =
+            argumentInfoCollection.getNewArgumentIndex(oldArgumentIndex, numberOfRemovedArguments);
+        Argument replacement;
         if (argumentInfo.isRewrittenTypeInfo()) {
-          rewriteArgumentType(
-              code,
-              instructionIterator,
-              argument,
-              argumentInfo.asRewrittenTypeInfo(),
-              affectedPhis,
-              newArgumentIndex);
+          replacement =
+              rewriteArgumentType(
+                  code,
+                  argument,
+                  argumentInfo.asRewrittenTypeInfo(),
+                  affectedPhis,
+                  newArgumentIndex);
+          argument.outValue().replaceUsers(replacement.outValue());
         } else if (newArgumentIndex != oldArgumentIndex) {
-          instructionIterator.replaceCurrentInstruction(
+          replacement =
               Argument.builder()
                   .setIndex(newArgumentIndex)
                   .setFreshOutValue(code, argument.getOutType(), argument.getLocalInfo())
                   .setPosition(argument.getPosition())
-                  .build());
+                  .build();
+          argument.outValue().replaceUsers(replacement.outValue());
+        } else {
+          replacement = argument;
         }
-        newArgumentIndex++;
+        if (newArgumentIndex == nextArgumentIndex) {
+          // This is the right position for the argument. Insert it into the code at this position.
+          if (replacement != argument) {
+            instructionIterator.replaceCurrentInstruction(replacement);
+          }
+          nextArgumentIndex++;
+        } else {
+          // Due the a permutation of the argument order, this argument needs to be inserted at a
+          // later point. Enqueue the argument into the argument postlude.
+          instructionIterator.removeInstructionIgnoreOutValue();
+          ListIterator<Instruction> argumentPostludeIterator = argumentPostlude.listIterator();
+          while (argumentPostludeIterator.hasNext()) {
+            Instruction current = argumentPostludeIterator.next();
+            if (!current.isArgument() || replacement.getIndex() < current.asArgument().getIndex()) {
+              argumentPostludeIterator.previous();
+              break;
+            }
+          }
+          argumentPostludeIterator.add(replacement);
+        }
       }
       oldArgumentIndex++;
     }
@@ -896,9 +938,8 @@
     instructionIterator.removeOrReplaceByDebugLocalRead();
   }
 
-  private void rewriteArgumentType(
+  private Argument rewriteArgumentType(
       IRCode code,
-      InstructionListIterator instructionIterator,
       Argument argument,
       RewrittenTypeInfo rewrittenTypeInfo,
       Set<Phi> affectedPhis,
@@ -910,8 +951,8 @@
             .setFreshOutValue(code, rewrittenType, argument.getLocalInfo())
             .setPosition(argument.getPosition())
             .build();
-    instructionIterator.replaceCurrentInstruction(replacement);
-    affectedPhis.addAll(replacement.outValue().uniquePhiUsers());
+    affectedPhis.addAll(argument.outValue().uniquePhiUsers());
+    return replacement;
   }
 
   private void removeUnusedArguments(
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java
index 6e69b3c..ecee91f 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java
@@ -73,19 +73,48 @@
       methodsToReprocessBuilder.add(method);
     }
 
-    public LongLivedProgramMethodSetBuilder<ProgramMethodSet> getMethodsToReprocessBuilder() {
-      return methodsToReprocessBuilder;
+    public void add(ProgramMethod method, GraphLens currentGraphLens) {
+      methodsToReprocessBuilder.add(method, currentGraphLens);
+    }
+
+    public void addAll(Collection<ProgramMethod> methods, GraphLens currentGraphLens) {
+      methods.forEach(method -> add(method, currentGraphLens));
+    }
+
+    public boolean contains(ProgramMethod method, GraphLens currentGraphLens) {
+      return methodsToReprocessBuilder.contains(method, currentGraphLens);
+    }
+
+    public PostMethodProcessor.Builder merge(
+        LongLivedProgramMethodSetBuilder<ProgramMethodSet> otherMethodsToReprocessBuilder) {
+      methodsToReprocessBuilder.merge(otherMethodsToReprocessBuilder);
+      return this;
     }
 
     public void put(ProgramMethodSet methodsToRevisit) {
       methodsToRevisit.forEach(this::add);
     }
 
+    public void remove(ProgramMethod method, GraphLens graphLens) {
+      methodsToReprocessBuilder.remove(method.getReference(), graphLens);
+    }
+
+    public PostMethodProcessor.Builder removeAll(Collection<DexMethod> methods) {
+      methodsToReprocessBuilder.removeAll(methods);
+      return this;
+    }
+
     // Some optimizations may change methods, creating new instances of the encoded methods with a
     // new signature. The compiler needs to update the set of methods that must be reprocessed
     // according to the graph lens.
-    public void rewrittenWithLens(AppView<AppInfoWithLiveness> appView) {
+    public PostMethodProcessor.Builder rewrittenWithLens(AppView<AppInfoWithLiveness> appView) {
       methodsToReprocessBuilder.rewrittenWithLens(appView);
+      return this;
+    }
+
+    public PostMethodProcessor.Builder rewrittenWithLens(GraphLens graphLens) {
+      methodsToReprocessBuilder.rewrittenWithLens(graphLens);
+      return this;
     }
 
     PostMethodProcessor build(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
index 49ad698..11d7e80 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
@@ -128,24 +128,16 @@
     DexMethod original = appView.graphLens().getOriginalMethodSignature(method);
     assert original != null;
     MethodProvider provider = rewritableMethods.getProvider(original);
-    // TODO(b/150693139): Since the DesugarLibraryRetargeter is run during IR processing while the
-    // backported method rewriter is run in cf to cf, we need here to compute if the method is
-    // actually going to be retargeted through desugared library backports, and compute the
-    // corresponding backported method if so. This can be removed once the DesugarLibraryRetargeter
-    // has been moved as a cf to cf transformation.
+    // Old versions of desugared library have in the jar file pre-desugared code. This is used
+    // to undesugar pre-desugared code, then the code is re-desugared with D8/R8. This is
+    // maintained for legacy only, recent desugared library should not be shipped with
+    // pre-desugared code.
+    Map<DexType, DexType> legacyBackport =
+        appView.options().machineDesugaredLibrarySpecification.getLegacyBackport();
     if (provider == null
         && appView.options().isDesugaredLibraryCompilation()
-        && appView
-            .options()
-            .desugaredLibrarySpecification
-            .getBackportCoreLibraryMember()
-            .containsKey(method.holder)) {
-      DexType newHolder =
-          appView
-              .options()
-              .desugaredLibrarySpecification
-              .getBackportCoreLibraryMember()
-              .get(method.holder);
+        && legacyBackport.containsKey(method.holder)) {
+      DexType newHolder = legacyBackport.get(method.holder);
       DexMethod backportedMethod =
           appView.dexItemFactory().createMethod(newHolder, method.proto, method.name);
       provider = rewritableMethods.getProvider(backportedMethod);
@@ -1111,6 +1103,39 @@
                 type));
       }
 
+      // java.util.concurrent.atomic.AtomicReference
+      {
+        // compareAndSet(Object expect, Object update)
+        DexType type = factory.createType("Ljava/util/concurrent/atomic/AtomicReference;");
+        DexString name = factory.createString("compareAndSet");
+        DexProto proto =
+            factory.createProto(factory.booleanType, factory.objectType, factory.objectType);
+        DexMethod method = factory.createMethod(type, proto, name);
+        addProvider(
+            new StatifyingMethodWithForwardingGenerator(
+                method,
+                BackportedMethods::AtomicReferenceMethods_compareAndSet,
+                "compareAndSet",
+                type));
+      }
+
+      // java.util.concurrent.atomic.AtomicReferenceArray
+      {
+        // compareAndSet(int index, Object expect, Object update)
+        DexType type = factory.createType("Ljava/util/concurrent/atomic/AtomicReferenceArray;");
+        DexString name = factory.createString("compareAndSet");
+        DexProto proto =
+            factory.createProto(
+                factory.booleanType, factory.intType, factory.objectType, factory.objectType);
+        DexMethod method = factory.createMethod(type, proto, name);
+        addProvider(
+            new StatifyingMethodWithForwardingGenerator(
+                method,
+                BackportedMethods::AtomicReferenceArrayMethods_compareAndSet,
+                "compareAndSet",
+                type));
+      }
+
       // java.util.concurrent.atomic.AtomicReferenceFieldUpdater
       {
         // compareAndSet(Object object, Object expect, Object update)
@@ -1412,7 +1437,7 @@
     }
 
     private void addProvider(MethodProvider generator) {
-      if (appView.options().desugaredLibrarySpecification.isSupported(generator.method, appView)) {
+      if (appView.options().machineDesugaredLibrarySpecification.isSupported(generator.method)) {
         // TODO(b/174453232): Remove this after the configuration file format has bee updated
         // with the "rewrite_method" section.
         if (generator.method.getHolderType() == appView.dexItemFactory().objectsType) {
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 282f4f1..3dbc8a5 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
@@ -32,10 +32,12 @@
 import com.google.common.collect.Sets;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.IdentityHashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Set;
 import java.util.function.BiConsumer;
 import java.util.function.Consumer;
@@ -236,6 +238,9 @@
     private void finalizeLambdaDesugaring(
         ClassConverterResult.Builder classConverterResultBuilder,
         Consumer<ProgramMethod> needsProcessing) {
+      // Sort synthesized lambda classes to ensure deterministic insertion of the synthesized
+      // $r8$lambda$ target methods.
+      synthesizedLambdaClasses.sort(Comparator.comparing(LambdaClass::getType));
       for (LambdaClass lambdaClass : synthesizedLambdaClasses) {
         lambdaClass.target.ensureAccessibilityIfNeeded(
             classConverterResultBuilder, needsProcessing);
@@ -423,9 +428,18 @@
     }
 
     private void finalizeLambdaDesugaring() {
+      // Sort synthesized lambda classes to ensure deterministic insertion of the synthesized
+      // $r8$lambda$ target methods.
+      List<Entry<LambdaClass, ProgramMethod>> sortedSynthesizedLambdaClasses =
+          new ArrayList<>(synthesizedLambdaClasses.entrySet());
+      sortedSynthesizedLambdaClasses.sort(Comparator.comparing(entry -> entry.getKey().getType()));
+
       Set<DexProgramClass> classesWithSerializableLambdas = Sets.newIdentityHashSet();
-      synthesizedLambdaClasses.forEach(
-          (lambdaClass, context) -> {
+      sortedSynthesizedLambdaClasses.forEach(
+          entry -> {
+            LambdaClass lambdaClass = entry.getKey();
+            ProgramMethod context = entry.getValue();
+
             lambdaClass.target.ensureAccessibilityIfNeeded();
 
             // Populate set of types with serialized lambda method for removal.
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java
index 1fd0ad0..1243435 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java
@@ -57,7 +57,7 @@
         InterfaceMethodProcessorFacade interfaceMethodProcessorFacade,
         RetargetingInfo retargetingInfo) {
       ArrayList<CfPostProcessingDesugaring> desugarings = new ArrayList<>();
-      if (!appView.options().desugaredLibrarySpecification.getRetargetCoreLibMember().isEmpty()
+      if (appView.options().machineDesugaredLibrarySpecification.hasRetargeting()
           && !appView.options().isDesugaredLibraryCompilation()) {
         desugarings.add(new DesugaredLibraryRetargeterPostProcessor(appView, retargetingInfo));
       }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
index 33c69ac..e55c246 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
@@ -82,9 +82,9 @@
     this.nestBasedAccessDesugaring = NestBasedAccessDesugaring.create(appView);
     BackportedMethodRewriter backportedMethodRewriter = null;
     desugaredLibraryRetargeter =
-        appView.options().desugaredLibrarySpecification.getRetargetCoreLibMember().isEmpty()
-            ? null
-            : new DesugaredLibraryRetargeter(appView);
+        appView.options().machineDesugaredLibrarySpecification.hasRetargeting()
+            ? new DesugaredLibraryRetargeter(appView)
+            : null;
     if (desugaredLibraryRetargeter != null) {
       desugarings.add(desugaredLibraryRetargeter);
     }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/PrefixRewritingMapper.java b/src/main/java/com/android/tools/r8/ir/desugar/PrefixRewritingMapper.java
index bf96b67..90f2505 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/PrefixRewritingMapper.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/PrefixRewritingMapper.java
@@ -4,20 +4,11 @@
 
 package com.android.tools.r8.ir.desugar;
 
-import com.android.tools.r8.errors.CompilationError;
 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.DexProto;
-import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineRewritingFlags;
-import com.android.tools.r8.utils.DescriptorUtils;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Sets;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
 import java.util.Map;
-import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Consumer;
 
@@ -31,7 +22,7 @@
 
   public abstract DexType rewrittenType(DexType type, AppView<?> appView);
 
-  public abstract DexType rewrittenContextType(DexType type, AppView<?> appView);
+  public abstract DexType rewrittenContextType(DexType type);
 
   public boolean hasRewrittenType(DexType type, AppView<?> appView) {
     return rewrittenType(type, appView) != null;
@@ -53,195 +44,34 @@
 
   public abstract void forAllRewrittenTypes(Consumer<DexType> consumer);
 
-  public static class DesugarPrefixRewritingMapper extends PrefixRewritingMapper {
-
-    private final Set<DexType> notRewritten = Sets.newConcurrentHashSet();
-    private final Map<DexType, DexType> rewritten = new ConcurrentHashMap<>();
-    private final Map<DexString, DexString> initialPrefixes;
-    private final DexItemFactory factory;
-    private final boolean l8Compilation;
-
-    public DesugarPrefixRewritingMapper(
-        Map<String, String> prefixes, DexItemFactory itemFactory, boolean libraryCompilation) {
-      assert itemFactory != null || prefixes.isEmpty();
-      this.factory = itemFactory;
-      this.l8Compilation = libraryCompilation;
-      ImmutableMap.Builder<DexString, DexString> builder = ImmutableMap.builder();
-      for (String key : prefixes.keySet()) {
-        builder.put(toDescriptorPrefix(key), toDescriptorPrefix(prefixes.get(key)));
-      }
-      this.initialPrefixes = builder.build();
-      validatePrefixes(prefixes);
-    }
-
-    private DexString toDescriptorPrefix(String prefix) {
-      return factory.createString("L" + DescriptorUtils.getBinaryNameFromJavaType(prefix));
-    }
-
-    @Override
-    public void forAllRewrittenTypes(Consumer<DexType> consumer) {
-      rewritten.keySet().forEach(consumer);
-    }
-
-    private void validatePrefixes(Map<String, String> initialPrefixes) {
-      String[] prefixes = initialPrefixes.keySet().toArray(new String[0]);
-      for (int i = 0; i < prefixes.length; i++) {
-        for (int j = i + 1; j < prefixes.length; j++) {
-          String small, large;
-          if (prefixes[i].length() < prefixes[j].length()) {
-            small = prefixes[i];
-            large = prefixes[j];
-          } else {
-            small = prefixes[j];
-            large = prefixes[i];
-          }
-          if (large.startsWith(small)) {
-            throw new CompilationError(
-                "Inconsistent prefix in desugared library:"
-                    + " Should a class starting with "
-                    + small
-                    + " be rewritten using "
-                    + small
-                    + " -> "
-                    + initialPrefixes.get(small)
-                    + " or using "
-                    + large
-                    + " - > "
-                    + initialPrefixes.get(large)
-                    + " ?");
-          }
-        }
-      }
-    }
-
-    @Override
-    public DexType rewrittenType(DexType type, AppView<?> appView) {
-      assert appView != null || l8Compilation;
-      if (notRewritten.contains(type)) {
-        return null;
-      }
-      if (rewritten.containsKey(type)) {
-        return rewritten.get(type);
-      }
-      return computePrefix(type, appView);
-    }
-
-    @Override
-    public DexType rewrittenContextType(DexType type, AppView<?> appView) {
-      DexType rewritten = rewrittenType(type, appView);
-      if (rewritten != null) {
-        return rewritten;
-      }
-      LegacyDesugaredLibrarySpecification desugaredLibrarySpecification =
-          appView.options().desugaredLibrarySpecification;
-      if (desugaredLibrarySpecification.getEmulateLibraryInterface().containsKey(type)) {
-        return desugaredLibrarySpecification.getEmulateLibraryInterface().get(type);
-      }
-      for (Map<DexType, DexType> value :
-          desugaredLibrarySpecification.getRetargetCoreLibMember().values()) {
-        if (value.containsKey(type)) {
-          // Hack until machine specification are ready.
-          String prefix =
-              DescriptorUtils.getJavaTypeFromBinaryName(
-                  desugaredLibrarySpecification.getSynthesizedLibraryClassesPackagePrefix());
-          String interfaceType = type.toString();
-          int firstPackage = interfaceType.indexOf('.');
-          return appView
-              .dexItemFactory()
-              .createType(
-                  DescriptorUtils.javaTypeToDescriptor(
-                      prefix + interfaceType.substring(firstPackage + 1)));
-        }
-      }
-      return null;
-    }
-
-    // Besides L8 compilation, program types should not be rewritten.
-    private void failIfRewritingProgramType(DexType type, AppView<?> appView) {
-      if (l8Compilation) {
-        return;
-      }
-
-      DexType dexType = type.isArrayType() ? type.toBaseType(appView.dexItemFactory()) : type;
-      DexClass dexClass = appView.definitionFor(dexType);
-      if (dexClass != null && dexClass.isProgramClass()) {
-        appView
-            .options()
-            .reporter
-            .error(
-                "Cannot compile program class "
-                    + dexType
-                    + " since it conflicts with a desugared library rewriting rule.");
-      }
-    }
-
-    @Override
-    public void rewriteType(DexType type, DexType rewrittenType) {
-      assert !notRewritten.contains(type)
-          : "New rewriting rule for "
-              + type
-              + " but the compiler has already made decisions based on the fact that this type was"
-              + " not rewritten";
-      assert !rewritten.containsKey(type) || rewritten.get(type) == rewrittenType
-          : "New rewriting rule for "
-              + type
-              + " but the compiler has already made decisions based on a different rewriting rule"
-              + " for this type";
-      rewritten.put(type, rewrittenType);
-    }
-
-    private DexType computePrefix(DexType type, AppView<?> appView) {
-      DexString prefixToMatch = type.descriptor.withoutArray(factory);
-      DexType result = lookup(type, prefixToMatch, initialPrefixes);
-      if (result != null) {
-        failIfRewritingProgramType(type, appView);
-        return result;
-      }
-      notRewritten.add(type);
-      return null;
-    }
-
-    private DexType lookup(DexType type, DexString prefixToMatch, Map<DexString, DexString> map) {
-      // TODO(b/154800164): We could use tries instead of looking-up everywhere.
-      for (DexString prefix : map.keySet()) {
-        if (prefixToMatch.startsWith(prefix)) {
-          DexString rewrittenTypeDescriptor =
-              type.descriptor.withNewPrefix(prefix, map.get(prefix), factory);
-          DexType rewrittenType = factory.createType(rewrittenTypeDescriptor);
-          rewriteType(type, rewrittenType);
-          return rewrittenType;
-        }
-      }
-      return null;
-    }
-
-    @Override
-    public boolean isRewriting() {
-      return true;
-    }
-  }
-
   public static class MachineDesugarPrefixRewritingMapper extends PrefixRewritingMapper {
 
-    private final PrefixRewritingMapper mapper;
     private final Map<DexType, DexType> rewriteType;
     private final Map<DexType, DexType> rewriteDerivedTypeOnly;
 
-    public MachineDesugarPrefixRewritingMapper(
-        PrefixRewritingMapper mapper, MachineRewritingFlags flags) {
-      this.mapper = mapper;
-      this.rewriteType = new ConcurrentHashMap<>(flags.getRewriteType());
-      rewriteDerivedTypeOnly = flags.getRewriteDerivedTypeOnly();
+    public MachineDesugarPrefixRewritingMapper(MachineDesugaredLibrarySpecification specification) {
+      this.rewriteType = new ConcurrentHashMap<>(specification.getRewriteType());
+      rewriteDerivedTypeOnly = specification.getRewriteDerivedTypeOnly();
     }
 
     @Override
     public DexType rewrittenType(DexType type, AppView<?> appView) {
-      assert mapper.rewrittenType(type, appView) == rewriteType.get(type);
+      if (type.isArrayType()) {
+        DexType rewrittenBaseType =
+            rewrittenType(type.toBaseType(appView.dexItemFactory()), appView);
+        if (rewrittenBaseType == null) {
+          return null;
+        }
+        return appView
+            .dexItemFactory()
+            .createArrayType(type.getNumberOfLeadingSquareBrackets(), rewrittenBaseType);
+      }
       return rewriteType.get(type);
     }
 
     @Override
-    public DexType rewrittenContextType(DexType context, AppView<?> appView) {
+    public DexType rewrittenContextType(DexType context) {
+      assert !context.isArrayType();
       if (rewriteType.containsKey(context)) {
         return rewriteType.get(context);
       }
@@ -250,7 +80,6 @@
 
     @Override
     public void rewriteType(DexType type, DexType rewrittenType) {
-      mapper.rewriteType(type, rewrittenType);
       rewriteType.compute(
           type,
           (t, val) -> {
@@ -278,7 +107,7 @@
     }
 
     @Override
-    public DexType rewrittenContextType(DexType type, AppView<?> appView) {
+    public DexType rewrittenContextType(DexType type) {
       return null;
     }
 
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 e6b793f..ccd00a9 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
@@ -97,6 +97,8 @@
     factory.createSynthesizedType("Ljava/util/OptionalInt;");
     factory.createSynthesizedType("Ljava/util/OptionalLong;");
     factory.createSynthesizedType("Ljava/util/Set;");
+    factory.createSynthesizedType("Ljava/util/concurrent/atomic/AtomicReference;");
+    factory.createSynthesizedType("Ljava/util/concurrent/atomic/AtomicReferenceArray;");
     factory.createSynthesizedType("Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater;");
     factory.createSynthesizedType("Ljava/util/function/Consumer;");
     factory.createSynthesizedType("Ljava/util/function/DoubleConsumer;");
@@ -115,6 +117,85 @@
     factory.createSynthesizedType("[Ljava/util/Map$Entry;");
   }
 
+  public static CfCode AtomicReferenceArrayMethods_compareAndSet(
+      InternalOptions options, DexMethod method) {
+    CfLabel label0 = new CfLabel();
+    CfLabel label1 = new CfLabel();
+    CfLabel label2 = new CfLabel();
+    CfLabel label3 = new CfLabel();
+    CfLabel label4 = new CfLabel();
+    return new CfCode(
+        method.holder,
+        4,
+        4,
+        ImmutableList.of(
+            label0,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3},
+                    new FrameType[] {
+                      FrameType.initialized(
+                          options.itemFactory.createType(
+                              "Ljava/util/concurrent/atomic/AtomicReferenceArray;")),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.objectType),
+                      FrameType.initialized(options.itemFactory.objectType)
+                    }),
+                new ArrayDeque<>(Arrays.asList())),
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.INT, 1),
+            new CfLoad(ValueType.OBJECT, 2),
+            new CfLoad(ValueType.OBJECT, 3),
+            new CfInvoke(
+                182,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType(
+                        "Ljava/util/concurrent/atomic/AtomicReferenceArray;"),
+                    options.itemFactory.createProto(
+                        options.itemFactory.booleanType,
+                        options.itemFactory.intType,
+                        options.itemFactory.objectType,
+                        options.itemFactory.objectType),
+                    options.itemFactory.createString("compareAndSet")),
+                false),
+            new CfIf(If.Type.EQ, ValueType.INT, label2),
+            label1,
+            new CfConstNumber(1, ValueType.INT),
+            new CfReturn(ValueType.INT),
+            label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3},
+                    new FrameType[] {
+                      FrameType.initialized(
+                          options.itemFactory.createType(
+                              "Ljava/util/concurrent/atomic/AtomicReferenceArray;")),
+                      FrameType.initialized(options.itemFactory.intType),
+                      FrameType.initialized(options.itemFactory.objectType),
+                      FrameType.initialized(options.itemFactory.objectType)
+                    }),
+                new ArrayDeque<>(Arrays.asList())),
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.INT, 1),
+            new CfInvoke(
+                182,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType(
+                        "Ljava/util/concurrent/atomic/AtomicReferenceArray;"),
+                    options.itemFactory.createProto(
+                        options.itemFactory.objectType, options.itemFactory.intType),
+                    options.itemFactory.createString("get")),
+                false),
+            new CfLoad(ValueType.OBJECT, 2),
+            new CfIfCmp(If.Type.EQ, ValueType.OBJECT, label0),
+            label3,
+            new CfConstNumber(0, ValueType.INT),
+            new CfReturn(ValueType.INT),
+            label4),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
+
   public static CfCode AtomicReferenceFieldUpdaterMethods_compareAndSet(
       InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
@@ -194,6 +275,77 @@
         ImmutableList.of());
   }
 
+  public static CfCode AtomicReferenceMethods_compareAndSet(
+      InternalOptions options, DexMethod method) {
+    CfLabel label0 = new CfLabel();
+    CfLabel label1 = new CfLabel();
+    CfLabel label2 = new CfLabel();
+    CfLabel label3 = new CfLabel();
+    CfLabel label4 = new CfLabel();
+    return new CfCode(
+        method.holder,
+        3,
+        3,
+        ImmutableList.of(
+            label0,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initialized(
+                          options.itemFactory.createType(
+                              "Ljava/util/concurrent/atomic/AtomicReference;")),
+                      FrameType.initialized(options.itemFactory.objectType),
+                      FrameType.initialized(options.itemFactory.objectType)
+                    }),
+                new ArrayDeque<>(Arrays.asList())),
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfLoad(ValueType.OBJECT, 2),
+            new CfInvoke(
+                182,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/util/concurrent/atomic/AtomicReference;"),
+                    options.itemFactory.createProto(
+                        options.itemFactory.booleanType,
+                        options.itemFactory.objectType,
+                        options.itemFactory.objectType),
+                    options.itemFactory.createString("compareAndSet")),
+                false),
+            new CfIf(If.Type.EQ, ValueType.INT, label2),
+            label1,
+            new CfConstNumber(1, ValueType.INT),
+            new CfReturn(ValueType.INT),
+            label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2},
+                    new FrameType[] {
+                      FrameType.initialized(
+                          options.itemFactory.createType(
+                              "Ljava/util/concurrent/atomic/AtomicReference;")),
+                      FrameType.initialized(options.itemFactory.objectType),
+                      FrameType.initialized(options.itemFactory.objectType)
+                    }),
+                new ArrayDeque<>(Arrays.asList())),
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfInvoke(
+                182,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/util/concurrent/atomic/AtomicReference;"),
+                    options.itemFactory.createProto(options.itemFactory.objectType),
+                    options.itemFactory.createString("get")),
+                false),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfIfCmp(If.Type.EQ, ValueType.OBJECT, label0),
+            label3,
+            new CfConstNumber(0, ValueType.INT),
+            new CfReturn(ValueType.INT),
+            label4),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
+
   public static CfCode BooleanMethods_compare(InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPICallbackSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPICallbackSynthesizer.java
index 150e6c4..b66a96a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPICallbackSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPICallbackSynthesizer.java
@@ -20,13 +20,13 @@
 import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaring;
 import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryWrapperSynthesizerEventConsumer.DesugaredLibraryAPICallbackSynthesizorEventConsumer;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
 import com.android.tools.r8.ir.synthetic.DesugaredLibraryAPIConversionCfCodeProvider.APICallbackWrapperCfCodeProvider;
 import com.android.tools.r8.utils.OptionalBool;
 import com.android.tools.r8.utils.WorkList;
 import com.google.common.collect.Sets;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ExecutorService;
 
@@ -109,8 +109,8 @@
     if (!appView.rewritePrefix.hasRewrittenTypeInSignature(definition.getProto(), appView)
         || appView
             .options()
-            .desugaredLibrarySpecification
-            .getEmulateLibraryInterface()
+            .machineDesugaredLibrarySpecification
+            .getEmulatedInterfaces()
             .containsKey(method.getHolderType())) {
       return false;
     }
@@ -127,7 +127,7 @@
         return false;
       }
     }
-    if (!appView.options().desugaredLibrarySpecification.supportAllCallbacksFromLibrary()
+    if (!appView.options().machineDesugaredLibrarySpecification.supportAllCallbacksFromLibrary()
         && appView.options().isDesugaredLibraryCompilation()) {
       return false;
     }
@@ -178,13 +178,13 @@
   }
 
   private boolean shouldGenerateCallbacksForEmulateInterfaceAPIs(DexClass dexClass) {
-    if (appView.options().desugaredLibrarySpecification.supportAllCallbacksFromLibrary()) {
+    if (appView.options().machineDesugaredLibrarySpecification.supportAllCallbacksFromLibrary()) {
       return true;
     }
-    Map<DexType, DexType> emulateLibraryInterfaces =
-        appView.options().desugaredLibrarySpecification.getEmulateLibraryInterface();
-    return !(emulateLibraryInterfaces.containsKey(dexClass.type)
-        || emulateLibraryInterfaces.containsValue(dexClass.type));
+    MachineDesugaredLibrarySpecification specification =
+        appView.options().machineDesugaredLibrarySpecification;
+    return !(specification.getEmulatedInterfaces().containsKey(dexClass.type)
+        || specification.isEmulatedInterfaceRewrittenType(dexClass.type));
   }
 
   private ProgramMethod generateCallbackMethod(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPIConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPIConverter.java
index 987dc8d..f234a1b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPIConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPIConverter.java
@@ -181,8 +181,8 @@
     return interfaceResult != null
         && appView
             .options()
-            .desugaredLibrarySpecification
-            .getEmulateLibraryInterface()
+            .machineDesugaredLibrarySpecification
+            .getEmulatedInterfaces()
             .containsKey(interfaceResult.getHolderType());
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryWrapperSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryWrapperSynthesizer.java
index 4a8cabe..71442ac 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryWrapperSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryWrapperSynthesizer.java
@@ -13,12 +13,14 @@
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexClasspathClass;
 import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMember;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.FieldAccessFlags;
 import com.android.tools.r8.graph.MethodAccessFlags;
@@ -38,6 +40,7 @@
 import com.android.tools.r8.synthesis.SyntheticClassBuilder;
 import com.android.tools.r8.synthesis.SyntheticMethodBuilder;
 import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
+import com.android.tools.r8.utils.Pair;
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.google.common.collect.Iterables;
 import java.util.ArrayList;
@@ -174,17 +177,21 @@
   private DexMethod getCustomConversion(DexType type, DexType srcType, DexType destType) {
     // ConversionType holds the methods "rewrittenType convert(type)" and the other way around.
     // But everything is going to be rewritten, so we need to use vivifiedType and type".
-    DexType conversionHolder =
-        appView.options().desugaredLibrarySpecification.getCustomConversions().get(type);
-    if (conversionHolder != null) {
+    Pair<DexType, DexString> pair =
+        appView.options().machineDesugaredLibrarySpecification.getCustomConversions().get(type);
+    if (pair != null) {
       return factory.createMethod(
-          conversionHolder, factory.createProto(destType, srcType), factory.convertMethodName);
+          pair.getFirst(), factory.createProto(destType, srcType), pair.getSecond());
     }
     return null;
   }
 
   private boolean canConvert(DexType type) {
-    return appView.options().desugaredLibrarySpecification.getCustomConversions().containsKey(type)
+    return appView
+            .options()
+            .machineDesugaredLibrarySpecification
+            .getCustomConversions()
+            .containsKey(type)
         || canGenerateWrapper(type);
   }
 
@@ -212,7 +219,7 @@
   }
 
   private boolean canGenerateWrapper(DexType type) {
-    return appView.options().desugaredLibrarySpecification.getWrapperConversions().contains(type);
+    return appView.options().machineDesugaredLibrarySpecification.getWrappers().containsKey(type);
   }
 
   private DexClass getValidClassToWrap(DexType type) {
@@ -458,9 +465,8 @@
       if (holderClass == null) {
         assert appView
             .options()
-            .desugaredLibrarySpecification
-            .getEmulateLibraryInterface()
-            .containsValue(method.getHolderType());
+            .machineDesugaredLibrarySpecification
+            .isEmulatedInterfaceRewrittenType(method.getHolderType());
         isInterface = true;
       } else {
         isInterface = holderClass.isInterface();
@@ -541,19 +547,17 @@
   }
 
   private Iterable<DexMethod> allImplementedMethods(DexClass clazz) {
-    if (appView.options().testing.machineDesugaredLibrarySpecification != null) {
+    if (appView.options().machineDesugaredLibrarySpecification != null) {
       return appView
           .options()
-          .testing
           .machineDesugaredLibrarySpecification
-          .getRewritingFlags()
           .getWrappers()
           .get(clazz.type);
     }
     List<DexEncodedMethod> dexEncodedMethods =
         allImplementedMethodsCache.computeIfAbsent(
             clazz.type, type -> internalAllImplementedMethods(clazz));
-    return Iterables.transform(dexEncodedMethods, m -> m.getReference());
+    return Iterables.transform(dexEncodedMethods, DexEncodedMember::getReference);
   }
 
   private List<DexEncodedMethod> internalAllImplementedMethods(DexClass libraryClass) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecification.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecification.java
index 6a6a2d4..1e871b3 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecification.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecification.java
@@ -6,14 +6,9 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.desugar.PrefixRewritingMapper;
-import com.android.tools.r8.ir.desugar.PrefixRewritingMapper.DesugarPrefixRewritingMapper;
 import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.InternalOptions;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -23,56 +18,20 @@
   private final boolean libraryCompilation;
   private final HumanTopLevelFlags topLevelFlags;
   private final HumanRewritingFlags rewritingFlags;
-  private final PrefixRewritingMapper prefixRewritingMapper;
-
-  public static HumanDesugaredLibrarySpecification withOnlyRewritePrefixForTesting(
-      Map<String, String> prefix, InternalOptions options) {
-    return new HumanDesugaredLibrarySpecification(
-        HumanTopLevelFlags.empty(),
-        HumanRewritingFlags.withOnlyRewritePrefixForTesting(prefix, options),
-        true,
-        options.itemFactory);
-  }
-
-  public static HumanDesugaredLibrarySpecification empty() {
-    return new HumanDesugaredLibrarySpecification(
-        HumanTopLevelFlags.empty(), HumanRewritingFlags.empty(), false, null) {
-
-      @Override
-      public boolean isSupported(DexReference reference, AppView<?> appView) {
-        return false;
-      }
-
-      @Override
-      public boolean isEmptyConfiguration() {
-        return true;
-      }
-    };
-  }
 
   public HumanDesugaredLibrarySpecification(
       HumanTopLevelFlags topLevelFlags,
       HumanRewritingFlags rewritingFlags,
-      boolean libraryCompilation,
-      DexItemFactory factory) {
+      boolean libraryCompilation) {
     this.libraryCompilation = libraryCompilation;
     this.topLevelFlags = topLevelFlags;
     this.rewritingFlags = rewritingFlags;
-    this.prefixRewritingMapper =
-        rewritingFlags.getRewritePrefix().isEmpty()
-            ? PrefixRewritingMapper.empty()
-            : new DesugarPrefixRewritingMapper(
-                rewritingFlags.getRewritePrefix(), factory, libraryCompilation);
   }
 
   public boolean supportAllCallbacksFromLibrary() {
     return topLevelFlags.supportAllCallbacksFromLibrary();
   }
 
-  public PrefixRewritingMapper getPrefixRewritingMapper() {
-    return prefixRewritingMapper;
-  }
-
   public AndroidApiLevel getRequiredCompilationApiLevel() {
     return topLevelFlags.getRequiredCompilationAPILevel();
   }
@@ -109,10 +68,6 @@
     return rewritingFlags.getEmulateLibraryInterface();
   }
 
-  public boolean isSupported(DexReference reference, AppView<?> appView) {
-    return prefixRewritingMapper.hasRewrittenType(reference.getContextType(), appView);
-  }
-
   // If the method is retargeted, answers the retargeted method, else null.
   public DexMethod retargetMethod(DexEncodedMethod method, AppView<?> appView) {
     Map<DexMethod, DexType> retargetCoreLibMember = rewritingFlags.getRetargetCoreLibMember();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecificationParser.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecificationParser.java
index a612177..e01def6 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecificationParser.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecificationParser.java
@@ -103,7 +103,7 @@
 
     HumanDesugaredLibrarySpecification config =
         new HumanDesugaredLibrarySpecification(
-            topLevelFlags, legacyRewritingFlags, libraryCompilation, dexItemFactory);
+            topLevelFlags, legacyRewritingFlags, libraryCompilation);
     origin = null;
     return config;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyDesugaredLibrarySpecification.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyDesugaredLibrarySpecification.java
index 3da0b97..9ab05b9 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyDesugaredLibrarySpecification.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyDesugaredLibrarySpecification.java
@@ -6,13 +6,9 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.desugar.PrefixRewritingMapper;
-import com.android.tools.r8.ir.desugar.PrefixRewritingMapper.DesugarPrefixRewritingMapper;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.InternalOptions;
@@ -26,25 +22,18 @@
   private final boolean libraryCompilation;
   private final LegacyTopLevelFlags topLevelFlags;
   private final LegacyRewritingFlags rewritingFlags;
-  private final PrefixRewritingMapper prefixRewritingMapper;
 
   public static LegacyDesugaredLibrarySpecification withOnlyRewritePrefixForTesting(
       Map<String, String> prefix, InternalOptions options) {
     return new LegacyDesugaredLibrarySpecification(
         LegacyTopLevelFlags.empty(),
         LegacyRewritingFlags.withOnlyRewritePrefixForTesting(prefix, options),
-        true,
-        options.itemFactory);
+        true);
   }
 
   public static LegacyDesugaredLibrarySpecification empty() {
     return new LegacyDesugaredLibrarySpecification(
-        LegacyTopLevelFlags.empty(), LegacyRewritingFlags.empty(), false, null) {
-
-      @Override
-      public boolean isSupported(DexReference reference, AppView<?> appView) {
-        return false;
-      }
+        LegacyTopLevelFlags.empty(), LegacyRewritingFlags.empty(), false) {
 
       @Override
       public boolean isEmptyConfiguration() {
@@ -56,16 +45,10 @@
   public LegacyDesugaredLibrarySpecification(
       LegacyTopLevelFlags topLevelFlags,
       LegacyRewritingFlags rewritingFlags,
-      boolean libraryCompilation,
-      DexItemFactory factory) {
+      boolean libraryCompilation) {
     this.libraryCompilation = libraryCompilation;
     this.topLevelFlags = topLevelFlags;
     this.rewritingFlags = rewritingFlags;
-    this.prefixRewritingMapper =
-        rewritingFlags.getRewritePrefix().isEmpty()
-            ? PrefixRewritingMapper.empty()
-            : new DesugarPrefixRewritingMapper(
-                rewritingFlags.getRewritePrefix(), factory, libraryCompilation);
   }
 
   public LegacyTopLevelFlags getTopLevelFlags() {
@@ -80,10 +63,6 @@
     return topLevelFlags.supportAllCallbacksFromLibrary();
   }
 
-  public PrefixRewritingMapper getPrefixRewritingMapper() {
-    return prefixRewritingMapper;
-  }
-
   public AndroidApiLevel getRequiredCompilationApiLevel() {
     return topLevelFlags.getRequiredCompilationAPILevel();
   }
@@ -123,10 +102,6 @@
     return rewritingFlags.getEmulateLibraryInterface();
   }
 
-  public boolean isSupported(DexReference reference, AppView<?> appView) {
-    return prefixRewritingMapper.hasRewrittenType(reference.getContextType(), appView);
-  }
-
   // If the method is retargeted, answers the retargeted method, else null.
   public DexMethod retargetMethod(DexEncodedMethod method, AppView<?> appView) {
     Map<DexString, Map<DexType, DexType>> retargetCoreLibMember =
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyDesugaredLibrarySpecificationParser.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyDesugaredLibrarySpecificationParser.java
index 7cece17..a3afcd9 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyDesugaredLibrarySpecificationParser.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyDesugaredLibrarySpecificationParser.java
@@ -110,7 +110,7 @@
 
     LegacyDesugaredLibrarySpecification config =
         new LegacyDesugaredLibrarySpecification(
-            topLevelFlags, legacyRewritingFlags, libraryCompilation, dexItemFactory);
+            topLevelFlags, legacyRewritingFlags, libraryCompilation);
     origin = null;
     return config;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java
index 501482e..e1368e3 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java
@@ -4,12 +4,41 @@
 
 package com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification;
 
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexReference;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.Pair;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Consumer;
+
 public class MachineDesugaredLibrarySpecification {
 
   private final boolean libraryCompilation;
   private final MachineTopLevelFlags topLevelFlags;
   private final MachineRewritingFlags rewritingFlags;
 
+  public static MachineDesugaredLibrarySpecification empty() {
+    return new MachineDesugaredLibrarySpecification(
+        false, MachineTopLevelFlags.empty(), MachineRewritingFlags.builder().build()) {
+      @Override
+      public boolean isSupported(DexReference reference) {
+        return false;
+      }
+    };
+  }
+
+  public static MachineDesugaredLibrarySpecification withOnlyRewriteTypeForTesting(
+      Map<DexType, DexType> rewriteTypeForTesting) {
+    MachineRewritingFlags.Builder builder = MachineRewritingFlags.builder();
+    rewriteTypeForTesting.forEach(builder::rewriteType);
+    return new MachineDesugaredLibrarySpecification(
+        true, MachineTopLevelFlags.empty(), builder.build());
+  }
+
   public MachineDesugaredLibrarySpecification(
       boolean libraryCompilation,
       MachineTopLevelFlags topLevelFlags,
@@ -23,11 +52,116 @@
     return libraryCompilation;
   }
 
-  public MachineTopLevelFlags getTopLevelFlags() {
-    return topLevelFlags;
+  public AndroidApiLevel getRequiredCompilationAPILevel() {
+    return topLevelFlags.getRequiredCompilationAPILevel();
   }
 
-  public MachineRewritingFlags getRewritingFlags() {
-    return rewritingFlags;
+  public String getSynthesizedLibraryClassesPackagePrefix() {
+    return topLevelFlags.getSynthesizedLibraryClassesPackagePrefix();
+  }
+
+  public String getIdentifier() {
+    return topLevelFlags.getIdentifier();
+  }
+
+  public String getJsonSource() {
+    return topLevelFlags.getJsonSource();
+  }
+
+  public boolean supportAllCallbacksFromLibrary() {
+    return topLevelFlags.supportAllCallbacksFromLibrary();
+  }
+
+  public List<String> getExtraKeepRules() {
+    return topLevelFlags.getExtraKeepRules();
+  }
+
+  public Map<DexType, DexType> getRewriteType() {
+    return rewritingFlags.getRewriteType();
+  }
+
+  public Map<DexType, DexType> getRewriteDerivedTypeOnly() {
+    return rewritingFlags.getRewriteDerivedTypeOnly();
+  }
+
+  public Map<DexMethod, DexMethod> getStaticRetarget() {
+    return rewritingFlags.getStaticRetarget();
+  }
+
+  public Map<DexMethod, DexMethod> getNonEmulatedVirtualRetarget() {
+    return rewritingFlags.getNonEmulatedVirtualRetarget();
+  }
+
+  public Map<DexMethod, EmulatedDispatchMethodDescriptor> getEmulatedVirtualRetarget() {
+    return rewritingFlags.getEmulatedVirtualRetarget();
+  }
+
+  public void forEachRetargetHolder(Consumer<DexType> consumer) {
+    rewritingFlags.forEachRetargetHolder(consumer);
+  }
+
+  public Map<DexType, EmulatedInterfaceDescriptor> getEmulatedInterfaces() {
+    return rewritingFlags.getEmulatedInterfaces();
+  }
+
+  public EmulatedDispatchMethodDescriptor getEmulatedInterfaceEmulatedDispatchMethodDescriptor(
+      DexMethod method) {
+    return rewritingFlags.getEmulatedInterfaceEmulatedDispatchMethodDescriptor(method);
+  }
+
+  public boolean isCustomConversionRewrittenType(DexType type) {
+    return rewritingFlags.isCustomConversionRewrittenType(type);
+  }
+
+  public boolean isEmulatedInterfaceRewrittenType(DexType type) {
+    return rewritingFlags.isEmulatedInterfaceRewrittenType(type);
+  }
+
+  public Map<DexType, List<DexMethod>> getWrappers() {
+    return rewritingFlags.getWrappers();
+  }
+
+  public Map<DexType, DexType> getLegacyBackport() {
+    return rewritingFlags.getLegacyBackport();
+  }
+
+  public Set<DexType> getDontRetarget() {
+    return rewritingFlags.getDontRetarget();
+  }
+
+  public Map<DexType, Pair<DexType, DexString>> getCustomConversions() {
+    return rewritingFlags.getCustomConversions();
+  }
+
+  public boolean hasRetargeting() {
+    return rewritingFlags.hasRetargeting();
+  }
+
+  public boolean hasEmulatedInterfaces() {
+    return rewritingFlags.hasEmulatedInterfaces();
+  }
+
+  public boolean isSupported(DexReference reference) {
+    // Support through type rewriting.
+    if (rewritingFlags.getRewriteType().containsKey(reference.getContextType())) {
+      return true;
+    }
+    if (!reference.isDexMethod()) {
+      return false;
+    }
+    // Support through retargeting.
+    DexMethod dexMethod = reference.asDexMethod();
+    if (getStaticRetarget().containsKey(dexMethod)
+        || getNonEmulatedVirtualRetarget().containsKey(dexMethod)
+        || getEmulatedVirtualRetarget().containsKey(dexMethod)) {
+      return true;
+    }
+    // Support through emulated interface.
+    for (EmulatedInterfaceDescriptor descriptor : getEmulatedInterfaces().values()) {
+      if (descriptor.getEmulatedMethods().containsKey(dexMethod)) {
+        return true;
+      }
+    }
+    return false;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java
index a6c487e..c5c77c6 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java
@@ -11,10 +11,12 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
 import java.util.IdentityHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.function.Consumer;
 
 public class MachineRewritingFlags {
 
@@ -94,6 +96,12 @@
     return emulatedVirtualRetarget;
   }
 
+  public void forEachRetargetHolder(Consumer<DexType> consumer) {
+    staticRetarget.keySet().forEach(m -> consumer.accept(m.getHolderType()));
+    nonEmulatedVirtualRetarget.keySet().forEach(m -> consumer.accept(m.getHolderType()));
+    emulatedVirtualRetarget.keySet().forEach(m -> consumer.accept(m.getHolderType()));
+  }
+
   public Map<DexType, EmulatedInterfaceDescriptor> getEmulatedInterfaces() {
     return emulatedInterfaces;
   }
@@ -110,10 +118,37 @@
     return dontRetarget;
   }
 
+  public boolean isCustomConversionRewrittenType(DexType type) {
+    return Iterables.any(customConversions.values(), pair -> pair.getFirst() == type);
+  }
+
   public Map<DexType, Pair<DexType, DexString>> getCustomConversions() {
     return customConversions;
   }
 
+  public boolean hasRetargeting() {
+    return !staticRetarget.isEmpty()
+        || !nonEmulatedVirtualRetarget.isEmpty()
+        || !emulatedVirtualRetarget.isEmpty();
+  }
+
+  public boolean isEmulatedInterfaceRewrittenType(DexType type) {
+    return Iterables.any(
+        emulatedInterfaces.values(), descriptor -> descriptor.getRewrittenType() == type);
+  }
+
+  public boolean hasEmulatedInterfaces() {
+    return !emulatedInterfaces.isEmpty();
+  }
+
+  EmulatedDispatchMethodDescriptor getEmulatedInterfaceEmulatedDispatchMethodDescriptor(
+      DexMethod method) {
+    if (!emulatedInterfaces.containsKey(method.getHolderType())) {
+      return null;
+    }
+    return emulatedInterfaces.get(method.getHolderType()).getEmulatedMethods().get(method);
+  }
+
   public static class Builder {
 
     Builder() {}
@@ -137,6 +172,7 @@
     public void rewriteType(DexType src, DexType target) {
       assert src != null;
       assert target != null;
+      assert src != target;
       rewriteType.put(src, target);
     }
 
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineTopLevelFlags.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineTopLevelFlags.java
index f426219..0c6a88a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineTopLevelFlags.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineTopLevelFlags.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification;
 
 import com.android.tools.r8.utils.AndroidApiLevel;
+import com.google.common.collect.ImmutableList;
 import java.util.List;
 
 public class MachineTopLevelFlags {
@@ -23,6 +24,11 @@
   private final boolean supportAllCallbacksFromLibrary;
   private final List<String> extraKeepRules;
 
+  public static MachineTopLevelFlags empty() {
+    return new MachineTopLevelFlags(
+        AndroidApiLevel.B, "unused", null, null, false, ImmutableList.of());
+  }
+
   public MachineTopLevelFlags(
       AndroidApiLevel requiredCompilationAPILevel,
       String synthesizedLibraryClassesPackagePrefix,
@@ -54,7 +60,7 @@
     return jsonSource;
   }
 
-  public boolean isSupportAllCallbacksFromLibrary() {
+  public boolean supportAllCallbacksFromLibrary() {
     return supportAllCallbacksFromLibrary;
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeter.java
index a54cd8d..5fcf037 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeter.java
@@ -129,8 +129,8 @@
     }
     if (appView
         .options()
-        .desugaredLibrarySpecification
-        .getDontRetargetLibMember()
+        .machineDesugaredLibrarySpecification
+        .getDontRetarget()
         .contains(context.getContextType())) {
       return NO_REWRITING;
     }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterL8Synthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterL8Synthesizer.java
index 11fcfb7..eb28d20 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterL8Synthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterL8Synthesizer.java
@@ -20,7 +20,7 @@
       AppView<?> appView, RetargetingInfo retargetingInfo) {
     assert appView.options().isDesugaredLibraryCompilation();
     if (retargetingInfo == null || retargetingInfo.getEmulatedVirtualRetarget().isEmpty()) {
-      assert appView.options().desugaredLibrarySpecification.getRetargetCoreLibMember().isEmpty();
+      assert !appView.options().machineDesugaredLibrarySpecification.hasRetargeting();
       return null;
     }
     return new DesugaredLibraryRetargeterL8Synthesizer(appView, retargetingInfo);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterLibraryTypeSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterLibraryTypeSynthesizer.java
index bbaa4cf..bfb423a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterLibraryTypeSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterLibraryTypeSynthesizer.java
@@ -39,16 +39,16 @@
 public class DesugaredLibraryRetargeterLibraryTypeSynthesizer {
 
   public static void checkForAssumedLibraryTypes(AppView<?> appView) {
-    Map<DexString, Map<DexType, DexType>> retargetCoreLibMember =
-        appView.options().desugaredLibrarySpecification.getRetargetCoreLibMember();
-    for (DexString methodName : retargetCoreLibMember.keySet()) {
-      for (DexType inType : retargetCoreLibMember.get(methodName).keySet()) {
-        DexClass typeClass = appView.definitionFor(inType);
-        if (typeClass == null) {
-          warnMissingRetargetCoreLibraryMember(inType, appView);
-        }
-      }
-    }
+    appView
+        .options()
+        .machineDesugaredLibrarySpecification
+        .forEachRetargetHolder(
+            inType -> {
+              DexClass typeClass = appView.definitionFor(inType);
+              if (typeClass == null) {
+                warnMissingRetargetCoreLibraryMember(inType, appView);
+              }
+            });
   }
 
   public static void amendLibraryWithRetargetedMembers(AppView<AppInfoWithClassHierarchy> appView) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterPostProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterPostProcessor.java
index 414c32d..d1e64ec 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterPostProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterPostProcessor.java
@@ -127,8 +127,8 @@
       }
       if (appView
           .options()
-          .desugaredLibrarySpecification
-          .getDontRetargetLibMember()
+          .machineDesugaredLibrarySpecification
+          .getDontRetarget()
           .contains(clazz.getType())) {
         continue;
       }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/RetargetingInfo.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/RetargetingInfo.java
index 9e1f5e3..b7ac366 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/RetargetingInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/RetargetingInfo.java
@@ -4,24 +4,9 @@
 package com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter;
 
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexClassAndMethod;
-import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexProto;
-import com.android.tools.r8.graph.DexString;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.DerivedMethod;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.EmulatedDispatchMethodDescriptor;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineRewritingFlags;
-import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
-import com.android.tools.r8.utils.WorkList;
-import com.google.common.collect.ImmutableMap;
-import java.util.ArrayList;
-import java.util.IdentityHashMap;
-import java.util.LinkedHashMap;
-import java.util.List;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
 import java.util.Map;
 
 public class RetargetingInfo {
@@ -40,15 +25,12 @@
   }
 
   public static RetargetingInfo get(AppView<?> appView) {
-    if (appView.options().testing.machineDesugaredLibrarySpecification != null) {
-      MachineRewritingFlags rewritingFlags =
-          appView.options().testing.machineDesugaredLibrarySpecification.getRewritingFlags();
-      return new RetargetingInfo(
-          rewritingFlags.getStaticRetarget(),
-          rewritingFlags.getNonEmulatedVirtualRetarget(),
-          rewritingFlags.getEmulatedVirtualRetarget());
-    }
-    return new RetargetingInfoBuilder(appView).computeRetargetingInfo();
+    MachineDesugaredLibrarySpecification specification =
+        appView.options().machineDesugaredLibrarySpecification;
+    return new RetargetingInfo(
+        specification.getStaticRetarget(),
+        specification.getNonEmulatedVirtualRetarget(),
+        specification.getEmulatedVirtualRetarget());
   }
 
   public Map<DexMethod, DexMethod> getStaticRetarget() {
@@ -62,152 +44,4 @@
   public Map<DexMethod, EmulatedDispatchMethodDescriptor> getEmulatedVirtualRetarget() {
     return emulatedVirtualRetarget;
   }
-
-  private static class RetargetingInfoBuilder {
-
-    private final AppView<?> appView;
-    private final Map<DexMethod, DexMethod> staticRetarget = new IdentityHashMap<>();
-    private final Map<DexMethod, DexMethod> nonEmulatedVirtualRetarget = new IdentityHashMap<>();
-    private final Map<DexMethod, EmulatedDispatchMethodDescriptor> emulatedVirtualRetarget =
-        new IdentityHashMap<>();
-
-    public RetargetingInfoBuilder(AppView<?> appView) {
-      this.appView = appView;
-    }
-
-    private RetargetingInfo computeRetargetingInfo() {
-      LegacyDesugaredLibrarySpecification desugaredLibrarySpecification =
-          appView.options().desugaredLibrarySpecification;
-      Map<DexString, Map<DexType, DexType>> retargetCoreLibMember =
-          desugaredLibrarySpecification.getRetargetCoreLibMember();
-      if (retargetCoreLibMember.isEmpty()) {
-        return new RetargetingInfo(ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of());
-      }
-      for (DexString methodName : retargetCoreLibMember.keySet()) {
-        for (DexType inType : retargetCoreLibMember.get(methodName).keySet()) {
-          DexClass typeClass = appView.definitionFor(inType);
-          if (typeClass != null) {
-            DexType newHolder = retargetCoreLibMember.get(methodName).get(inType);
-            List<DexClassAndMethod> found = findMethodsWithName(methodName, typeClass);
-            for (DexClassAndMethod method : found) {
-              DexMethod methodReference = method.getReference();
-              if (method.getAccessFlags().isStatic()) {
-                staticRetarget.put(
-                    methodReference,
-                    computeRetargetMethod(
-                        methodReference, method.getAccessFlags().isStatic(), newHolder));
-                continue;
-              }
-              if (isEmulatedInterfaceDispatch(method)) {
-                continue;
-              }
-              if (typeClass.isFinal() || method.getAccessFlags().isFinal()) {
-                nonEmulatedVirtualRetarget.put(
-                    methodReference,
-                    computeRetargetMethod(
-                        methodReference, method.getAccessFlags().isStatic(), newHolder));
-              } else {
-                // Virtual rewrites require emulated dispatch for inheritance.
-                // The call is rewritten to the dispatch holder class instead.
-                DexProto newProto = appView.dexItemFactory().prependHolderToProto(methodReference);
-                DexMethod forwardingDexMethod =
-                    appView.dexItemFactory().createMethod(newHolder, newProto, methodName);
-                DerivedMethod forwardingMethod = new DerivedMethod(forwardingDexMethod);
-                DerivedMethod interfaceMethod =
-                    new DerivedMethod(methodReference, SyntheticKind.RETARGET_INTERFACE);
-                DerivedMethod dispatchMethod =
-                    new DerivedMethod(methodReference, SyntheticKind.RETARGET_CLASS);
-                emulatedVirtualRetarget.put(
-                    methodReference,
-                    new EmulatedDispatchMethodDescriptor(
-                        interfaceMethod, dispatchMethod, forwardingMethod, new LinkedHashMap<>()));
-              }
-            }
-          }
-        }
-      }
-      if (desugaredLibrarySpecification.isLibraryCompilation()) {
-        // TODO(b/177977763): This is only a workaround rewriting invokes of j.u.Arrays.deepEquals0
-        // to j.u.DesugarArrays.deepEquals0.
-        DexItemFactory itemFactory = appView.options().dexItemFactory();
-        DexString name = itemFactory.createString("deepEquals0");
-        DexProto proto =
-            itemFactory.createProto(
-                itemFactory.booleanType, itemFactory.objectType, itemFactory.objectType);
-        DexMethod source =
-            itemFactory.createMethod(
-                itemFactory.createType(itemFactory.arraysDescriptor), proto, name);
-        DexMethod target =
-            computeRetargetMethod(
-                source, true, itemFactory.createType("Ljava/util/DesugarArrays;"));
-        staticRetarget.put(source, target);
-        // TODO(b/181629049): This is only a workaround rewriting invokes of
-        //  j.u.TimeZone.getTimeZone taking a java.time.ZoneId.
-        name = itemFactory.createString("getTimeZone");
-        proto =
-            itemFactory.createProto(
-                itemFactory.createType("Ljava/util/TimeZone;"),
-                itemFactory.createType("Ljava/time/ZoneId;"));
-        source =
-            itemFactory.createMethod(itemFactory.createType("Ljava/util/TimeZone;"), proto, name);
-        target =
-            computeRetargetMethod(
-                source, true, itemFactory.createType("Ljava/util/DesugarTimeZone;"));
-        staticRetarget.put(source, target);
-      }
-      return new RetargetingInfo(
-          ImmutableMap.copyOf(staticRetarget),
-          ImmutableMap.copyOf(nonEmulatedVirtualRetarget),
-          emulatedVirtualRetarget);
-    }
-
-    DexMethod computeRetargetMethod(DexMethod method, boolean isStatic, DexType newHolder) {
-      DexItemFactory factory = appView.dexItemFactory();
-      DexProto newProto = isStatic ? method.getProto() : factory.prependHolderToProto(method);
-      return factory.createMethod(newHolder, newProto, method.getName());
-    }
-
-    private boolean isEmulatedInterfaceDispatch(DexClassAndMethod method) {
-      // Answers true if this method is already managed through emulated interface dispatch.
-      Map<DexType, DexType> emulateLibraryInterface =
-          appView.options().desugaredLibrarySpecification.getEmulateLibraryInterface();
-      if (emulateLibraryInterface.isEmpty()) {
-        return false;
-      }
-      DexMethod methodToFind = method.getReference();
-      // Look-up all superclass and interfaces, if an emulated interface is found, and it implements
-      // the method, answers true.
-      WorkList<DexClass> worklist = WorkList.newIdentityWorkList(method.getHolder());
-      while (worklist.hasNext()) {
-        DexClass clazz = worklist.next();
-        if (clazz.isInterface()
-            && emulateLibraryInterface.containsKey(clazz.getType())
-            && clazz.lookupMethod(methodToFind) != null) {
-          return true;
-        }
-        // All super types are library class, or we are doing L8 compilation.
-        clazz.forEachImmediateSupertype(
-            superType -> {
-              DexClass superClass = appView.definitionFor(superType);
-              if (superClass != null) {
-                worklist.addIfNotSeen(superClass);
-              }
-            });
-      }
-      return false;
-    }
-
-    private List<DexClassAndMethod> findMethodsWithName(DexString methodName, DexClass clazz) {
-      List<DexClassAndMethod> found = new ArrayList<>();
-      clazz.forEachClassMethodMatching(
-          definition -> definition.getName() == methodName, found::add);
-      assert !found.isEmpty()
-          : "Should have found a method (library specifications) for "
-              + clazz.toSourceString()
-              + "."
-              + methodName
-              + ". Maybe the library used for the compilation should be newer.";
-      return found;
-    }
-  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineEmulatedInterfaceConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineEmulatedInterfaceConverter.java
index 2411ba0..04cc12b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineEmulatedInterfaceConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineEmulatedInterfaceConverter.java
@@ -8,7 +8,6 @@
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanRewritingFlags;
@@ -44,10 +43,10 @@
     Set<DexMethod> dontRewriteInvocation = rewritingFlags.getDontRewriteInvocation();
     emulatedInterfaceHierarchy = processEmulatedInterfaceHierarchy(appInfo, emulateInterfaces);
     for (DexType itf : emulateInterfaces.keySet()) {
-      DexProgramClass itfClass = appInfo.contextIndependentDefinitionFor(itf).asProgramClass();
+      DexClass itfClass = appInfo.contextIndependentDefinitionFor(itf);
       assert itfClass != null;
       Map<DexMethod, EmulatedDispatchMethodDescriptor> emulatedMethods = new IdentityHashMap<>();
-      itfClass.forEachProgramVirtualMethodMatching(
+      itfClass.forEachClassMethodMatching(
           m -> m.isDefaultMethod() && !dontRewriteInvocation.contains(m.getReference()),
           method ->
               emulatedMethods.put(
@@ -104,7 +103,6 @@
       for (int i = subInterfaces.size() - 1; i >= 0; i--) {
         DexClass subInterfaceClass = appInfo.definitionFor(subInterfaces.get(i));
         assert subInterfaceClass != null;
-        assert subInterfaceClass.isProgramClass();
         // Else computation of subInterface would have failed.
         // if the method is implemented, extra dispatch is required.
         DexEncodedMethod result = subInterfaceClass.lookupVirtualMethod(method);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachinePrefixConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachinePrefixConverter.java
index 6ff7428..92efb24 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachinePrefixConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachinePrefixConverter.java
@@ -5,22 +5,31 @@
 package com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion;
 
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanRewritingFlags;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineRewritingFlags;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.google.common.collect.ImmutableMap;
+import java.util.IdentityHashMap;
 import java.util.Map;
 
 public class HumanToMachinePrefixConverter {
 
   private final AppInfoWithClassHierarchy appInfo;
+  private final MachineRewritingFlags.Builder builder;
+  private final String synthesizedPrefix;
+  private final Map<DexType, DexType> reverse = new IdentityHashMap<>();
 
-  public HumanToMachinePrefixConverter(AppInfoWithClassHierarchy appInfo) {
+  public HumanToMachinePrefixConverter(
+      AppInfoWithClassHierarchy appInfo,
+      MachineRewritingFlags.Builder builder,
+      String synthesizedPrefix) {
     this.appInfo = appInfo;
+    this.builder = builder;
+    this.synthesizedPrefix = synthesizedPrefix;
   }
 
   private DexString toDescriptorPrefix(String prefix) {
@@ -29,20 +38,30 @@
         .createString("L" + DescriptorUtils.getBinaryNameFromJavaType(prefix));
   }
 
-  public void convertPrefixFlags(
-      HumanRewritingFlags rewritingFlags,
-      MachineRewritingFlags.Builder builder,
-      String synthesizedPrefix) {
+  public void convertPrefixFlags(HumanRewritingFlags rewritingFlags) {
     Map<DexString, DexString> descriptorPrefix = convertRewritePrefix(rewritingFlags);
-    rewriteClasses(descriptorPrefix, builder);
-    rewriteValues(descriptorPrefix, builder, rewritingFlags.getRetargetCoreLibMember());
-    rewriteValues(descriptorPrefix, builder, rewritingFlags.getCustomConversions());
-    rewriteEmulatedInterface(builder, rewritingFlags.getEmulateLibraryInterface());
-    rewriteRetargetKeys(builder, rewritingFlags.getRetargetCoreLibMember(), synthesizedPrefix);
+    rewriteClasses(descriptorPrefix);
+    rewriteValues(descriptorPrefix, rewritingFlags.getRetargetCoreLibMember());
+    rewriteValues(descriptorPrefix, rewritingFlags.getCustomConversions());
+    rewriteEmulatedInterface(rewritingFlags.getEmulateLibraryInterface());
+    rewriteRetargetKeys(rewritingFlags.getRetargetCoreLibMember());
+    rewriteReverse(descriptorPrefix);
   }
 
-  public DexType convertJavaNameToDesugaredLibrary(DexType type, String prefix) {
-    String convertedPrefix = DescriptorUtils.getJavaTypeFromBinaryName(prefix);
+  // For custom conversions, this is responsible in rewriting backward.
+  private void rewriteReverse(Map<DexString, DexString> descriptorPrefix) {
+    reverse.forEach(
+        (rewrittenType, type) -> {
+          DexType backwardRewrittenType = rewrittenType(descriptorPrefix, rewrittenType);
+          if (backwardRewrittenType != null) {
+            assert backwardRewrittenType == type;
+            builder.rewriteType(rewrittenType, type);
+          }
+        });
+  }
+
+  public DexType convertJavaNameToDesugaredLibrary(DexType type) {
+    String convertedPrefix = DescriptorUtils.getJavaTypeFromBinaryName(synthesizedPrefix);
     String interfaceType = type.toString();
     int firstPackage = interfaceType.indexOf('.');
     return appInfo
@@ -52,40 +71,49 @@
                 convertedPrefix + interfaceType.substring(firstPackage + 1)));
   }
 
-  private void rewriteRetargetKeys(
-      MachineRewritingFlags.Builder builder, Map<DexMethod, DexType> retarget, String prefix) {
+  private void rewriteRetargetKeys(Map<DexMethod, DexType> retarget) {
     for (DexMethod dexMethod : retarget.keySet()) {
-      DexType type = convertJavaNameToDesugaredLibrary(dexMethod.holder, prefix);
+      DexType type = convertJavaNameToDesugaredLibrary(dexMethod.holder);
       builder.rewriteDerivedTypeOnly(dexMethod.holder, type);
     }
   }
 
-  private void rewriteEmulatedInterface(
-      MachineRewritingFlags.Builder builder, Map<DexType, DexType> emulateLibraryInterface) {
+  private void rewriteEmulatedInterface(Map<DexType, DexType> emulateLibraryInterface) {
     emulateLibraryInterface.forEach(builder::rewriteDerivedTypeOnly);
   }
 
+  private void rewriteType(DexType type, DexType rewrittenType) {
+    builder.rewriteType(type, rewrittenType);
+    reverse.put(rewrittenType, type);
+  }
+
   private void rewriteValues(
       Map<DexString, DexString> descriptorPrefix,
-      MachineRewritingFlags.Builder builder,
       Map<?, DexType> flags) {
     for (DexType type : flags.values()) {
       DexType rewrittenType = rewrittenType(descriptorPrefix, type);
       if (rewrittenType != null) {
-        builder.rewriteType(type, rewrittenType);
+        rewriteType(type, rewrittenType);
       }
     }
   }
 
-  private void rewriteClasses(
-      Map<DexString, DexString> descriptorPrefix, MachineRewritingFlags.Builder builder) {
-    for (DexProgramClass clazz : appInfo.classes()) {
-      DexType type = clazz.type;
-      DexType rewrittenType = rewrittenType(descriptorPrefix, type);
-      if (rewrittenType != null) {
-        builder.rewriteType(type, rewrittenType);
-      }
+  private void rewriteClasses(Map<DexString, DexString> descriptorPrefix) {
+    for (DexClass clazz : appInfo.app().asDirect().libraryClasses()) {
+      rewriteClass(descriptorPrefix, clazz);
     }
+    for (DexClass clazz : appInfo.classes()) {
+      rewriteClass(descriptorPrefix, clazz);
+    }
+  }
+
+  private void rewriteClass(Map<DexString, DexString> descriptorPrefix, DexClass clazz) {
+    DexType type = clazz.type;
+    DexType rewrittenType = rewrittenType(descriptorPrefix, type);
+    if (rewrittenType == null) {
+      return;
+    }
+    rewriteType(type, rewrittenType);
   }
 
   private DexType rewrittenType(Map<DexString, DexString> descriptorPrefix, DexType type) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineRetargetConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineRetargetConverter.java
index 6dce945..b5c1ac3 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineRetargetConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineRetargetConverter.java
@@ -92,10 +92,14 @@
       DexClass subclass = appInfo.definitionFor(subtype);
       MethodResolutionResult resolutionResult =
           appInfo.resolveMethodOn(subclass, src.getReference());
-      if (resolutionResult.isSuccessfulMemberResolutionResult()
-          && resolutionResult.getResolvedMethod().getReference() != src.getReference()) {
-        assert false; // Unsupported.
-      }
+      // The resolution is not successful when compiling to dex if the method rewritten is missing
+      // in Android.jar.
+      assert !resolutionResult.isSuccessfulMemberResolutionResult()
+          || resolutionResult.getResolvedMethod().getReference() == src.getReference()
+          // There is a difference in the sql library between Android.jar and the JDK which leads
+          // to this resolution when compiling Cf to Cf while the methods do not exist in Android.
+          || (resolutionResult.getResolvedMethod().getHolderType().toString().contains("java.sql")
+              && resolutionResult.getResolvedMethod().getName().toString().equals("toInstant"));
     }
     return true;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java
index bc3a663..23824c5 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java
@@ -4,6 +4,8 @@
 
 package com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion;
 
+import com.android.tools.r8.ClassFileResourceProvider;
+import com.android.tools.r8.ProgramResourceProvider;
 import com.android.tools.r8.dex.ApplicationReader;
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
@@ -22,15 +24,51 @@
 import com.android.tools.r8.utils.Timing;
 import java.io.IOException;
 import java.nio.file.Path;
+import java.util.List;
 import java.util.concurrent.ExecutorService;
 
 public class HumanToMachineSpecificationConverter {
 
   public MachineDesugaredLibrarySpecification convert(
-      HumanDesugaredLibrarySpecification humanSpec, Path androidLib, InternalOptions options)
+      HumanDesugaredLibrarySpecification humanSpec,
+      List<ProgramResourceProvider> desugaredJDKLib,
+      List<ClassFileResourceProvider> library,
+      InternalOptions options)
       throws IOException {
-    DexApplication app = readApp(androidLib, options);
+    assert !humanSpec.isLibraryCompilation() || desugaredJDKLib != null;
+    AndroidApp.Builder builder = AndroidApp.builder();
+    for (ClassFileResourceProvider classFileResourceProvider : library) {
+      builder.addLibraryResourceProvider(classFileResourceProvider);
+    }
+    if (humanSpec.isLibraryCompilation()) {
+      for (ProgramResourceProvider programResourceProvider : desugaredJDKLib) {
+        builder.addProgramResourceProvider(programResourceProvider);
+      }
+    }
+    return internalConvert(humanSpec, builder.build(), options);
+  }
+
+  public MachineDesugaredLibrarySpecification convert(
+      HumanDesugaredLibrarySpecification humanSpec,
+      Path desugaredJDKLib,
+      Path androidLib,
+      InternalOptions options)
+      throws IOException {
+    assert !humanSpec.isLibraryCompilation() || desugaredJDKLib != null;
+    AndroidApp.Builder builder = AndroidApp.builder();
+    if (humanSpec.isLibraryCompilation()) {
+      builder.addProgramFile(desugaredJDKLib);
+    }
+    AndroidApp inputApp = builder.addLibraryFile(androidLib).build();
+    return internalConvert(humanSpec, inputApp, options);
+  }
+
+  public MachineDesugaredLibrarySpecification internalConvert(
+      HumanDesugaredLibrarySpecification humanSpec, AndroidApp inputApp, InternalOptions options)
+      throws IOException {
+    DexApplication app = readApp(inputApp, options);
     AppView<?> appView = AppView.createForD8(AppInfo.createInitialAppInfo(app));
+    LibraryValidator.validate(app, humanSpec.getTopLevelFlags().getRequiredCompilationAPILevel());
     MachineRewritingFlags machineRewritingFlags =
         convertRewritingFlags(
             humanSpec.getSynthesizedLibraryClassesPackagePrefix(),
@@ -59,8 +97,8 @@
     new HumanToMachineRetargetConverter(appInfo).convertRetargetFlags(rewritingFlags, builder);
     new HumanToMachineEmulatedInterfaceConverter(appInfo)
         .convertEmulatedInterfaces(rewritingFlags, appInfo, builder);
-    new HumanToMachinePrefixConverter(appInfo)
-        .convertPrefixFlags(rewritingFlags, builder, synthesizedPrefix);
+    new HumanToMachinePrefixConverter(appInfo, builder, synthesizedPrefix)
+        .convertPrefixFlags(rewritingFlags);
     new HumanToMachineWrapperConverter(appInfo).convertWrappers(rewritingFlags, builder);
     rewritingFlags
         .getCustomConversions()
@@ -75,10 +113,8 @@
     return builder.build();
   }
 
-  private DexApplication readApp(Path androidLib, InternalOptions options) throws IOException {
-    AndroidApp androidApp = AndroidApp.builder().addProgramFile(androidLib).build();
-    ApplicationReader applicationReader =
-        new ApplicationReader(androidApp, options, Timing.empty());
+  private DexApplication readApp(AndroidApp inputApp, InternalOptions options) throws IOException {
+    ApplicationReader applicationReader = new ApplicationReader(inputApp, options, Timing.empty());
     ExecutorService executorService = ThreadUtils.getExecutorService(options);
     return applicationReader.read(executorService).toDirect();
   }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LegacyToHumanSpecificationConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LegacyToHumanSpecificationConverter.java
index 34bf5fa..e5c9b42 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LegacyToHumanSpecificationConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LegacyToHumanSpecificationConverter.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion;
 
+import com.android.tools.r8.ClassFileResourceProvider;
 import com.android.tools.r8.StringConsumer;
 import com.android.tools.r8.StringResource;
 import com.android.tools.r8.dex.ApplicationReader;
@@ -65,7 +66,9 @@
       InternalOptions options)
       throws IOException {
     Origin origin = legacySpec.getOrigin();
-    DexApplication app = readApp(androidLib, options);
+    AndroidApp androidApp = AndroidApp.builder().addLibraryFile(androidLib).build();
+    DexApplication app = readApp(androidApp, options);
+    LibraryValidator.validate(app, legacySpec.getTopLevelFlags().getRequiredCompilationAPILevel());
     HumanTopLevelFlags humanTopLevelFlags = convertTopLevelFlags(legacySpec.getTopLevelFlags());
     Int2ObjectArrayMap<HumanRewritingFlags> commonFlags =
         convertRewritingFlagMap(legacySpec.getCommonFlags(), app, origin);
@@ -81,11 +84,30 @@
   }
 
   public HumanDesugaredLibrarySpecification convert(
+      LegacyDesugaredLibrarySpecification legacySpec,
+      List<ClassFileResourceProvider> library,
+      InternalOptions options)
+      throws IOException {
+    AndroidApp.Builder builder = AndroidApp.builder();
+    for (ClassFileResourceProvider classFileResourceProvider : library) {
+      builder.addLibraryResourceProvider(classFileResourceProvider);
+    }
+    return internalConvert(legacySpec, builder.build(), options);
+  }
+
+  public HumanDesugaredLibrarySpecification convert(
       LegacyDesugaredLibrarySpecification legacySpec, Path androidLib, InternalOptions options)
       throws IOException {
-    DexApplication app = readApp(androidLib, options);
-    HumanTopLevelFlags humanTopLevelFlags = convertTopLevelFlags(legacySpec.getTopLevelFlags());
+    AndroidApp androidApp = AndroidApp.builder().addLibraryFile(androidLib).build();
+    return internalConvert(legacySpec, androidApp, options);
+  }
 
+  public HumanDesugaredLibrarySpecification internalConvert(
+      LegacyDesugaredLibrarySpecification legacySpec, AndroidApp inputApp, InternalOptions options)
+      throws IOException {
+    DexApplication app = readApp(inputApp, options);
+    LibraryValidator.validate(app, legacySpec.getTopLevelFlags().getRequiredCompilationAPILevel());
+    HumanTopLevelFlags humanTopLevelFlags = convertTopLevelFlags(legacySpec.getTopLevelFlags());
     // The origin is not maintained in non multi-level specifications.
     // It should not matter since the origin is used to report invalid specifications, and
     // converting non multi-level specifications should be performed only with *valid*
@@ -95,10 +117,7 @@
     HumanRewritingFlags humanRewritingFlags =
         convertRewritingFlags(legacySpec.getRewritingFlags(), app, origin);
     return new HumanDesugaredLibrarySpecification(
-        humanTopLevelFlags,
-        humanRewritingFlags,
-        legacySpec.isLibraryCompilation(),
-        app.dexItemFactory());
+        humanTopLevelFlags, humanRewritingFlags, legacySpec.isLibraryCompilation());
   }
 
   private void legacyLibraryFlagHacks(
@@ -134,10 +153,8 @@
     libraryFlags.put(level, builder.build());
   }
 
-  private DexApplication readApp(Path androidLib, InternalOptions options) throws IOException {
-    AndroidApp androidApp = AndroidApp.builder().addLibraryFile(androidLib).build();
-    ApplicationReader applicationReader =
-        new ApplicationReader(androidApp, options, Timing.empty());
+  private DexApplication readApp(AndroidApp inputApp, InternalOptions options) throws IOException {
+    ApplicationReader applicationReader = new ApplicationReader(inputApp, options, Timing.empty());
     ExecutorService executorService = ThreadUtils.getExecutorService(options);
     return applicationReader.read(executorService).toDirect();
   }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LibraryValidator.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LibraryValidator.java
new file mode 100644
index 0000000..b415c354
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LibraryValidator.java
@@ -0,0 +1,36 @@
+// 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.desugaredlibrary.specificationconversion;
+
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.utils.AndroidApiLevel;
+
+public class LibraryValidator {
+
+  // Estimates if the library passed is at the expected minimum level, if it is not, raise
+  // a warning.
+  public static void validate(DexApplication app, AndroidApiLevel requiredCompilationAPILevel) {
+    DexType levelType;
+    if (requiredCompilationAPILevel.isEqualTo(AndroidApiLevel.O)) {
+      levelType = app.dexItemFactory.createType("Ljava/time/LocalTime;");
+    } else if (requiredCompilationAPILevel.isEqualTo(AndroidApiLevel.R)) {
+      levelType = app.dexItemFactory.createType("Ljava/util/concurrent/Flow;");
+    } else {
+      app.options.reporter.warning(
+          "Unsupported requiredCompilationAPILevel: " + requiredCompilationAPILevel);
+      return;
+    }
+    DexClass dexClass = app.definitionFor(levelType);
+    if (dexClass == null) {
+      app.options.reporter.warning(
+          "Desugared library requires to be compiled with a library file of API greater or equal to"
+              + " "
+              + requiredCompilationAPILevel
+              + ", but it seems the library file passed is of a lower API.");
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
index 758812e..33f7037 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
@@ -24,10 +24,10 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.GenericSignature;
 import com.android.tools.r8.graph.GenericSignature.ClassTypeSignature;
-import com.android.tools.r8.graph.LibraryMethod;
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.MethodResolutionResult;
 import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.DerivedMethod;
 import com.android.tools.r8.position.MethodPosition;
 import com.android.tools.r8.utils.BooleanBox;
 import com.android.tools.r8.utils.BooleanUtils;
@@ -49,7 +49,6 @@
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.BiConsumer;
-import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.Predicate;
 import org.objectweb.asm.Opcodes;
@@ -376,12 +375,7 @@
     this.dexItemFactory = appView.dexItemFactory();
     this.helper = new InterfaceDesugaringSyntheticHelper(appView);
     needsLibraryInfo =
-        !appView.options().desugaredLibrarySpecification.getEmulateLibraryInterface().isEmpty()
-            || !appView
-                .options()
-                .desugaredLibrarySpecification
-                .getRetargetCoreLibMember()
-                .isEmpty();
+        appView.options().machineDesugaredLibrarySpecification.hasEmulatedInterfaces();
     this.isLiveMethod = isLiveMethod;
   }
 
@@ -510,7 +504,7 @@
       DexClass iface = appView.definitionFor(emulatedInterface);
       if (iface != null) {
         assert iface.isLibraryClass()
-            || appView.options().desugaredLibrarySpecification.isLibraryCompilation();
+            || appView.options().machineDesugaredLibrarySpecification.isLibraryCompilation();
         workList.addIfNotSeen(iface.getInterfaces());
       }
     }
@@ -670,10 +664,10 @@
       resolveForwardForSignature(
           clazz,
           wrapper.get(),
-          target -> {
+          (target, forward) -> {
             if (isLiveMethod(target) && !superInfo.isTargetedByForwards(target)) {
               additionalForwards.add(target);
-              addForwardingMethod(target, clazz);
+              addForwardingMethod(target, forward, clazz);
             }
           });
     }
@@ -682,7 +676,7 @@
   // Looks up a method signature from the point of 'clazz', if it can dispatch to a default method
   // the 'addForward' call-back is called with the target of the forward.
   private void resolveForwardForSignature(
-      DexClass clazz, DexMethod method, Consumer<DexClassAndMethod> addForward) {
+      DexClass clazz, DexMethod method, BiConsumer<DexClassAndMethod, DexMethod> addForward) {
     AppInfoWithClassHierarchy appInfo = appView.appInfoForDesugaring();
     MethodResolutionResult resolutionResult = appInfo.resolveMethodOn(clazz, method);
     if (resolutionResult.isFailedResolution()
@@ -722,50 +716,22 @@
         resolutionResult.lookupVirtualDispatchTarget(clazz, appInfo);
     assert virtualDispatchTarget != null;
 
-    // Don't forward if the target is explicitly marked as 'dont-rewrite'
-    if (dontRewrite(virtualDispatchTarget)) {
-      return;
-    }
-
     // If resolution targets a default interface method, forward it.
     if (virtualDispatchTarget.isDefaultMethod()) {
-      addForward.accept(virtualDispatchTarget);
+      addForward.accept(
+          virtualDispatchTarget,
+          helper.ensureDefaultAsMethodOfCompanionClassStub(virtualDispatchTarget).getReference());
       return;
     }
 
-    // Remaining edge cases only pertain to desugaring of library methods.
-    if (!virtualDispatchTarget.isLibraryMethod() || ignoreLibraryInfo()) {
-      return;
+    DerivedMethod forwardingMethod =
+        helper.computeEmulatedInterfaceForwardingMethod(
+            virtualDispatchTarget.getHolder(), virtualDispatchTarget);
+    if (forwardingMethod != null) {
+      DexMethod concreteForwardingMethod =
+          helper.ensureEmulatedInterfaceForwardingMethod(forwardingMethod);
+      addForward.accept(virtualDispatchTarget, concreteForwardingMethod);
     }
-
-    LibraryMethod libraryMethod = virtualDispatchTarget.asLibraryMethod();
-    if (isRetargetMethod(libraryMethod)) {
-      addForward.accept(virtualDispatchTarget);
-      return;
-    }
-
-    // If target is a non-interface library class it may be an emulated interface,
-    // except on a rewritten type, where L8 has already dealt with the desugaring.
-    if (!libraryMethod.getHolder().isInterface()
-        && !appView.rewritePrefix.hasRewrittenType(libraryMethod.getHolderType(), appView)) {
-      // Here we use step-3 of resolution to find a maximally specific default interface method.
-      DexClassAndMethod result =
-          appInfo.lookupMaximallySpecificMethod(libraryMethod.getHolder(), method);
-      if (result != null && helper.isEmulatedInterface(result.getHolderType())) {
-        addForward.accept(result);
-      }
-    }
-  }
-
-  private boolean isRetargetMethod(LibraryMethod method) {
-    assert needsLibraryInfo();
-    assert method.getDefinition().isNonPrivateVirtualMethod();
-    return !method.getAccessFlags().isFinal()
-        && appView.options().desugaredLibrarySpecification.retargetMethod(method, appView) != null;
-  }
-
-  private boolean dontRewrite(DexClassAndMethod method) {
-    return needsLibraryInfo() && method.getHolder().isLibraryClass() && helper.dontRewrite(method);
   }
 
   // Construction of actual forwarding methods.
@@ -830,7 +796,8 @@
 
   // Note: The parameter 'target' may be a public method on a class in case of desugared
   // library retargeting (See below target.isInterface check).
-  private void addForwardingMethod(DexClassAndMethod target, DexClass clazz) {
+  private void addForwardingMethod(
+      DexClassAndMethod target, DexMethod forwardMethod, DexClass clazz) {
     if (!clazz.isProgramClass()) {
       return;
     }
@@ -847,10 +814,6 @@
     // NOTE: Never add a forwarding method to methods of classes unknown or coming from android.jar
     // even if this results in invalid code, these classes are never desugared.
     // In desugared library, emulated interface methods can be overridden by retarget lib members.
-    DexMethod forwardMethod =
-        target.getHolder().isInterface()
-            ? helper.ensureDefaultAsMethodOfCompanionClassStub(target).getReference()
-            : appView.options().desugaredLibrarySpecification.retargetMethod(target, appView);
     DexEncodedMethod desugaringForwardingMethod =
         DexEncodedMethod.createDesugaringForwardingMethod(
             target.getDefinition(), clazz, forwardMethod, dexItemFactory);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/EmulatedInterfaceApplicationRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/EmulatedInterfaceApplicationRewriter.java
index 74d94e3..d06fee7 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/EmulatedInterfaceApplicationRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/EmulatedInterfaceApplicationRewriter.java
@@ -15,6 +15,7 @@
 import com.android.tools.r8.utils.IterableUtils;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.IdentityHashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -25,8 +26,15 @@
 
   public EmulatedInterfaceApplicationRewriter(AppView<?> appView) {
     this.appView = appView;
-    this.emulatedInterfaces =
-        appView.options().desugaredLibrarySpecification.getEmulateLibraryInterface();
+    emulatedInterfaces = new IdentityHashMap<>();
+    appView
+        .options()
+        .machineDesugaredLibrarySpecification
+        .getEmulatedInterfaces()
+        .forEach(
+            (ei, descriptor) -> {
+              emulatedInterfaces.put(ei, descriptor.getRewrittenType());
+            });
   }
 
   public void rewriteApplication(DexApplication.Builder<?> builder) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringForTesting.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringForTesting.java
index a3e0164..6636e1e 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringForTesting.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringForTesting.java
@@ -4,10 +4,12 @@
 
 package com.android.tools.r8.ir.desugar.itf;
 
+import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
+
 public class InterfaceDesugaringForTesting {
 
   public static String getEmulateLibraryClassNameSuffix() {
-    return InterfaceDesugaringSyntheticHelper.EMULATE_LIBRARY_CLASS_NAME_SUFFIX;
+    return SyntheticKind.EMULATED_INTERFACE_CLASS.descriptor;
   }
 
   public static String getCompanionClassNameSuffix() {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java
index 77d99e5..cb4a537 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java
@@ -24,6 +24,7 @@
 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.DexProto;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DexValue.DexValueInt;
@@ -31,19 +32,20 @@
 import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
 import com.android.tools.r8.graph.InvalidCode;
 import com.android.tools.r8.graph.MethodAccessFlags;
+import com.android.tools.r8.graph.MethodResolutionResult;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.ThrowNullCode;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.DerivedMethod;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.EmulatedDispatchMethodDescriptor;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.EmulatedInterfaceDescriptor;
 import com.android.tools.r8.ir.desugar.itf.EmulatedInterfaceSynthesizerEventConsumer.ClasspathEmulatedInterfaceSynthesizerEventConsumer;
 import com.android.tools.r8.synthesis.SyntheticClassBuilder;
 import com.android.tools.r8.synthesis.SyntheticMethodBuilder;
 import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
 import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.Pair;
 import com.android.tools.r8.utils.structural.Ordered;
 import com.google.common.collect.ImmutableList;
-import java.util.Map;
-import java.util.Set;
 import java.util.function.Predicate;
 
 public class InterfaceDesugaringSyntheticHelper {
@@ -57,37 +59,37 @@
   }
 
   // Use InterfaceDesugaringForTesting for public accesses in tests.
-  static final String EMULATE_LIBRARY_CLASS_NAME_SUFFIX = "$-EL";
   static final String COMPANION_CLASS_NAME_SUFFIX = "$-CC";
   static final String DEFAULT_METHOD_PREFIX = "$default$";
   static final String PRIVATE_METHOD_PREFIX = "$private$";
 
   private final AppView<?> appView;
-  private final Map<DexType, DexType> emulatedInterfaces;
   private final Predicate<DexType> shouldIgnoreFromReportsPredicate;
 
   public InterfaceDesugaringSyntheticHelper(AppView<?> appView) {
     this.appView = appView;
-    emulatedInterfaces =
-        appView.options().desugaredLibrarySpecification.getEmulateLibraryInterface();
-
     this.shouldIgnoreFromReportsPredicate = getShouldIgnoreFromReportsPredicate(appView);
   }
 
   boolean isEmulatedInterface(DexType itf) {
-    return emulatedInterfaces.containsKey(itf);
+    return appView
+        .options()
+        .machineDesugaredLibrarySpecification
+        .getEmulatedInterfaces()
+        .containsKey(itf);
   }
 
   boolean isRewrittenEmulatedInterface(DexType itf) {
-    return emulatedInterfaces.containsValue(itf);
-  }
-
-  Set<DexType> getEmulatedInterfaces() {
-    return emulatedInterfaces.keySet();
+    return appView
+        .options()
+        .machineDesugaredLibrarySpecification
+        .isEmulatedInterfaceRewrittenType(itf);
   }
 
   DexType getEmulatedInterface(DexType type) {
-    return emulatedInterfaces.get(type);
+    EmulatedInterfaceDescriptor interfaceDescriptor =
+        appView.options().machineDesugaredLibrarySpecification.getEmulatedInterfaces().get(type);
+    return interfaceDescriptor == null ? null : interfaceDescriptor.getRewrittenType();
   }
 
   boolean isInDesugaredLibrary(DexClass clazz) {
@@ -98,17 +100,6 @@
     return appView.rewritePrefix.hasRewrittenType(clazz.type, appView);
   }
 
-  boolean dontRewrite(DexClassAndMethod method) {
-    for (Pair<DexType, DexString> dontRewrite :
-        appView.options().desugaredLibrarySpecification.getDontRewriteInvocation()) {
-      if (method.getHolderType() == dontRewrite.getFirst()
-          && method.getName() == dontRewrite.getSecond()) {
-        return true;
-      }
-    }
-    return false;
-  }
-
   final boolean isCompatibleDefaultMethod(DexEncodedMethod method) {
     assert !method.accessFlags.isConstructor();
     assert !method.accessFlags.isStatic();
@@ -129,25 +120,15 @@
     return true;
   }
 
-  public DexMethod emulateInterfaceLibraryMethod(DexClassAndMethod method) {
-    DexItemFactory factory = appView.dexItemFactory();
-    return factory.createMethod(
-        getEmulateLibraryInterfaceClassType(method.getHolderType(), factory),
-        factory.prependTypeToProto(method.getHolderType(), method.getProto()),
-        method.getName());
+  DexMethod emulatedInterfaceDispatchMethod(DerivedMethod method, DexType holder) {
+    assert method.getHolderKind() == SyntheticKind.EMULATED_INTERFACE_CLASS;
+    DexProto newProto = appView.dexItemFactory().prependHolderToProto(method.getMethod());
+    return appView.dexItemFactory().createMethod(holder, newProto, method.getName());
   }
 
-  private static String getEmulateLibraryInterfaceClassDescriptor(String descriptor) {
-    return descriptor.substring(0, descriptor.length() - 1)
-        + EMULATE_LIBRARY_CLASS_NAME_SUFFIX
-        + ";";
-  }
-
-  public static DexType getEmulateLibraryInterfaceClassType(DexType type, DexItemFactory factory) {
-    assert type.isClassType();
-    String descriptor = type.descriptor.toString();
-    String elTypeDescriptor = getEmulateLibraryInterfaceClassDescriptor(descriptor);
-    return factory.createSynthesizedType(elTypeDescriptor);
+  DexMethod emulatedInterfaceInterfaceMethod(DerivedMethod method) {
+    assert method.getHolderKind() == null;
+    return method.getMethod();
   }
 
   public static String getCompanionClassDescriptor(String descriptor) {
@@ -167,10 +148,6 @@
     return type.descriptor.toString().endsWith(COMPANION_CLASS_NAME_SUFFIX + ";");
   }
 
-  public static boolean isEmulatedLibraryClassType(DexType type) {
-    return type.descriptor.toString().endsWith(EMULATE_LIBRARY_CLASS_NAME_SUFFIX + ";");
-  }
-
   // Gets the interface class for a companion class `type`.
   DexType getInterfaceClassType(DexType type) {
     return getInterfaceClassType(type, appView.dexItemFactory());
@@ -197,9 +174,91 @@
             SyntheticClassBuilder::setInterface);
   }
 
-  DexClassAndMethod ensureEmulatedInterfaceMethod(
-      DexClassAndMethod method, ClasspathEmulatedInterfaceSynthesizerEventConsumer eventConsumer) {
-    DexMethod emulatedInterfaceMethod = emulateInterfaceLibraryMethod(method);
+  DexClassAndMethod lookupMaximallySpecificIncludingSelf(
+      DexClass initialResolutionHolder, DexClassAndMethod method) {
+    assert method.getHolderType().isClassType();
+    if (method.getHolder().isInterface()) {
+      return method;
+    }
+    return appView
+        .appInfoForDesugaring()
+        .lookupMaximallySpecificMethod(initialResolutionHolder, method.getReference());
+  }
+
+  EmulatedDispatchMethodDescriptor getEmulatedDispatchDescriptor(
+      DexClass initialResolutionHolder, DexClassAndMethod method) {
+    if (method == null) {
+      return null;
+    }
+    assert initialResolutionHolder != null;
+    // Outside L8 compilation, only library methods can lead to emulated interface dispatch.
+    if (!method.isLibraryMethod() && !appView.options().isDesugaredLibraryCompilation()) {
+      return null;
+    }
+    DexClassAndMethod maximallySpecificMethod =
+        lookupMaximallySpecificIncludingSelf(initialResolutionHolder, method);
+    if (maximallySpecificMethod == null) {
+      return null;
+    }
+    EmulatedDispatchMethodDescriptor descriptor =
+        appView
+            .options()
+            .machineDesugaredLibrarySpecification
+            .getEmulatedInterfaceEmulatedDispatchMethodDescriptor(
+                maximallySpecificMethod.getReference());
+    if (!appView.options().isDesugaredLibraryCompilation()) {
+      return descriptor;
+    }
+    return requiresEmulatedDispatchInL8(method, descriptor) ? descriptor : null;
+  }
+
+  private boolean requiresEmulatedDispatchInL8(
+      DexClassAndMethod method, EmulatedDispatchMethodDescriptor descriptor) {
+    return method.isLibraryMethod()
+        || isEmulatedInterface(method.getHolderType())
+        || (descriptor != null
+            && descriptor.getDispatchCases().containsKey(method.getHolderType()));
+  }
+
+  DerivedMethod computeEmulatedInterfaceDispatchMethod(MethodResolutionResult resolutionResult) {
+    EmulatedDispatchMethodDescriptor descriptor =
+        getEmulatedDispatchDescriptor(
+            resolutionResult.getInitialResolutionHolder(), resolutionResult.getResolutionPair());
+    return descriptor == null ? null : descriptor.getEmulatedDispatchMethod();
+  }
+
+  DerivedMethod computeEmulatedInterfaceForwardingMethod(
+      DexClass initialResolutionHolder, DexClassAndMethod method) {
+    EmulatedDispatchMethodDescriptor descriptor =
+        getEmulatedDispatchDescriptor(initialResolutionHolder, method);
+    if (descriptor == null) {
+      return null;
+    }
+    return descriptor.getDispatchCases().containsKey(method.getHolderType())
+        ? descriptor.getDispatchCases().get(method.getHolderType())
+        : descriptor.getForwardingMethod();
+  }
+
+  DexMethod ensureEmulatedInterfaceForwardingMethod(DerivedMethod method) {
+    if (method.getHolderKind() == null) {
+      return method.getMethod();
+    }
+    assert method.getHolderKind() == SyntheticKind.COMPANION_CLASS;
+    DexClassAndMethod resolvedMethod =
+        appView.appInfoForDesugaring().resolveMethod(method.getMethod(), true).getResolutionPair();
+    return ensureDefaultAsMethodOfCompanionClassStub(resolvedMethod).getReference();
+  }
+
+  DexClassAndMethod ensureEmulatedInterfaceDispatchMethod(
+      DerivedMethod emulatedDispatchMethod,
+      ClasspathEmulatedInterfaceSynthesizerEventConsumer eventConsumer) {
+    assert emulatedDispatchMethod.getHolderKind() == SyntheticKind.EMULATED_INTERFACE_CLASS;
+    DexClassAndMethod method =
+        appView
+            .appInfoForDesugaring()
+            .resolveMethod(emulatedDispatchMethod.getMethod(), true)
+            .getResolutionPair();
+    assert emulatedDispatchMethod.getHolderKind() == SyntheticKind.EMULATED_INTERFACE_CLASS;
     if (method.isProgramMethod()) {
       assert appView.options().isDesugaredLibraryCompilation();
       DexProgramClass emulatedInterface =
@@ -209,8 +268,15 @@
                   SyntheticKind.EMULATED_INTERFACE_CLASS,
                   method.asProgramMethod().getHolder(),
                   appView);
+      DexMethod emulatedInterfaceMethod =
+          emulatedInterfaceDispatchMethod(emulatedDispatchMethod, emulatedInterface.type);
+      assert emulatedInterface.lookupProgramMethod(emulatedInterfaceMethod) != null;
       return emulatedInterface.lookupProgramMethod(emulatedInterfaceMethod);
     }
+    // The holder is not used.
+    DexMethod emulatedInterfaceMethod =
+        emulatedInterfaceDispatchMethod(
+            emulatedDispatchMethod, appView.dexItemFactory().objectType);
     return appView
         .getSyntheticItems()
         .ensureFixedClasspathClassMethod(
@@ -511,7 +577,7 @@
       return appView.rewritePrefix.hasRewrittenType(type, appView)
           || descriptor.endsWith(companionClassNameDescriptorSuffix)
           || isRewrittenEmulatedInterface(type)
-          || options.desugaredLibrarySpecification.getCustomConversions().containsValue(type)
+          || options.machineDesugaredLibrarySpecification.isCustomConversionRewrittenType(type)
           || appView.getDontWarnConfiguration().matches(type);
     };
   }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
index 6740441..0227b1f 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
@@ -35,7 +35,8 @@
 import com.android.tools.r8.ir.desugar.DesugarDescription;
 import com.android.tools.r8.ir.desugar.FreshLocalProvider;
 import com.android.tools.r8.ir.desugar.LocalStackAllocator;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.DerivedMethod;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
 import com.android.tools.r8.ir.desugar.icce.AlwaysThrowingInstructionDesugaring;
 import com.android.tools.r8.ir.desugar.lambda.LambdaInstructionDesugaring;
 import com.android.tools.r8.ir.desugar.stringconcat.StringConcatInstructionDesugaring;
@@ -53,7 +54,6 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
-import java.util.function.BiConsumer;
 import java.util.function.Predicate;
 
 //
@@ -119,18 +119,31 @@
   }
 
   public static void checkForAssumedLibraryTypes(AppInfo appInfo, InternalOptions options) {
-    LegacyDesugaredLibrarySpecification spec = options.desugaredLibrarySpecification;
-    BiConsumer<DexType, DexType> registerEntry = registerMapEntry(appInfo);
-    spec.getEmulateLibraryInterface().forEach(registerEntry);
-    spec.getCustomConversions().forEach(registerEntry);
-    spec.getRetargetCoreLibMember().forEach((method, types) -> types.forEach(registerEntry));
-  }
-
-  private static BiConsumer<DexType, DexType> registerMapEntry(AppInfo appInfo) {
-    return (key, value) -> {
-      registerType(appInfo, key);
-      registerType(appInfo, value);
-    };
+    MachineDesugaredLibrarySpecification machineDesugaredLibrarySpecification =
+        options.machineDesugaredLibrarySpecification;
+    machineDesugaredLibrarySpecification
+        .getEmulatedInterfaces()
+        .forEach(
+            (ei, descriptor) -> {
+              registerType(appInfo, ei);
+              registerType(appInfo, descriptor.getRewrittenType());
+              descriptor
+                  .getEmulatedMethods()
+                  .forEach(
+                      (method, methodDescriptor) -> {
+                        methodDescriptor
+                            .getDispatchCases()
+                            .keySet()
+                            .forEach(t -> registerType(appInfo, t));
+                      });
+            });
+    machineDesugaredLibrarySpecification
+        .getCustomConversions()
+        .forEach(
+            (type, conversionPair) -> {
+              registerType(appInfo, type);
+              registerType(appInfo, conversionPair.getFirst());
+            });
   }
 
   private static void registerType(AppInfo appInfo, DexType type) {
@@ -156,9 +169,9 @@
   }
 
   private void initializeEmulatedInterfaceVariables() {
-    Map<DexType, DexType> emulateLibraryInterface =
-        options.desugaredLibrarySpecification.getEmulateLibraryInterface();
-    for (DexType interfaceType : emulateLibraryInterface.keySet()) {
+    Set<DexType> emulateLibraryInterface =
+        options.machineDesugaredLibrarySpecification.getEmulatedInterfaces().keySet();
+    for (DexType interfaceType : emulateLibraryInterface) {
       DexClass emulatedInterfaceClass = appView.definitionFor(interfaceType);
       if (emulatedInterfaceClass != null) {
         for (DexEncodedMethod encodedMethod :
@@ -449,24 +462,27 @@
   }
 
   private DesugarDescription computeEmulatedInterfaceVirtualDispatchOrNull(CfInvoke invoke) {
-    DexClassAndMethod defaultMethod =
-        defaultMethodForEmulatedDispatchOrNull(invoke.getMethod(), invoke.isInterface());
-    if (defaultMethod != null) {
-      return DesugarDescription.builder()
-          .setDesugarRewrite(
-              (freshLocalProvider,
-                  localStackAllocator,
-                  eventConsumer,
-                  context1,
-                  methodProcessingContext,
-                  dexItemFactory) ->
-                  getInvokeStaticInstructions(
-                      helper
-                          .ensureEmulatedInterfaceMethod(defaultMethod, eventConsumer)
-                          .getReference()))
-          .build();
+    MethodResolutionResult resolutionResult =
+        appView.appInfoForDesugaring().resolveMethod(invoke.getMethod(), invoke.isInterface());
+    DerivedMethod emulatedDispatchMethod =
+        helper.computeEmulatedInterfaceDispatchMethod(resolutionResult);
+    if (emulatedDispatchMethod == null) {
+      return null;
     }
-    return null;
+    return DesugarDescription.builder()
+        .setDesugarRewrite(
+            (freshLocalProvider,
+                localStackAllocator,
+                eventConsumer,
+                context1,
+                methodProcessingContext,
+                dexItemFactory) ->
+                getInvokeStaticInstructions(
+                    helper
+                        .ensureEmulatedInterfaceDispatchMethod(
+                            emulatedDispatchMethod, eventConsumer)
+                        .getReference()))
+        .build();
   }
 
   private DesugarDescription computeInvokeDirect(
@@ -704,103 +720,49 @@
 
   private DesugarDescription computeEmulatedInterfaceInvokeSpecial(
       DexClass clazz, DexMethod invokedMethod, ProgramMethod context) {
-    DexType emulatedItf = maximallySpecificEmulatedInterfaceOrNull(invokedMethod);
-    if (emulatedItf == null) {
-      if (clazz.isInterface() && appView.rewritePrefix.hasRewrittenType(clazz.type, appView)) {
-        DexClassAndMethod target =
-            appView.appInfoForDesugaring().lookupSuperTarget(invokedMethod, context);
-        if (target != null && target.getDefinition().isDefaultMethod()) {
-          DexClass holder = target.getHolder();
-          if (holder.isLibraryClass() && holder.isInterface()) {
-            return DesugarDescription.builder()
-                .setDesugarRewrite(
-                    (freshLocalProvider,
-                        localStackAllocator,
-                        eventConsumer,
-                        context13,
-                        methodProcessingContext,
-                        dexItemFactory) -> {
-                      DexClassAndMethod companionTarget =
-                          helper.ensureDefaultAsMethodOfCompanionClassStub(target);
-                      acceptCompanionMethod(target, companionTarget, eventConsumer);
-                      return getInvokeStaticInstructions(companionTarget.getReference());
-                    })
-                .build();
-          }
+    DexClassAndMethod superTarget =
+        appView.appInfoForDesugaring().lookupSuperTarget(invokedMethod, context);
+    if (clazz.isInterface() && appView.rewritePrefix.hasRewrittenType(clazz.type, appView)) {
+      if (superTarget != null && superTarget.getDefinition().isDefaultMethod()) {
+        DexClass holder = superTarget.getHolder();
+        if (holder.isLibraryClass() && holder.isInterface()) {
+          return DesugarDescription.builder()
+              .setDesugarRewrite(
+                  (freshLocalProvider,
+                      localStackAllocator,
+                      eventConsumer,
+                      context13,
+                      methodProcessingContext,
+                      dexItemFactory) -> {
+                    DexClassAndMethod companionTarget =
+                        helper.ensureDefaultAsMethodOfCompanionClassStub(superTarget);
+                    acceptCompanionMethod(superTarget, companionTarget, eventConsumer);
+                    return getInvokeStaticInstructions(companionTarget.getReference());
+                  })
+              .build();
         }
       }
-      return DesugarDescription.nothing();
     }
     // That invoke super may not resolve since the super method may not be present
     // since it's in the emulated interface. We need to force resolution. If it resolves
     // to a library method, then it needs to be rewritten.
     // If it resolves to a program overrides, the invoke-super can remain.
-    DexClassAndMethod superTarget =
-        appView.appInfoForDesugaring().lookupSuperTarget(invokedMethod, context);
-    if (superTarget != null && superTarget.isLibraryMethod()) {
-      // Rewriting is required because the super invoke resolves into a missing
-      // method (method is on desugared library). Find out if it needs to be
-      // retargeted or if it just calls a companion class method and rewrite.
-      DexMethod retargetMethod =
-          options.desugaredLibrarySpecification.retargetMethod(superTarget, appView);
-      if (retargetMethod != null) {
-        return DesugarDescription.builder()
-            .setDesugarRewrite(
-                (freshLocalProvider,
-                    localStackAllocator,
-                    eventConsumer,
-                    context14,
-                    methodProcessingContext,
-                    dexItemFactory) -> getInvokeStaticInstructions(retargetMethod))
-            .build();
-      }
-      DexClassAndMethod emulatedMethod =
-          superTarget.getReference().lookupMemberOnClass(appView.definitionFor(emulatedItf));
-      if (emulatedMethod == null) {
-        assert false;
-        return DesugarDescription.nothing();
-      }
-      return DesugarDescription.builder()
-          .setDesugarRewrite(
-              (freshLocalProvider,
-                  localStackAllocator,
-                  eventConsumer,
-                  context15,
-                  methodProcessingContext,
-                  dexItemFactory) -> {
-                DexClassAndMethod companionMethod =
-                    helper.ensureDefaultAsMethodOfCompanionClassStub(emulatedMethod);
-                return getInvokeStaticInstructions(companionMethod.getReference());
-              })
-          .build();
+    DerivedMethod forwardingMethod =
+        helper.computeEmulatedInterfaceForwardingMethod(clazz, superTarget);
+    if (forwardingMethod == null) {
+      return DesugarDescription.nothing();
     }
-    return DesugarDescription.nothing();
-  }
-
-  private DexClassAndMethod defaultMethodForEmulatedDispatchOrNull(
-      DexMethod invokedMethod, boolean interfaceBit) {
-    DexType emulatedItf = maximallySpecificEmulatedInterfaceOrNull(invokedMethod);
-    if (emulatedItf == null) {
-      return null;
-    }
-    // The call potentially ends up in a library class, in which case we need to rewrite, since the
-    // code may be in the desugared library.
-    SingleResolutionResult resolution =
-        appView
-            .appInfoForDesugaring()
-            .resolveMethod(invokedMethod, interfaceBit)
-            .asSingleResolution();
-    if (resolution != null
-        && (resolution.getResolvedHolder().isLibraryClass()
-            || helper.isEmulatedInterface(resolution.getResolvedHolder().type))) {
-      DexClassAndMethod defaultMethod =
-          appView.definitionFor(emulatedItf).lookupClassMethod(invokedMethod);
-      if (defaultMethod != null && !helper.dontRewrite(defaultMethod)) {
-        assert !defaultMethod.getAccessFlags().isAbstract();
-        return defaultMethod;
-      }
-    }
-    return null;
+    return DesugarDescription.builder()
+        .setDesugarRewrite(
+            (freshLocalProvider,
+                localStackAllocator,
+                eventConsumer,
+                context14,
+                methodProcessingContext,
+                dexItemFactory) ->
+                getInvokeStaticInstructions(
+                    helper.ensureEmulatedInterfaceForwardingMethod(forwardingMethod)))
+        .build();
   }
 
   private boolean shouldRewriteToInvokeToThrow(
@@ -809,40 +771,6 @@
         || resolutionResult.getResolvedMethod().isStatic() != isInvokeStatic;
   }
 
-  private DexType maximallySpecificEmulatedInterfaceOrNull(DexMethod invokedMethod) {
-    // Here we try to avoid doing the expensive look-up on all invokes.
-    if (!emulatedMethods.contains(invokedMethod.name)) {
-      return null;
-    }
-    DexClass dexClass = appView.definitionFor(invokedMethod.holder);
-    // We cannot rewrite the invoke we do not know what the class is.
-    if (dexClass == null) {
-      return null;
-    }
-    DexEncodedMethod singleTarget = null;
-    if (dexClass.isInterface()) {
-      // Look for exact method on the interface.
-      singleTarget = dexClass.lookupMethod(invokedMethod);
-    }
-    if (singleTarget == null) {
-      DexClassAndMethod result =
-          appView.appInfoForDesugaring().lookupMaximallySpecificMethod(dexClass, invokedMethod);
-      if (result != null) {
-        singleTarget = result.getDefinition();
-      }
-    }
-    if (singleTarget == null) {
-      // At this point we are in a library class. Failures can happen with NoSuchMethod if a
-      // library class implement a method with same signature but not related to emulated
-      // interfaces.
-      return null;
-    }
-    if (!singleTarget.isAbstract() && helper.isEmulatedInterface(singleTarget.getHolderType())) {
-      return singleTarget.getHolderType();
-    }
-    return null;
-  }
-
   private boolean isNonDesugaredLibraryClass(DexClass clazz) {
     return clazz.isLibraryClass() && !helper.isInDesugaredLibrary(clazz);
   }
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 f712cb4..7e8a2cc 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
@@ -5,11 +5,9 @@
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexProto;
-import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -25,27 +23,16 @@
 import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
 import com.android.tools.r8.synthesis.SyntheticProgramClassBuilder;
 import com.android.tools.r8.utils.StringDiagnostic;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Sets;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.IdentityHashMap;
 import java.util.LinkedHashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
 
 public final class ProgramEmulatedInterfaceSynthesizer implements CfClassSynthesizerDesugaring {
 
   private final AppView<?> appView;
   private final InterfaceDesugaringSyntheticHelper helper;
-  private final Map<DexType, List<DexType>> emulatedInterfacesHierarchy;
 
   public static ProgramEmulatedInterfaceSynthesizer create(AppView<?> appView) {
     if (!appView.options().isDesugaredLibraryCompilation()
-        || appView.options().desugaredLibrarySpecification.getEmulateLibraryInterface().isEmpty()) {
+        || !appView.options().machineDesugaredLibrarySpecification.hasEmulatedInterfaces()) {
       return null;
     }
     return new ProgramEmulatedInterfaceSynthesizer(appView);
@@ -54,53 +41,11 @@
   public ProgramEmulatedInterfaceSynthesizer(AppView<?> appView) {
     this.appView = appView;
     helper = new InterfaceDesugaringSyntheticHelper(appView);
-    // Avoid the computation outside L8 since it is not needed.
-    emulatedInterfacesHierarchy =
-        appView.options().isDesugaredLibraryCompilation()
-            ? processEmulatedInterfaceHierarchy()
-            : Collections.emptyMap();
-  }
-
-  private Map<DexType, List<DexType>> processEmulatedInterfaceHierarchy() {
-    Map<DexType, List<DexType>> emulatedInterfacesHierarchy = new IdentityHashMap<>();
-    Set<DexType> processed = Sets.newIdentityHashSet();
-    ArrayList<DexType> emulatedInterfacesSorted = new ArrayList<>(helper.getEmulatedInterfaces());
-    emulatedInterfacesSorted.sort(DexType::compareTo);
-    for (DexType interfaceType : emulatedInterfacesSorted) {
-      processEmulatedInterfaceHierarchy(interfaceType, processed, emulatedInterfacesHierarchy);
-    }
-    return emulatedInterfacesHierarchy;
-  }
-
-  private void processEmulatedInterfaceHierarchy(
-      DexType interfaceType,
-      Set<DexType> processed,
-      Map<DexType, List<DexType>> emulatedInterfacesHierarchy) {
-    if (processed.contains(interfaceType)) {
-      return;
-    }
-    emulatedInterfacesHierarchy.put(interfaceType, new ArrayList<>());
-    processed.add(interfaceType);
-    DexClass theInterface = appView.definitionFor(interfaceType);
-    if (theInterface == null) {
-      return;
-    }
-    LinkedList<DexType> workList = new LinkedList<>(Arrays.asList(theInterface.interfaces.values));
-    while (!workList.isEmpty()) {
-      DexType next = workList.removeLast();
-      if (helper.isEmulatedInterface(next)) {
-        processEmulatedInterfaceHierarchy(next, processed, emulatedInterfacesHierarchy);
-        emulatedInterfacesHierarchy.get(next).add(interfaceType);
-        DexClass nextClass = appView.definitionFor(next);
-        if (nextClass != null) {
-          workList.addAll(Arrays.asList(nextClass.interfaces.values));
-        }
-      }
-    }
   }
 
   DexProgramClass synthesizeProgramEmulatedInterface(
       DexProgramClass emulatedInterface,
+      EmulatedInterfaceDescriptor emulatedInterfaceDescriptor,
       L8ProgramEmulatedInterfaceSynthesizerEventConsumer eventConsumer) {
     return appView
         .getSyntheticItems()
@@ -108,20 +53,26 @@
             SyntheticNaming.SyntheticKind.EMULATED_INTERFACE_CLASS,
             emulatedInterface,
             appView,
-            builder -> synthesizeEmulateInterfaceMethods(emulatedInterface, builder),
+            builder ->
+                synthesizeEmulateInterfaceMethods(
+                    emulatedInterface, emulatedInterfaceDescriptor, builder),
             eventConsumer::acceptProgramEmulatedInterface);
   }
 
   private void synthesizeEmulateInterfaceMethods(
-      DexProgramClass emulatedInterface, SyntheticProgramClassBuilder builder) {
-    assert helper.isEmulatedInterface(emulatedInterface.type);
+      DexProgramClass emulatedInterface,
+      EmulatedInterfaceDescriptor emulatedInterfaceDescriptor,
+      SyntheticProgramClassBuilder builder) {
     emulatedInterface.forEachProgramVirtualMethodMatching(
-        DexEncodedMethod::isDefaultMethod,
+        m -> emulatedInterfaceDescriptor.getEmulatedMethods().containsKey(m.getReference()),
         method ->
             builder.addMethod(
                 methodBuilder ->
                     synthesizeEmulatedInterfaceMethod(
-                        method, emulatedInterface, builder.getType(), methodBuilder)));
+                        method,
+                        emulatedInterfaceDescriptor.getEmulatedMethods().get(method.getReference()),
+                        builder.getType(),
+                        methodBuilder)));
   }
 
   private DexMethod emulatedMethod(DerivedMethod method, DexType holder) {
@@ -137,24 +88,17 @@
 
   private void synthesizeEmulatedInterfaceMethod(
       ProgramMethod method,
-      DexProgramClass theInterface,
+      EmulatedDispatchMethodDescriptor descriptor,
       DexType dispatchType,
       SyntheticMethodBuilder methodBuilder) {
     assert !method.getDefinition().isStatic();
-    if (appView.options().testing.machineDesugaredLibrarySpecification != null) {
-      synthesizeEmulatedInterfaceMethodFromMachineSpecification(
-          method, theInterface, dispatchType, methodBuilder);
-      return;
-    }
-    DexMethod emulatedMethod = helper.emulateInterfaceLibraryMethod(method);
-    DexMethod itfMethod =
-        method
-            .getReference()
-            .withHolder(helper.getEmulatedInterface(theInterface.type), appView.dexItemFactory());
+    DexMethod emulatedMethod =
+        helper.emulatedInterfaceDispatchMethod(
+            descriptor.getEmulatedDispatchMethod(), dispatchType);
+    DexMethod itfMethod = helper.emulatedInterfaceInterfaceMethod(descriptor.getInterfaceMethod());
     DexMethod companionMethod =
-        helper.ensureDefaultAsMethodOfProgramCompanionClassStub(method).getReference();
-    LinkedHashMap<DexType, DexMethod> extraDispatchCases =
-        getDispatchCases(method, theInterface, companionMethod);
+        helper.ensureEmulatedInterfaceForwardingMethod(descriptor.getForwardingMethod());
+    LinkedHashMap<DexType, DexMethod> extraDispatchCases = resolveDispatchCases(descriptor);
     methodBuilder
         .setName(emulatedMethod.getName())
         .setProto(emulatedMethod.getProto())
@@ -170,162 +114,40 @@
                     .generateCfCode());
   }
 
-  private void synthesizeEmulatedInterfaceMethodFromMachineSpecification(
-      ProgramMethod method,
-      DexProgramClass theInterface,
-      DexType dispatchType,
-      SyntheticMethodBuilder methodBuilder) {
-    EmulatedInterfaceDescriptor emulatedInterfaceDescriptor =
-        appView
-            .options()
-            .testing
-            .machineDesugaredLibrarySpecification
-            .getRewritingFlags()
-            .getEmulatedInterfaces()
-            .get(theInterface.type);
-    EmulatedDispatchMethodDescriptor descriptor =
-        emulatedInterfaceDescriptor.getEmulatedMethods().get(method.getReference());
-    DexMethod emulatedMethod = emulatedMethod(descriptor.getEmulatedDispatchMethod(), dispatchType);
-    DexMethod itfMethod = interfaceMethod(descriptor.getInterfaceMethod());
-    // TODO(b/184026720): Adapt to use the forwarding method.
-    DerivedMethod forwardingMethod = descriptor.getForwardingMethod();
-    assert forwardingMethod.getHolderKind() == SyntheticKind.COMPANION_CLASS;
-    assert forwardingMethod.getMethod() == method.getReference();
-    DexMethod companionMethod =
-        helper.ensureDefaultAsMethodOfProgramCompanionClassStub(method).getReference();
-    LinkedHashMap<DexType, DexMethod> extraDispatchCases = resolveDispatchCases(descriptor);
-    methodBuilder
-        .setName(descriptor.getEmulatedDispatchMethod().getName())
-        .setProto(descriptor.getEmulatedDispatchMethod().getProto())
-        .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
-        .setCode(
-            emulatedInterfaceMethod ->
-                new EmulateDispatchSyntheticCfCodeProvider(
-                        emulatedMethod.getHolderType(),
-                        companionMethod,
-                        itfMethod,
-                        extraDispatchCases,
-                        appView)
-                    .generateCfCode());
-  }
-
   private LinkedHashMap<DexType, DexMethod> resolveDispatchCases(
       EmulatedDispatchMethodDescriptor descriptor) {
     LinkedHashMap<DexType, DexMethod> extraDispatchCases = new LinkedHashMap<>();
     descriptor
         .getDispatchCases()
         .forEach(
-            (type, derivedMethod) -> {
-              DexMethod caseMethod;
-              if (derivedMethod.getHolderKind() == null) {
-                caseMethod = derivedMethod.getMethod();
-              } else {
-                assert derivedMethod.getHolderKind() == SyntheticKind.COMPANION_CLASS;
-                ProgramMethod resolvedProgramMethod =
-                    appView
-                        .appInfoForDesugaring()
-                        .resolveMethod(derivedMethod.getMethod(), true)
-                        .getResolvedProgramMethod();
-                caseMethod =
-                    helper
-                        .ensureDefaultAsMethodOfProgramCompanionClassStub(resolvedProgramMethod)
-                        .getReference();
-              }
-              extraDispatchCases.put(type, caseMethod);
-            });
+            (type, derivedMethod) ->
+                extraDispatchCases.put(
+                    type, helper.ensureEmulatedInterfaceForwardingMethod(derivedMethod)));
     return extraDispatchCases;
   }
 
-  private LinkedHashMap<DexType, DexMethod> getDispatchCases(
-      ProgramMethod method, DexProgramClass theInterface, DexMethod companionMethod) {
-    // To properly emulate the library interface call, we need to compute the interfaces
-    // inheriting from the interface and manually implement the dispatch with instance of.
-    // The list guarantees that an interface is always after interfaces it extends,
-    // hence reverse iteration.
-    List<DexType> subInterfaces = emulatedInterfacesHierarchy.get(theInterface.type);
-    LinkedHashMap<DexType, DexMethod> extraDispatchCases = new LinkedHashMap<>();
-    // In practice, there is usually a single case (except for tests),
-    // so we do not bother to make the following loop more clever.
-    Map<DexString, Map<DexType, DexType>> retargetCoreLibMember =
-        appView.options().desugaredLibrarySpecification.getRetargetCoreLibMember();
-    for (DexString methodName : retargetCoreLibMember.keySet()) {
-      if (method.getName() == methodName) {
-        for (DexType inType : retargetCoreLibMember.get(methodName).keySet()) {
-          DexClass inClass = appView.definitionFor(inType);
-          if (inClass != null && implementsInterface(inClass, theInterface.type)) {
-            extraDispatchCases.put(
-                inType,
-                appView
-                    .dexItemFactory()
-                    .createMethod(
-                        retargetCoreLibMember.get(methodName).get(inType),
-                        appView
-                            .dexItemFactory()
-                            .protoWithDifferentFirstParameter(companionMethod.proto, inType),
-                        method.getName()));
-          }
-        }
-      }
-    }
-    if (subInterfaces != null) {
-      for (int i = subInterfaces.size() - 1; i >= 0; i--) {
-        DexClass subInterfaceClass = appView.definitionFor(subInterfaces.get(i));
-        assert subInterfaceClass != null;
-        assert subInterfaceClass.isProgramClass();
-        // Else computation of subInterface would have failed.
-        // if the method is implemented, extra dispatch is required.
-        DexEncodedMethod result = subInterfaceClass.lookupVirtualMethod(method.getReference());
-        if (result != null && !result.isAbstract()) {
-          assert result.isDefaultMethod();
-          DexMethod forward =
-              helper
-                  .ensureDefaultAsMethodOfProgramCompanionClassStub(
-                      new ProgramMethod(subInterfaceClass.asProgramClass(), result))
-                  .getReference();
-          extraDispatchCases.put(subInterfaceClass.type, forward);
-        }
-      }
-    } else {
-      assert extraDispatchCases.size() <= 1;
-    }
-    return extraDispatchCases;
-  }
-
-  private boolean implementsInterface(DexClass clazz, DexType interfaceType) {
-    LinkedList<DexType> workList = new LinkedList<>(Arrays.asList(clazz.interfaces.values));
-    while (!workList.isEmpty()) {
-      DexType next = workList.removeLast();
-      if (interfaceType == next) {
-        return true;
-      }
-      DexClass nextClass = appView.definitionFor(next);
-      if (nextClass != null) {
-        workList.addAll(Arrays.asList(nextClass.interfaces.values));
-      }
-    }
-    return false;
-  }
-
   @Override
   public void synthesizeClasses(CfClassSynthesizerDesugaringEventConsumer eventConsumer) {
     assert appView.options().isDesugaredLibraryCompilation();
-    for (DexType emulatedInterfaceType : helper.getEmulatedInterfaces()) {
-      DexClass emulatedInterfaceClazz = appView.definitionFor(emulatedInterfaceType);
-      if (emulatedInterfaceClazz == null || !emulatedInterfaceClazz.isProgramClass()) {
-        warnMissingEmulatedInterface(emulatedInterfaceType);
-        continue;
-      }
-      DexProgramClass emulatedInterface = emulatedInterfaceClazz.asProgramClass();
-      assert emulatedInterface != null;
-      if (!appView.isAlreadyLibraryDesugared(emulatedInterface)
-          && needsEmulateInterfaceLibrary(emulatedInterface)) {
-        synthesizeProgramEmulatedInterface(emulatedInterface, eventConsumer);
-      }
-    }
-  }
-
-  private boolean needsEmulateInterfaceLibrary(DexClass emulatedInterface) {
-    return Iterables.any(emulatedInterface.methods(), DexEncodedMethod::isDefaultMethod);
+    appView
+        .options()
+        .machineDesugaredLibrarySpecification
+        .getEmulatedInterfaces()
+        .forEach(
+            (emulatedInterfaceType, emulatedInterfaceDescriptor) -> {
+              DexClass emulatedInterfaceClazz = appView.definitionFor(emulatedInterfaceType);
+              if (emulatedInterfaceClazz == null || !emulatedInterfaceClazz.isProgramClass()) {
+                warnMissingEmulatedInterface(emulatedInterfaceType);
+                return;
+              }
+              DexProgramClass emulatedInterface = emulatedInterfaceClazz.asProgramClass();
+              assert emulatedInterface != null;
+              if (!appView.isAlreadyLibraryDesugared(emulatedInterface)
+                  && !emulatedInterfaceDescriptor.getEmulatedMethods().isEmpty()) {
+                synthesizeProgramEmulatedInterface(
+                    emulatedInterface, emulatedInterfaceDescriptor, eventConsumer);
+              }
+            });
   }
 
   private void warnMissingEmulatedInterface(DexType interfaceType) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/AssertionsRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/AssertionsRewriter.java
index ba2c90f..0da7886 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/AssertionsRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/AssertionsRewriter.java
@@ -12,19 +12,32 @@
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.DominatorTree;
+import com.android.tools.r8.ir.code.FieldInstruction;
+import com.android.tools.r8.ir.code.Goto;
 import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.If;
 import com.android.tools.r8.ir.code.Instruction;
 import com.android.tools.r8.ir.code.InstructionListIterator;
 import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.InvokeStatic;
 import com.android.tools.r8.ir.code.StaticGet;
 import com.android.tools.r8.ir.code.StaticPut;
+import com.android.tools.r8.ir.code.Throw;
+import com.android.tools.r8.references.MethodReference;
 import com.android.tools.r8.utils.AssertionConfigurationWithDefault;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.LazyBox;
 import com.android.tools.r8.utils.ThrowingCharIterator;
 import com.android.tools.r8.utils.Timing;
+import com.android.tools.r8.utils.WorkList;
+import com.google.common.collect.ImmutableList;
 import java.io.UTFDataFormatException;
+import java.util.IdentityHashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.stream.Collectors;
 
 public class AssertionsRewriter {
@@ -72,11 +85,11 @@
       }
     }
 
-    public boolean isEnabled() {
+    public boolean isCompileTimeEnabled() {
       return entry.isCompileTimeEnabled();
     }
 
-    public boolean isDisabled() {
+    public boolean isCompileTimeDisabled() {
       return entry.isCompileTimeDisabled();
     }
 
@@ -87,6 +100,11 @@
     public boolean isAssertionHandler() {
       return entry.isAssertionHandler();
     }
+
+    public MethodReference getAssertionHandler() {
+      assert isAssertionHandler();
+      return entry.getAssertionHandler();
+    }
   }
 
   private final AppView<?> appView;
@@ -322,6 +340,7 @@
     if (enabled) {
       timing.begin("Rewrite assertions");
       runInternal(method, code);
+      assert code.isConsistentSSA();
       timing.end();
     }
   }
@@ -343,6 +362,39 @@
       }
       clinit = clazz.getClassInitializer();
     }
+    // For the transformation to rewrite the throw with a callback collect information on the
+    // blocks covered by the if (!$assertionsDisabled or ENABLED) condition together with weather
+    // the assertion handling is on the true or false branch.
+    Map<If, Boolean> assertionEntryIfs = new IdentityHashMap<>();
+    Map<Throw, BasicBlock> throwSuccessorAfterHandler = new IdentityHashMap<>();
+    if (configuration.isAssertionHandler()) {
+      LazyBox<DominatorTree> dominatorTree = new LazyBox<>(() -> new DominatorTree(code));
+      code.getBlocks()
+          .forEach(
+              basicBlock -> {
+                If theIf = isCheckAssertionsEnabledBlock(basicBlock);
+                if (theIf != null) {
+                  // All blocks dominated by the if is the assertion code. For Java it is on the
+                  // false branch and for Kotlin on the true branch (for Java it is negated
+                  // $assertionsDisabled field and for Kotlin it is the ENABLED field).
+                  boolean conditionForAssertionBlock =
+                      !isUsingJavaAssertionsDisabledField(
+                          theIf.lhs().getDefinition().asStaticGet());
+                  BasicBlock assertionBlockEntry =
+                      theIf.targetFromBoolean(conditionForAssertionBlock);
+                  List<BasicBlock> blocks =
+                      dominatorTree.computeIfAbsent().dominatedBlocks(assertionBlockEntry);
+                  Throw throwInstruction = isAlwaysThrowingEntry(assertionBlockEntry, blocks);
+                  if (throwInstruction != null) {
+                    assertionEntryIfs.put(theIf, conditionForAssertionBlock);
+                    throwSuccessorAfterHandler.put(
+                        throwInstruction, theIf.targetFromBoolean(!conditionForAssertionBlock));
+                  }
+                }
+              });
+    }
+    assert assertionEntryIfs.size() == throwSuccessorAfterHandler.size();
+
     // For javac generated code it is assumed that the code in <clinit> will tell if the code
     // in other methods of the class can have assertion checks.
     boolean isInitializerEnablingJavaVmAssertions =
@@ -363,22 +415,57 @@
       } else if (current.isStaticPut()) {
         StaticPut staticPut = current.asStaticPut();
         if (isInitializerEnablingJavaVmAssertions
-            && staticPut.getField().name == dexItemFactory.assertionsDisabled) {
+            && isUsingJavaAssertionsDisabledField(staticPut)) {
           iterator.remove();
         }
       } else if (current.isStaticGet()) {
         StaticGet staticGet = current.asStaticGet();
         // Rewrite $assertionsDisabled getter (only if the initializer enabled assertions).
         if (isInitializerEnablingJavaVmAssertions
-            && staticGet.getField().name == dexItemFactory.assertionsDisabled) {
-          iterator.replaceCurrentInstruction(
-              code.createIntConstant(configuration.isDisabled() ? 1 : 0, current.getLocalInfo()));
+            && isUsingJavaAssertionsDisabledField(staticGet)) {
+          // For assertion handler rewrite just leave the static get, as it will become dead code.
+          if (!configuration.isAssertionHandler()) {
+            iterator.replaceCurrentInstruction(
+                code.createIntConstant(
+                    configuration.isCompileTimeDisabled() ? 1 : 0, current.getLocalInfo()));
+          }
         }
         // Rewrite kotlin._Assertions.ENABLED getter.
         if (staticGet.getField() == dexItemFactory.kotlin.assertions.enabledField) {
-          iterator.replaceCurrentInstruction(
-              code.createIntConstant(
-                  kotlinTransformation.isDisabled() ? 0 : 1, current.getLocalInfo()));
+          // For assertion handler rewrite just leave the static get, as it will become dead code.
+          if (!configuration.isAssertionHandler()) {
+            iterator.replaceCurrentInstruction(
+                code.createIntConstant(
+                    kotlinTransformation.isCompileTimeDisabled() ? 0 : 1, current.getLocalInfo()));
+          }
+        }
+      }
+
+      // Rewriting of if and throw to replace throw with invoke of the assertion handler.
+      if (configuration.isAssertionHandler()) {
+        if (current.isIf()) {
+          If ifInstruction = current.asIf();
+          if (assertionEntryIfs.containsKey(ifInstruction)) {
+            ifInstruction
+                .targetFromBoolean(!assertionEntryIfs.get(ifInstruction))
+                .unlinkSinglePredecessorSiblingsAllowed();
+            ifInstruction.lhs().removeUser(ifInstruction);
+            iterator.replaceCurrentInstruction(new Goto());
+          }
+        } else if (current.isThrow()) {
+          Throw throwInstruction = current.asThrow();
+          if (throwSuccessorAfterHandler.containsKey(throwInstruction)) {
+            BasicBlock throwingBlock = throwInstruction.getBlock();
+            iterator.replaceCurrentInstruction(
+                new InvokeStatic(
+                    dexItemFactory.createMethod(configuration.getAssertionHandler()),
+                    null,
+                    ImmutableList.of(throwInstruction.exception())));
+            Goto gotoBlockAfterAssertion = new Goto(throwingBlock);
+            gotoBlockAfterAssertion.setPosition(throwInstruction.getPosition());
+            throwingBlock.link(throwSuccessorAfterHandler.get(throwInstruction));
+            iterator.add(gotoBlockAfterAssertion);
+          }
         }
       }
     }
@@ -389,7 +476,7 @@
       ConfigurationEntryWithDexString configuration,
       InstructionListIterator iterator,
       InvokeMethod invoke) {
-    if (iterator.hasNext() && configuration.isDisabled()) {
+    if (iterator.hasNext() && configuration.isCompileTimeDisabled()) {
       // Check if the invocation of Class.desiredAssertionStatus() is followed by a static
       // put to kotlin._Assertions.ENABLED, and if so remove both instructions.
       // See comment in ClassInitializerAssertionEnablingAnalysis for the expected instruction
@@ -416,7 +503,63 @@
         iterator.replaceCurrentInstruction(code.createIntConstant(0));
       }
     } else {
-      iterator.replaceCurrentInstruction(code.createIntConstant(configuration.isEnabled() ? 1 : 0));
+      iterator.replaceCurrentInstruction(
+          code.createIntConstant(configuration.isCompileTimeEnabled() ? 1 : 0));
     }
   }
+
+  private boolean isUsingAssertionsControlField(FieldInstruction instruction) {
+    return isUsingJavaAssertionsDisabledField(instruction)
+        || isUsingKotlinAssertionsEnabledField(instruction);
+  }
+
+  private boolean isUsingJavaAssertionsDisabledField(FieldInstruction instruction) {
+    // This does not check the holder, as for inner classe the field is read from the outer class
+    // and not the class itself.
+    return instruction.getField().getName() == dexItemFactory.assertionsDisabled
+        && instruction.getField().getType() == dexItemFactory.booleanType;
+  }
+
+  private boolean isUsingKotlinAssertionsEnabledField(FieldInstruction instruction) {
+    return instruction.getField() == dexItemFactory.kotlin.assertions.enabledField;
+  }
+
+  private If isCheckAssertionsEnabledBlock(BasicBlock basicBlock) {
+    if (!basicBlock.exit().isIf()) {
+      return null;
+    }
+    If theIf = basicBlock.exit().asIf();
+    if (!theIf.isZeroTest()
+        || !theIf.lhs().isDefinedByInstructionSatisfying(Instruction::isStaticGet)) {
+      return null;
+    }
+    StaticGet staticGet = theIf.lhs().getDefinition().asStaticGet();
+    return isUsingAssertionsControlField(staticGet)
+            && staticGet.value().hasSingleUniqueUser()
+            && !staticGet.value().hasPhiUsers()
+        ? theIf
+        : null;
+  }
+
+  private Throw isAlwaysThrowingEntry(BasicBlock block, List<BasicBlock> blocks) {
+    WorkList<BasicBlock> workList = WorkList.newIdentityWorkList(block);
+    Throw theThrow = null;
+    while (workList.hasNext()) {
+      BasicBlock current = workList.next();
+      workList.addIfNotSeen(current.getNormalSuccessors());
+      if (!blocks.containsAll(current.getNormalSuccessors())) {
+        return null;
+      }
+      if (current.exit().isReturn()) {
+        return null;
+      }
+      if (current.exit().isThrow()) {
+        if (theThrow != null) {
+          return null;
+        }
+        theThrow = current.exit().asThrow();
+      }
+    }
+    return theThrow;
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index a658752..fba3991 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -10,7 +10,6 @@
 
 import com.android.tools.r8.androidapi.AvailableApiExceptions;
 import com.android.tools.r8.graph.AccessFlags;
-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.DexClassAndMethod;
@@ -544,9 +543,8 @@
     }
 
     InlineeWithReason buildInliningIR(
-        AppView<? extends AppInfoWithClassHierarchy> appView,
+        AppView<AppInfoWithLiveness> appView,
         InvokeMethod invoke,
-        ProgramMethod context,
         InliningIRProvider inliningIRProvider,
         LensCodeRewriter lensCodeRewriter) {
       DexItemFactory dexItemFactory = appView.dexItemFactory();
@@ -715,12 +713,10 @@
 
     private void synthesizeInitClass(IRCode code) {
       List<Value> arguments = code.collectArguments();
-      BasicBlock entryBlock = code.entryBlock();
-
+      BasicBlock block = code.entryBlock();
       // Insert a new block between the last argument instruction and the first actual instruction
       // of the method.
-      BasicBlock initClassBlock =
-          entryBlock.listIterator(code, arguments.size()).split(code, 0, null);
+      BasicBlock initClassBlock = block.listIterator(code, arguments.size()).split(code, 0, null);
       assert !initClassBlock.hasCatchHandlers();
 
       InstructionListIterator iterator = initClassBlock.listIterator(code);
@@ -1018,8 +1014,7 @@
           }
 
           InlineeWithReason inlinee =
-              action.buildInliningIR(
-                  appView, invoke, context, inliningIRProvider, lensCodeRewriter);
+              action.buildInliningIR(appView, invoke, inliningIRProvider, lensCodeRewriter);
           if (strategy.willExceedBudget(
               code, invoke, inlinee, block, whyAreYouNotInliningReporter)) {
             assert whyAreYouNotInliningReporter.unsetReasonHasBeenReportedFlag();
@@ -1286,7 +1281,6 @@
       Timing timing)
       throws ExecutionException {
     postMethodProcessorBuilder
-        .getMethodsToReprocessBuilder()
         .rewrittenWithLens(appView)
         .merge(
             singleInlineCallers
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MultiCallerInliner.java b/src/main/java/com/android/tools/r8/ir/optimize/MultiCallerInliner.java
index afe3547..8312643 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/MultiCallerInliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MultiCallerInliner.java
@@ -199,7 +199,6 @@
     LongLivedProgramMethodSetBuilder<ProgramMethodSet> multiInlineCallers =
         timing.time("Needs inlining analysis", () -> computeMultiInlineCallerMethods(callGraph));
     postMethodProcessorBuilder
-        .getMethodsToReprocessBuilder()
         .rewrittenWithLens(appView)
         .merge(multiInlineCallers);
     timing.end();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java
index 984b6c1..bcef49c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java
@@ -40,6 +40,7 @@
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoCollection;
 import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
+import com.android.tools.r8.utils.InternalOptions;
 import com.google.common.collect.Sets;
 import it.unimi.dsi.fastutil.objects.Reference2IntMap;
 import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
@@ -91,7 +92,9 @@
   }
 
   public static boolean shouldRun(AppView<?> appView, IRCode code) {
-    return appView.options().enableRedundantFieldLoadElimination
+    InternalOptions options = appView.options();
+    return options.enableRedundantFieldLoadElimination
+        && !options.debug
         && (code.metadata().mayHaveArrayGet()
             || code.metadata().mayHaveFieldInstruction()
             || code.metadata().mayHaveInitClass());
@@ -553,10 +556,6 @@
   }
 
   private void handleArrayGet(InstructionListIterator it, ArrayGet arrayGet) {
-    if (arrayGet.outValue().hasLocalInfo()) {
-      return;
-    }
-
     Value array = arrayGet.array().getAliasedValue();
     Value index = arrayGet.index().getAliasedValue();
     ArraySlot arraySlot = ArraySlot.create(array, index, arrayGet.getMemberType());
@@ -594,11 +593,6 @@
       InstanceGet instanceGet,
       DexClassAndField field,
       AssumeRemover assumeRemover) {
-    if (instanceGet.outValue().hasLocalInfo()) {
-      clearMostRecentInstanceFieldWrite(instanceGet, field);
-      return;
-    }
-
     Value object = instanceGet.object().getAliasedValue();
     FieldAndObject fieldAndObject = new FieldAndObject(field.getReference(), object);
     FieldValue replacement = activeState.getInstanceFieldValue(fieldAndObject);
@@ -692,12 +686,6 @@
       AssumeRemover assumeRemover) {
     markClassAsInitialized(field.getHolderType());
 
-    if (staticGet.outValue().hasLocalInfo()) {
-      killNonFinalActiveFields(staticGet);
-      clearMostRecentStaticFieldWrite(staticGet, field);
-      return;
-    }
-
     FieldValue replacement = activeState.getStaticFieldValue(field.getReference());
     if (replacement != null) {
       markAssumeDynamicTypeUsersForRemoval(staticGet, replacement, assumeRemover);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/SimpleDominatingEffectAnalysis.java b/src/main/java/com/android/tools/r8/ir/optimize/SimpleDominatingEffectAnalysis.java
index ae95f9e..0f68783 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/SimpleDominatingEffectAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/SimpleDominatingEffectAnalysis.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.ir.optimize;
 
+import static com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.Query.DIRECTLY;
 import static com.android.tools.r8.ir.optimize.SimpleDominatingEffectAnalysis.InstructionEffect.NO_EFFECT;
 import static com.android.tools.r8.ir.optimize.SimpleDominatingEffectAnalysis.InstructionEffect.OTHER_EFFECT;
 import static com.android.tools.r8.utils.TraversalContinuation.BREAK;
@@ -11,10 +12,12 @@
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.AnalysisAssumption;
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.Instruction;
 import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.DepthFirstSearchWorkListBase.DFSNodeWithState;
 import com.android.tools.r8.utils.DepthFirstSearchWorkListBase.StatefulDepthFirstSearchWorkList;
 import com.android.tools.r8.utils.IntBox;
@@ -201,6 +204,10 @@
       return result.isNotSatisfied();
     }
 
+    public boolean isSatisfied() {
+      return result.isSatisfied();
+    }
+
     public boolean isPartial() {
       return result.isPartial();
     }
@@ -328,4 +335,35 @@
               : NO_EFFECT;
         });
   }
+
+  public static SimpleEffectAnalysisResult triggersClassInitializationBeforeAnyStaticRead(
+      AppView<AppInfoWithLiveness> appView, IRCode code) {
+    assert code.context().getDefinition().isStatic();
+    ProgramMethod context = code.context();
+    return run(
+        code,
+        instruction -> {
+          if (instruction.definitelyTriggersClassInitialization(
+              context.getHolderType(),
+              context,
+              appView,
+              DIRECTLY,
+              AnalysisAssumption.INSTRUCTION_DOES_NOT_THROW)) {
+            // In order to preserve class initialization semantic, the exception must not be caught
+            // by any handler. Therefore, we must ignore this instruction if it is covered by a
+            // catch handler.
+            // Note: this is a conservative approach where we consider that any catch handler could
+            // catch the exception, even if it cannot catch an ExceptionInInitializerError.
+            return InstructionEffect.fromBoolean(!instruction.getBlock().hasCatchHandlers());
+          }
+          // A static field can be updated by a static initializer and then accessed by an instance
+          // method. This is a problem if we later see DESIRED_EFFECT. The check for any instance
+          // method is quite conservative.
+          // TODO(b/217530538): Track if instance methods is accessing static fields.
+          return instruction.isInvokeMethodWithReceiver()
+                  || instruction.instructionMayHaveSideEffects(appView, context)
+              ? OTHER_EFFECT
+              : NO_EFFECT;
+        });
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/AlwaysFalseClassInlinerMethodConstraint.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/AlwaysFalseClassInlinerMethodConstraint.java
index 645a4bf..1826502 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/AlwaysFalseClassInlinerMethodConstraint.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/AlwaysFalseClassInlinerMethodConstraint.java
@@ -6,7 +6,7 @@
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
+import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
 import com.android.tools.r8.ir.analysis.value.objectstate.ObjectState;
 import com.android.tools.r8.ir.optimize.classinliner.analysis.ParameterUsage;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/AlwaysTrueClassInlinerMethodConstraint.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/AlwaysTrueClassInlinerMethodConstraint.java
index d629d45..1f33962 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/AlwaysTrueClassInlinerMethodConstraint.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/AlwaysTrueClassInlinerMethodConstraint.java
@@ -6,7 +6,7 @@
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
+import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
 import com.android.tools.r8.ir.analysis.value.objectstate.ObjectState;
 import com.android.tools.r8.ir.optimize.classinliner.analysis.ParameterUsage;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/ClassInlinerMethodConstraint.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/ClassInlinerMethodConstraint.java
index 8b44af5..5bc6317 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/ClassInlinerMethodConstraint.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/ClassInlinerMethodConstraint.java
@@ -6,7 +6,7 @@
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
+import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
 import com.android.tools.r8.ir.analysis.value.objectstate.ObjectState;
 import com.android.tools.r8.ir.optimize.classinliner.analysis.ParameterUsage;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
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 4aceb17..eaae1fe 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
@@ -9,8 +9,8 @@
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexField;
 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.proto.ArgumentInfo;
+import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
 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;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EmptyEnumUnboxer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EmptyEnumUnboxer.java
index 1583873..23ae546 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EmptyEnumUnboxer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EmptyEnumUnboxer.java
@@ -8,7 +8,7 @@
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription;
+import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
 import com.android.tools.r8.ir.analysis.fieldvalueanalysis.StaticFieldValues;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.Phi;
@@ -66,6 +66,11 @@
   }
 
   @Override
+  public void rewriteWithLens() {
+    // Intentionally empty.
+  }
+
+  @Override
   public void unboxEnums(
       AppView<AppInfoWithLiveness> appView,
       IRConverter converter,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
index 8ff6293..ae119f6 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
@@ -8,7 +8,7 @@
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription;
+import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
 import com.android.tools.r8.ir.analysis.fieldvalueanalysis.StaticFieldValues;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.Phi;
@@ -46,6 +46,7 @@
   public abstract Set<Phi> rewriteCode(
       IRCode code, MethodProcessor methodProcessor, RewrittenPrototypeDescription prototypeChanges);
 
+  public abstract void rewriteWithLens();
 
   public abstract void unboxEnums(
       AppView<AppInfoWithLiveness> appView,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
index 13fc08e..c8c21eb 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
@@ -43,7 +43,7 @@
 import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.PrunedItems;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription;
+import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
 import com.android.tools.r8.ir.analysis.fieldvalueanalysis.StaticFieldValues;
 import com.android.tools.r8.ir.analysis.fieldvalueanalysis.StaticFieldValues.EnumStaticFieldValues;
 import com.android.tools.r8.ir.analysis.type.ArrayTypeElement;
@@ -605,6 +605,12 @@
   }
 
   @Override
+  public void rewriteWithLens() {
+    methodsDependingOnLibraryModelisation =
+        methodsDependingOnLibraryModelisation.rewrittenWithLens(appView.graphLens());
+  }
+
+  @Override
   public void unboxEnums(
       AppView<AppInfoWithLiveness> appView,
       IRConverter converter,
@@ -667,7 +673,6 @@
     // the builders with the methods removed by the tree fixer (since these methods references are
     // already fully lens rewritten).
     postMethodProcessorBuilder
-        .getMethodsToReprocessBuilder()
         .rewrittenWithLens(appView)
         .removeAll(treeFixerResult.getPrunedItems().getRemovedMethods())
         .merge(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateInfoCollection.java
index 39dd787..b79dea1 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateInfoCollection.java
@@ -26,7 +26,7 @@
 public class EnumUnboxingCandidateInfoCollection {
 
   private final Map<DexType, EnumUnboxingCandidateInfo> enumTypeToInfo = new ConcurrentHashMap<>();
-  private final Set<DexMethod> prunedMethods = Sets.newIdentityHashSet();
+  private final Set<DexMethod> prunedMethods = Sets.newConcurrentHashSet();
 
   public void addCandidate(
       AppView<AppInfoWithLiveness> appView,
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 8a3d24e..e515c08 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
@@ -11,8 +11,9 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.NestedGraphLens;
 import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.RewrittenTypeInfo;
+import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
+import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
+import com.android.tools.r8.graph.proto.RewrittenTypeInfo;
 import com.android.tools.r8.ir.analysis.value.AbstractValueFactory;
 import com.android.tools.r8.ir.analysis.value.SingleFieldValue;
 import com.android.tools.r8.ir.analysis.value.SingleValue;
@@ -160,8 +161,9 @@
       newMethodSignatures.put(from, to);
       int offsetDiff = 0;
       int toOffset = BooleanUtils.intValue(!toStatic);
-      RewrittenPrototypeDescription.ArgumentInfoCollection.Builder builder =
-          RewrittenPrototypeDescription.ArgumentInfoCollection.builder();
+      ArgumentInfoCollection.Builder builder =
+          ArgumentInfoCollection.builder()
+              .setArgumentInfosSize(from.getNumberOfArguments(fromStatic));
       if (fromStatic != toStatic) {
         assert toStatic;
         offsetDiff = 1;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
index b0a4ed5..1a62ad5 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
@@ -13,9 +13,9 @@
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfo;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.RewrittenTypeInfo;
+import com.android.tools.r8.graph.proto.ArgumentInfo;
+import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
+import com.android.tools.r8.graph.proto.RewrittenTypeInfo;
 import com.android.tools.r8.ir.analysis.type.ArrayTypeElement;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.code.ArrayAccess;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
index 773dadf..0214979 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
@@ -24,7 +24,7 @@
 import com.android.tools.r8.graph.ProgramField;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.PrunedItems;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription;
+import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
 import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.code.BasicBlock;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java
index 95a4b63..293d93f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java
@@ -106,13 +106,24 @@
         FieldOptimizationInfo optimizationInfo = definition.getOptimizationInfo();
         AbstractValue abstractValue = optimizationInfo.getAbstractValue();
 
+        if (methodWithReceiver.hasUnusedOutValue()) {
+          // Remove the invoke if it is a call to Enum.name() or Enum.ordinal() as they don't have
+          // any side effects. Enum.toString() can be overridden and calls to it could therefore
+          // have arbitrary side effects.
+          if (methodWithReceiver.getReceiver().getType().isDefinitelyNotNull()
+              && !isToStringInvoke) {
+            assert isNameInvoke || isOrdinalInvoke;
+            iterator.removeOrReplaceByDebugLocalRead();
+          }
+          continue;
+        }
+
         Value outValue = methodWithReceiver.outValue();
         if (isOrdinalInvoke) {
           SingleNumberValue ordinalValue =
               getOrdinalValue(code, abstractValue, methodWithReceiver.getReceiver().isNeverNull());
-            if (ordinalValue != null) {
-              iterator.replaceCurrentInstruction(
-                  new ConstNumber(outValue, ordinalValue.getValue()));
+          if (ordinalValue != null) {
+            iterator.replaceCurrentInstruction(new ConstNumber(outValue, ordinalValue.getValue()));
           }
           continue;
         }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/classification/CheckNotNullEnumUnboxerMethodClassification.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/classification/CheckNotNullEnumUnboxerMethodClassification.java
index e9f1bdb..36f2de2 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/classification/CheckNotNullEnumUnboxerMethodClassification.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/classification/CheckNotNullEnumUnboxerMethodClassification.java
@@ -4,7 +4,7 @@
 
 package com.android.tools.r8.ir.optimize.enums.classification;
 
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
+import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
 import com.android.tools.r8.ir.code.InvokeStatic;
 import com.android.tools.r8.ir.code.Value;
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/classification/EnumUnboxerMethodClassification.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/classification/EnumUnboxerMethodClassification.java
index f9192b9..e08270b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/classification/EnumUnboxerMethodClassification.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/classification/EnumUnboxerMethodClassification.java
@@ -4,7 +4,7 @@
 
 package com.android.tools.r8.ir.optimize.enums.classification;
 
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
+import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
 
 public abstract class EnumUnboxerMethodClassification {
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/classification/UnknownEnumUnboxerMethodClassification.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/classification/UnknownEnumUnboxerMethodClassification.java
index 04529b4..ee10791 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/classification/UnknownEnumUnboxerMethodClassification.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/classification/UnknownEnumUnboxerMethodClassification.java
@@ -4,7 +4,7 @@
 
 package com.android.tools.r8.ir.optimize.enums.classification;
 
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
+import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
 
 public final class UnknownEnumUnboxerMethodClassification extends EnumUnboxerMethodClassification {
 
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 e855aa1..d8fe949 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
@@ -9,8 +9,8 @@
 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.graph.RewrittenPrototypeDescription;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
+import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
+import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
 import com.android.tools.r8.ir.analysis.type.DynamicType;
 import com.android.tools.r8.ir.analysis.type.DynamicTypeWithUpperBound;
 import com.android.tools.r8.ir.analysis.type.Nullability;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
index 95483ce..f6aa6ad 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
@@ -18,6 +18,7 @@
 import com.android.tools.r8.ir.optimize.info.initializer.DefaultInstanceInitializerInfo;
 import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
 import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.OptionalBool;
 import com.google.common.collect.ImmutableSet;
 import java.util.BitSet;
 import java.util.Set;
@@ -161,6 +162,11 @@
   }
 
   @Override
+  public OptionalBool isReturnValueUsed() {
+    return OptionalBool.unknown();
+  }
+
+  @Override
   public boolean forceInline() {
     return false;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
index 17cffe5..473f718 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
@@ -15,6 +15,7 @@
 import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
 import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
 import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.OptionalBool;
 import java.util.BitSet;
 import java.util.Set;
 
@@ -86,6 +87,8 @@
 
   public abstract boolean isMultiCallerMethod();
 
+  public abstract OptionalBool isReturnValueUsed();
+
   public abstract boolean forceInline();
 
   public abstract boolean triggersClassInitBeforeAnySideEffect();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
index acf0da6..594328f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.optimize.info;
 
-import static com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.Query.DIRECTLY;
 import static com.android.tools.r8.ir.code.DominatorTree.Assumption.MAY_HAVE_UNREACHABLE_BLOCKS;
 import static com.android.tools.r8.ir.code.Opcodes.ADD;
 import static com.android.tools.r8.ir.code.Opcodes.AND;
@@ -55,7 +54,6 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.MethodResolutionResult;
 import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.AnalysisAssumption;
 import com.android.tools.r8.ir.analysis.DeterminismAnalysis;
 import com.android.tools.r8.ir.analysis.InitializedClassesOnNormalExitAnalysis;
 import com.android.tools.r8.ir.analysis.inlining.SimpleInliningConstraintAnalysis;
@@ -89,6 +87,8 @@
 import com.android.tools.r8.ir.conversion.IRConverter;
 import com.android.tools.r8.ir.conversion.MethodProcessor;
 import com.android.tools.r8.ir.optimize.DynamicTypeOptimization;
+import com.android.tools.r8.ir.optimize.SimpleDominatingEffectAnalysis;
+import com.android.tools.r8.ir.optimize.SimpleDominatingEffectAnalysis.SimpleEffectAnalysisResult;
 import com.android.tools.r8.ir.optimize.classinliner.analysis.ClassInlinerMethodConstraintAnalysis;
 import com.android.tools.r8.ir.optimize.classinliner.constraint.ClassInlinerMethodConstraint;
 import com.android.tools.r8.ir.optimize.enums.classification.EnumUnboxerMethodClassification;
@@ -540,46 +540,15 @@
       DexEncodedMethod method, IRCode code, OptimizationFeedback feedback) {
     if (method.isStatic()) {
       // Identifies if the method preserves class initialization after inlining.
+      SimpleEffectAnalysisResult simpleEffectAnalysisResult =
+          SimpleDominatingEffectAnalysis.triggersClassInitializationBeforeAnyStaticRead(
+              appView, code);
       feedback.markTriggerClassInitBeforeAnySideEffect(
-          method, triggersClassInitializationBeforeSideEffect(code));
+          method, simpleEffectAnalysisResult.isSatisfied());
     }
   }
 
   /**
-   * Returns true if the given code unconditionally triggers class initialization before any other
-   * side effecting instruction.
-   *
-   * <p>Note: we do not track phis so we may return false negative. This is a conservative approach.
-   */
-  private boolean triggersClassInitializationBeforeSideEffect(IRCode code) {
-    return alwaysTriggerExpectedEffectBeforeAnythingElse(
-        code,
-        (instruction, it) -> {
-          ProgramMethod context = code.context();
-          if (instruction.definitelyTriggersClassInitialization(
-              context.getHolderType(),
-              context,
-              appView,
-              DIRECTLY,
-              AnalysisAssumption.INSTRUCTION_DOES_NOT_THROW)) {
-            // In order to preserve class initialization semantic, the exception must not be caught
-            // by any handler. Therefore, we must ignore this instruction if it is covered by a
-            // catch handler.
-            // Note: this is a conservative approach where we consider that any catch handler could
-            // catch the exception, even if it cannot catch an ExceptionInInitializerError.
-            if (!instruction.getBlock().hasCatchHandlers()) {
-              // We found an instruction that preserves initialization of the class.
-              return InstructionEffect.DESIRED_EFFECT;
-            }
-          } else if (instruction.instructionMayHaveSideEffects(appView, context)) {
-            // We found a side effect before class initialization.
-            return InstructionEffect.OTHER_EFFECT;
-          }
-          return InstructionEffect.NO_EFFECT;
-        });
-  }
-
-  /**
    * Returns true if the given code unconditionally triggers an expected effect before anything
    * else, false otherwise.
    *
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 69f0497..2cffcc0 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
@@ -28,6 +28,7 @@
 import com.android.tools.r8.utils.BitSetUtils;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.OptionalBool;
 import java.util.BitSet;
 import java.util.Collections;
 import java.util.Set;
@@ -47,6 +48,7 @@
       EnumUnboxerMethodClassification.unknown();
   private DynamicType dynamicType = DynamicType.unknown();
   private InlinePreference inlining = InlinePreference.Default;
+  private OptionalBool isReturnValueUsed = OptionalBool.unknown();
   // Stores information about instance methods and constructors for
   // class inliner, null value indicates that the method is not eligible.
   private BridgeInfo bridgeInfo = null;
@@ -481,6 +483,15 @@
   }
 
   @Override
+  public OptionalBool isReturnValueUsed() {
+    return isReturnValueUsed;
+  }
+
+  void setIsReturnValueUsed(OptionalBool isReturnValueUsed) {
+    this.isReturnValueUsed = isReturnValueUsed;
+  }
+
+  @Override
   public boolean forceInline() {
     return inlining == InlinePreference.ForceInline;
   }
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 5be58f0..1b54d1f 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
@@ -18,6 +18,7 @@
 import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
 import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfoCollection;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.OptionalBool;
 import java.util.BitSet;
 import java.util.Set;
 import java.util.function.Consumer;
@@ -179,6 +180,10 @@
     method.getMutableOptimizationInfo().setInitializerEnablingJavaAssertions();
   }
 
+  public void setIsReturnValueUsed(OptionalBool isReturnValueUsed, ProgramMethod method) {
+    method.getDefinition().getMutableOptimizationInfo().setIsReturnValueUsed(isReturnValueUsed);
+  }
+
   @Override
   public void setNonNullParamOrThrow(DexEncodedMethod method, BitSet facts) {
     method.getMutableOptimizationInfo().setNonNullParamOrThrow(facts);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/field/EmptyInstanceFieldInitializationInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/info/field/EmptyInstanceFieldInitializationInfoCollection.java
index 833f0c6..7694837 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/field/EmptyInstanceFieldInitializationInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/field/EmptyInstanceFieldInitializationInfoCollection.java
@@ -8,7 +8,7 @@
 import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.GraphLens;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
+import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import java.util.function.BiConsumer;
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldArgumentInitializationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldArgumentInitializationInfo.java
index b689d8a..0d28a5c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldArgumentInitializationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldArgumentInitializationInfo.java
@@ -6,9 +6,9 @@
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.GraphLens;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfo;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.RemovedArgumentInfo;
+import com.android.tools.r8.graph.proto.ArgumentInfo;
+import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
+import com.android.tools.r8.graph.proto.RemovedArgumentInfo;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 
 /**
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfo.java
index 0a34194..6bf9604 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfo.java
@@ -6,7 +6,7 @@
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.GraphLens;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
+import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
 import com.android.tools.r8.ir.analysis.value.SingleValue;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfoCollection.java
index 2520561..772e2d8 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfoCollection.java
@@ -10,7 +10,7 @@
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.GraphLens;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
+import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import java.util.TreeMap;
 import java.util.function.BiConsumer;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldTypeInitializationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldTypeInitializationInfo.java
index 24730bb..b494732 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldTypeInitializationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldTypeInitializationInfo.java
@@ -6,7 +6,7 @@
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.GraphLens;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
+import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
 import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.optimize.enums.EnumDataMap;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/field/NonTrivialInstanceFieldInitializationInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/info/field/NonTrivialInstanceFieldInitializationInfoCollection.java
index b996542..19c4ab9 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/field/NonTrivialInstanceFieldInitializationInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/field/NonTrivialInstanceFieldInitializationInfoCollection.java
@@ -11,7 +11,7 @@
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.GraphLens;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
+import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.StringUtils;
 import java.util.ArrayList;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/field/UnknownInstanceFieldInitializationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/field/UnknownInstanceFieldInitializationInfo.java
index 77409cb..0f41427 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/field/UnknownInstanceFieldInitializationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/field/UnknownInstanceFieldInitializationInfo.java
@@ -6,7 +6,7 @@
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.GraphLens;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
+import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 
 /**
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/ContextInsensitiveInstanceInitializerInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/ContextInsensitiveInstanceInitializerInfoCollection.java
index b15e8e4..0e5ce2c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/ContextInsensitiveInstanceInitializerInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/ContextInsensitiveInstanceInitializerInfoCollection.java
@@ -7,7 +7,7 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.PrunedItems;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
+import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
 import com.android.tools.r8.ir.code.InvokeDirect;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/ContextSensitiveInstanceInitializerInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/ContextSensitiveInstanceInitializerInfoCollection.java
index fec6ed4..6a5efbb 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/ContextSensitiveInstanceInitializerInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/ContextSensitiveInstanceInitializerInfoCollection.java
@@ -7,7 +7,7 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.PrunedItems;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
+import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
 import com.android.tools.r8.ir.code.InvokeDirect;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.google.common.collect.ImmutableMap;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/DefaultInstanceInitializerInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/DefaultInstanceInitializerInfo.java
index 46a3511..59a6141 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/DefaultInstanceInitializerInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/DefaultInstanceInitializerInfo.java
@@ -8,7 +8,7 @@
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.PrunedItems;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
+import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
 import com.android.tools.r8.ir.analysis.fieldvalueanalysis.AbstractFieldSet;
 import com.android.tools.r8.ir.analysis.fieldvalueanalysis.UnknownFieldSet;
 import com.android.tools.r8.ir.optimize.info.field.EmptyInstanceFieldInitializationInfoCollection;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/EmptyInstanceInitializerInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/EmptyInstanceInitializerInfoCollection.java
index 9ac9762..19fea9a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/EmptyInstanceInitializerInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/EmptyInstanceInitializerInfoCollection.java
@@ -7,7 +7,7 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.PrunedItems;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
+import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
 import com.android.tools.r8.ir.code.InvokeDirect;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/InstanceInitializerInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/InstanceInitializerInfo.java
index e07b514..cbf442c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/InstanceInitializerInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/InstanceInitializerInfo.java
@@ -8,7 +8,7 @@
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.PrunedItems;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
+import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
 import com.android.tools.r8.ir.analysis.fieldvalueanalysis.AbstractFieldSet;
 import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoCollection;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/InstanceInitializerInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/InstanceInitializerInfoCollection.java
index 8002513..fd793a6 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/InstanceInitializerInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/InstanceInitializerInfoCollection.java
@@ -7,7 +7,7 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.PrunedItems;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
+import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
 import com.android.tools.r8.ir.code.InvokeDirect;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.MapUtils;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/NonTrivialInstanceInitializerInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/NonTrivialInstanceInitializerInfo.java
index 58af519..327e46a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/NonTrivialInstanceInitializerInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/NonTrivialInstanceInitializerInfo.java
@@ -9,7 +9,7 @@
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.PrunedItems;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
+import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
 import com.android.tools.r8.ir.analysis.fieldvalueanalysis.AbstractFieldSet;
 import com.android.tools.r8.ir.analysis.fieldvalueanalysis.ConcreteMutableFieldSet;
 import com.android.tools.r8.ir.analysis.fieldvalueanalysis.EmptyFieldSet;
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/AbstractSynthesizedCode.java b/src/main/java/com/android/tools/r8/ir/synthetic/AbstractSynthesizedCode.java
index b750f84..acf4d56 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/AbstractSynthesizedCode.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/AbstractSynthesizedCode.java
@@ -12,8 +12,8 @@
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription;
 import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.NumberGenerator;
 import com.android.tools.r8.ir.code.Position;
diff --git a/src/main/java/com/android/tools/r8/optimize/FieldRebindingIdentityLens.java b/src/main/java/com/android/tools/r8/optimize/FieldRebindingIdentityLens.java
index f4b80a7..00867f4 100644
--- a/src/main/java/com/android/tools/r8/optimize/FieldRebindingIdentityLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/FieldRebindingIdentityLens.java
@@ -10,7 +10,7 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription;
+import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
 import java.util.IdentityHashMap;
 import java.util.Map;
 
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
index 81c4ae1..b77fbd1 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
@@ -15,7 +15,7 @@
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
 import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription;
+import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
 import java.util.IdentityHashMap;
 import java.util.Map;
 
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java
index da5a6d5..edba428 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java
@@ -15,7 +15,7 @@
 import com.android.tools.r8.graph.FieldAccessInfo;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription;
+import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
 import com.android.tools.r8.ir.code.Invoke;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import java.util.Collections;
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 f5b55c6..b7dd7ff 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
@@ -171,6 +171,7 @@
                   stronglyConnectedProgramComponent, (unused) -> DexMethodSignatureSet.create())
               .add(signature);
         },
+        postMethodProcessorBuilder,
         executorService,
         timing);
 
@@ -205,6 +206,7 @@
       ImmediateProgramSubtypingInfo immediateSubtypingInfo,
       List<Set<DexProgramClass>> stronglyConnectedProgramComponents,
       BiConsumer<Set<DexProgramClass>, DexMethodSignature> interfaceDispatchOutsideProgram,
+      PostMethodProcessor.Builder postMethodProcessorBuilder,
       ExecutorService executorService,
       Timing timing)
       throws ExecutionException {
@@ -214,14 +216,18 @@
     appView.testing().argumentPropagatorEventConsumer.acceptCodeScannerResult(codeScannerResult);
     codeScanner = null;
 
+    postMethodProcessorBuilder.rewrittenWithLens(appView);
+
     timing.begin("Compute optimization info");
     new ArgumentPropagatorOptimizationInfoPopulator(
             appView,
+            converter,
             immediateSubtypingInfo,
             codeScannerResult,
+            postMethodProcessorBuilder,
             stronglyConnectedProgramComponents,
             interfaceDispatchOutsideProgram)
-        .populateOptimizationInfo(converter, executorService, timing);
+        .populateOptimizationInfo(executorService, timing);
     timing.end();
 
     timing.begin("Compute unused arguments");
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 85bef2b..e72be3d 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
@@ -19,8 +19,8 @@
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.MethodCollection;
 import com.android.tools.r8.graph.PrunedItems;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription;
 import com.android.tools.r8.graph.TreeFixerBase;
+import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
 import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfoFixer;
 import com.android.tools.r8.ir.optimize.info.MutableFieldOptimizationInfo;
 import com.android.tools.r8.ir.optimize.info.MutableMethodOptimizationInfo;
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 2839aeb..ba4b05a 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
@@ -47,7 +47,6 @@
 import com.android.tools.r8.optimize.argumentpropagation.utils.WideningUtils;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.Timing;
-import com.google.common.collect.Iterables;
 import com.google.common.collect.Sets;
 import java.util.ArrayList;
 import java.util.IdentityHashMap;
@@ -152,12 +151,6 @@
   }
 
   private void scan(InvokeMethod invoke, ProgramMethod context, Timing timing) {
-    List<Value> arguments = invoke.arguments();
-    if (arguments.isEmpty()) {
-      // Nothing to propagate.
-      return;
-    }
-
     DexMethod invokedMethod = invoke.getInvokedMethod();
     if (invokedMethod.getHolderType().isArrayType()) {
       // Nothing to propagate; the targeted method is not a program method.
@@ -191,7 +184,7 @@
       return;
     }
 
-    if (arguments.size() != resolvedMethod.getDefinition().getNumberOfArguments()
+    if (invoke.arguments().size() != resolvedMethod.getDefinition().getNumberOfArguments()
         || invoke.isInvokeStatic() != resolvedMethod.getAccessFlags().isStatic()) {
       // Nothing to propagate; the invoke instruction fails.
       return;
@@ -410,13 +403,10 @@
               methodReprocessingCriteria.getParameterReprocessingCriteria(argumentIndex)));
     }
 
-    // If all parameter states are unknown, then return a canonicalized unknown method state that
-    // has this property.
-    if (Iterables.all(parameterStates, ParameterState::isUnknown)) {
-      return MethodState.unknown();
-    }
-
-    return new ConcreteMonomorphicMethodState(parameterStates);
+    // We simulate that the return value is used for methods with void return type. This ensures
+    // that we will widen the method state to unknown if/when all parameter states become unknown.
+    boolean isReturnValueUsed = invoke.getReturnType().isVoidType() || invoke.hasUsedOutValue();
+    return ConcreteMonomorphicMethodState.create(isReturnValueUsed, parameterStates);
   }
 
   // For receivers there is not much point in trying to track an abstract value. Therefore we only
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 ae5322e..d67718a 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
@@ -8,7 +8,7 @@
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.NestedGraphLens;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription;
+import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
 import com.android.tools.r8.ir.code.Invoke.Type;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap;
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 6479ad3..42537e8 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
@@ -23,8 +23,6 @@
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Timing;
-import com.android.tools.r8.utils.collections.LongLivedProgramMethodSetBuilder;
-import com.android.tools.r8.utils.collections.ProgramMethodSet;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
@@ -59,19 +57,16 @@
     // Bring the methods to reprocess set up-to-date with the current graph lens (i.e., the one
     // prior to the argument propagator lens, which has not yet been installed!).
     timing.begin("Rewrite methods to reprocess");
-    LongLivedProgramMethodSetBuilder<ProgramMethodSet> methodsToReprocessBuilder =
-        postMethodProcessorBuilder
-            .getMethodsToReprocessBuilder()
-            .rewrittenWithLens(appView.graphLens());
+    postMethodProcessorBuilder.rewrittenWithLens(appView);
     timing.end();
 
     timing.begin("Enqueue methods with non-trivial info");
-    enqueueAffectedCallees(graphLens, methodsToReprocessBuilder);
+    enqueueAffectedCallees(graphLens, postMethodProcessorBuilder);
     timing.end();
 
     timing.begin("Enqueue affected methods");
     if (graphLens != null) {
-      enqueueAffectedCallers(graphLens, methodsToReprocessBuilder, executorService);
+      enqueueAffectedCallers(graphLens, postMethodProcessorBuilder, executorService);
     }
     timing.end();
 
@@ -80,7 +75,7 @@
 
   private void enqueueAffectedCallees(
       ArgumentPropagatorGraphLens graphLens,
-      LongLivedProgramMethodSetBuilder<ProgramMethodSet> methodsToReprocessBuilder) {
+      PostMethodProcessor.Builder postMethodProcessorBuilder) {
     GraphLens currentGraphLens = appView.graphLens();
     for (DexProgramClass clazz : appView.appInfo().classes()) {
       clazz.forEachProgramMethodMatching(
@@ -95,7 +90,7 @@
                   graphLens.internalGetNextMethodSignature(method.getReference());
               if (graphLens.hasPrototypeChanges(rewrittenMethodSignature)) {
                 assert !appView.appInfo().isNeverReprocessMethod(method);
-                methodsToReprocessBuilder.add(method, currentGraphLens);
+                postMethodProcessorBuilder.add(method, currentGraphLens);
                 appView.testing().callSiteOptimizationInfoInspector.accept(method);
                 return;
               }
@@ -107,7 +102,7 @@
                     .getReprocessingCriteria(method)
                     .shouldReprocess(appView, method, callSiteOptimizationInfo)
                 && !appView.appInfo().isNeverReprocessMethod(method)) {
-              methodsToReprocessBuilder.add(method, currentGraphLens);
+              postMethodProcessorBuilder.add(method, currentGraphLens);
               appView.testing().callSiteOptimizationInfoInspector.accept(method);
             }
           });
@@ -119,7 +114,7 @@
   //  methods as unoptimizable prior to removing parameters from the application.
   private void enqueueAffectedCallers(
       ArgumentPropagatorGraphLens graphLens,
-      LongLivedProgramMethodSetBuilder<ProgramMethodSet> methodsToReprocessBuilder,
+      PostMethodProcessor.Builder postMethodProcessorBuilder,
       ExecutorService executorService)
       throws ExecutionException {
     GraphLens currentGraphLens = appView.graphLens();
@@ -131,7 +126,7 @@
               clazz.forEachProgramMethodMatching(
                   DexEncodedMethod::hasCode,
                   method -> {
-                    if (!methodsToReprocessBuilder.contains(method, currentGraphLens)) {
+                    if (!postMethodProcessorBuilder.contains(method, currentGraphLens)) {
                       AffectedMethodUseRegistry registry =
                           new AffectedMethodUseRegistry(appView, method, graphLens);
                       if (method.registerCodeReferencesWithResult(registry)) {
@@ -145,7 +140,7 @@
             executorService);
     methodsToReprocess.forEach(
         methodsToReprocessInClass ->
-            methodsToReprocessBuilder.addAll(methodsToReprocessInClass, currentGraphLens));
+            postMethodProcessorBuilder.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 f8d617b..0b47a5e 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
@@ -16,6 +16,7 @@
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
 import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.ir.conversion.PostMethodProcessor;
 import com.android.tools.r8.ir.optimize.info.ConcreteCallSiteOptimizationInfo;
 import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
@@ -32,7 +33,9 @@
 import com.android.tools.r8.optimize.argumentpropagation.propagation.VirtualDispatchMethodArgumentPropagator;
 import com.android.tools.r8.optimize.argumentpropagation.utils.WideningUtils;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.OptionalBool;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Timing;
 import java.util.List;
@@ -49,7 +52,10 @@
 public class ArgumentPropagatorOptimizationInfoPopulator {
 
   private final AppView<AppInfoWithLiveness> appView;
+  private final IRConverter converter;
   private final MethodStateCollectionByReference methodStates;
+  private final InternalOptions options;
+  private final PostMethodProcessor.Builder postMethodProcessorBuilder;
 
   private final ImmediateProgramSubtypingInfo immediateSubtypingInfo;
   private final List<Set<DexProgramClass>> stronglyConnectedProgramComponents;
@@ -59,13 +65,18 @@
 
   ArgumentPropagatorOptimizationInfoPopulator(
       AppView<AppInfoWithLiveness> appView,
+      IRConverter converter,
       ImmediateProgramSubtypingInfo immediateSubtypingInfo,
       MethodStateCollectionByReference methodStates,
+      PostMethodProcessor.Builder postMethodProcessorBuilder,
       List<Set<DexProgramClass>> stronglyConnectedProgramComponents,
       BiConsumer<Set<DexProgramClass>, DexMethodSignature> interfaceDispatchOutsideProgram) {
     this.appView = appView;
+    this.converter = converter;
     this.immediateSubtypingInfo = immediateSubtypingInfo;
     this.methodStates = methodStates;
+    this.options = appView.options();
+    this.postMethodProcessorBuilder = postMethodProcessorBuilder;
     this.stronglyConnectedProgramComponents = stronglyConnectedProgramComponents;
     this.interfaceDispatchOutsideProgram = interfaceDispatchOutsideProgram;
   }
@@ -74,8 +85,7 @@
    * Computes an over-approximation of each parameter's value and type and stores the result in
    * {@link com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo}.
    */
-  void populateOptimizationInfo(
-      IRConverter converter, ExecutorService executorService, Timing timing)
+  void populateOptimizationInfo(ExecutorService executorService, Timing timing)
       throws ExecutionException {
     // TODO(b/190154391): Propagate argument information to handle virtual dispatch.
     // TODO(b/190154391): To deal with arguments that are themselves passed as arguments to invoke
@@ -147,18 +157,18 @@
   }
 
   private void setOptimizationInfo(ProgramMethod method) {
-    MethodState methodState = methodStates.removeOrElse(method, null);
-    if (methodState == null) {
-      return;
-    }
-
+    MethodState methodState = methodStates.remove(method);
     if (methodState.isBottom()) {
-      method.convertToAbstractOrThrowNullMethod(appView);
+      if (method.getDefinition().hasCode() && !method.getDefinition().isClassInitializer()) {
+        method.convertToAbstractOrThrowNullMethod(appView);
+        converter.onMethodCodePruned(method);
+        postMethodProcessorBuilder.remove(method, appView.graphLens());
+      }
       return;
     }
 
     // Do not optimize @KeepConstantArgument methods.
-    if (appView.appInfo().isKeepConstantArgumentsMethod(method)) {
+    if (!appView.getKeepInfo(method).isConstantArgumentOptimizationAllowed(options)) {
       methodState = MethodState.unknown();
     }
 
@@ -194,11 +204,17 @@
         .map(ParameterState::asConcrete)
         .noneMatch(ConcreteParameterState::hasInParameters);
 
-    getSimpleFeedback()
-        .setArgumentInfos(
-            method,
-            ConcreteCallSiteOptimizationInfo.fromMethodState(
-                appView, method, monomorphicMethodState));
+    if (monomorphicMethodState.size() > 0) {
+      getSimpleFeedback()
+          .setArgumentInfos(
+              method,
+              ConcreteCallSiteOptimizationInfo.fromMethodState(
+                  appView, method, monomorphicMethodState));
+    }
+
+    if (!monomorphicMethodState.isReturnValueUsed()) {
+      getSimpleFeedback().setIsReturnValueUsed(OptionalBool.FALSE, method);
+    }
 
     // Strengthen the return value of the method if the method is known to return one of the
     // arguments.
@@ -215,15 +231,23 @@
   private MethodState getMethodStateAfterUninstantiatedParameterRemoval(
       ProgramMethod method, MethodState methodState) {
     assert methodState.isMonomorphic() || methodState.isUnknown();
-    if (appView.appInfo().isKeepConstantArgumentsMethod(method)) {
+    if (!appView.getKeepInfo(method).isConstantArgumentOptimizationAllowed(options)) {
       return methodState;
     }
 
     int numberOfArguments = method.getDefinition().getNumberOfArguments();
-    List<ParameterState> parameterStates =
-        methodState.isMonomorphic()
-            ? methodState.asMonomorphic().getParameterStates()
-            : ListUtils.newInitializedArrayList(numberOfArguments, ParameterState.unknown());
+    boolean isReturnValueUsed;
+    List<ParameterState> parameterStates;
+    if (methodState.isMonomorphic()) {
+      ConcreteMonomorphicMethodState monomorphicMethodState = methodState.asMonomorphic();
+      isReturnValueUsed = monomorphicMethodState.isReturnValueUsed();
+      parameterStates = monomorphicMethodState.getParameterStates();
+    } else {
+      assert methodState.isUnknown();
+      isReturnValueUsed = true;
+      parameterStates =
+          ListUtils.newInitializedArrayList(numberOfArguments, ParameterState.unknown());
+    }
     List<ParameterState> narrowedParameterStates =
         ListUtils.mapOrElse(
             parameterStates,
@@ -240,7 +264,7 @@
             },
             null);
     return narrowedParameterStates != null
-        ? new ConcreteMonomorphicMethodState(narrowedParameterStates)
+        ? new ConcreteMonomorphicMethodState(isReturnValueUsed, narrowedParameterStates)
         : methodState;
   }
 
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 c2912c0..c30afaf 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
@@ -20,10 +20,10 @@
 import com.android.tools.r8.graph.ObjectAllocationInfoCollection;
 import com.android.tools.r8.graph.ProgramField;
 import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.RemovedArgumentInfo;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.RewrittenTypeInfo;
+import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
+import com.android.tools.r8.graph.proto.RemovedArgumentInfo;
+import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
+import com.android.tools.r8.graph.proto.RewrittenTypeInfo;
 import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
 import com.android.tools.r8.ir.analysis.type.DynamicType;
 import com.android.tools.r8.ir.analysis.type.DynamicTypeWithUpperBound;
@@ -44,6 +44,7 @@
 import com.android.tools.r8.utils.IntBox;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.InternalOptions.CallSiteOptimizationOptions;
+import com.android.tools.r8.utils.OptionalBool;
 import com.android.tools.r8.utils.Pair;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Timing;
@@ -389,14 +390,10 @@
             }
 
             // Also record the found return value for abstract virtual methods.
-            if (newReturnType == dexItemFactory.voidType) {
+            if (newReturnType == dexItemFactory.voidType && returnValueForVirtualMethods != null) {
               for (ProgramMethod method : methods) {
                 if (method.getAccessFlags().isAbstract()) {
                   returnValuesForVirtualMethods.put(method, returnValueForVirtualMethods);
-                } else {
-                  AbstractValue returnValueForVirtualMethod =
-                      method.getOptimizationInfo().getAbstractReturnValue();
-                  assert returnValueForVirtualMethod.equals(returnValueForVirtualMethods);
                 }
               }
             }
@@ -441,7 +438,10 @@
         if (!appView.appInfo().mayPropagateValueFor(method)) {
           return null;
         }
-        AbstractValue returnValueForMethod = method.getOptimizationInfo().getAbstractReturnValue();
+        AbstractValue returnValueForMethod =
+            method.getReturnType().isAlwaysNull(appView)
+                ? appView.abstractValueFactory().createNullValue()
+                : method.getOptimizationInfo().getAbstractReturnValue();
         if (!returnValueForMethod.isSingleValue()
             || !returnValueForMethod.asSingleValue().isMaterializableInAllContexts(appView)
             || (returnValue != null && !returnValueForMethod.equals(returnValue))) {
@@ -510,16 +510,17 @@
 
     private DexType getNewReturnTypeForVirtualMethods(
         ProgramMethodSet methods, SingleValue returnValue) {
-      if (returnValue != null) {
+      if (returnValue != null || isReturnValueUnusedForVirtualMethods(methods)) {
         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);
+        DexType newReturnTypeForMethod = getNewReturnType(method, OptionalBool.UNKNOWN, null);
         if (newReturnTypeForMethod == null
             || (newReturnType != null && newReturnType != newReturnTypeForMethod)) {
           return null;
@@ -530,6 +531,16 @@
       return newReturnType;
     }
 
+    private boolean isReturnValueUnusedForVirtualMethods(ProgramMethodSet methods) {
+      ProgramMethod representative = methods.getFirst();
+      return !representative.getReturnType().isVoidType()
+          && Iterables.all(
+              methods,
+              method ->
+                  appView.getKeepInfo(method).isUnusedReturnValueOptimizationAllowed(options)
+                      && method.getOptimizationInfo().isReturnValueUsed().isFalse());
+    }
+
     private DexType getNewParameterTypeForVirtualMethods(
         ProgramMethodSet methods, int parameterIndex) {
       if (parameterIndex == 0) {
@@ -850,7 +861,8 @@
         IntSet removableParameterIndices) {
       // Treat the parameters as unused.
       ArgumentInfoCollection.Builder argumentInfoCollectionBuilder =
-          ArgumentInfoCollection.builder();
+          ArgumentInfoCollection.builder()
+              .setArgumentInfosSize(method.getDefinition().getNumberOfArguments());
       for (int argumentIndex = 0;
           argumentIndex < method.getDefinition().getNumberOfArguments();
           argumentIndex++) {
@@ -885,18 +897,26 @@
     }
 
     private DexType getNewReturnType(ProgramMethod method) {
-      return getNewReturnType(method, getReturnValue(method));
+      return getNewReturnType(
+          method, method.getOptimizationInfo().isReturnValueUsed(), getReturnValue(method));
     }
 
-    private DexType getNewReturnType(ProgramMethod method, SingleValue returnValue) {
+    private DexType getNewReturnType(
+        ProgramMethod method, OptionalBool isReturnValueUsed, SingleValue returnValue) {
       DexType staticType = method.getReturnType();
-      if (staticType.isVoidType()
-          || !appView.getKeepInfo(method).isReturnTypeStrengtheningAllowed(options)) {
+      if (staticType.isVoidType()) {
         return null;
       }
       if (returnValue != null) {
         return dexItemFactory.voidType;
       }
+      KeepMethodInfo keepInfo = appView.getKeepInfo(method);
+      if (keepInfo.isUnusedReturnValueOptimizationAllowed(options) && isReturnValueUsed.isFalse()) {
+        return dexItemFactory.voidType;
+      }
+      if (!keepInfo.isReturnTypeStrengtheningAllowed(options)) {
+        return null;
+      }
       TypeElement newReturnTypeElement =
           method
               .getOptimizationInfo()
@@ -992,7 +1012,9 @@
         ProgramMethod method,
         IntFunction<DexType> newParameterTypes,
         IntPredicate removableParameterIndices) {
-      ArgumentInfoCollection.Builder parameterChangesBuilder = ArgumentInfoCollection.builder();
+      ArgumentInfoCollection.Builder parameterChangesBuilder =
+          ArgumentInfoCollection.builder()
+              .setArgumentInfosSize(method.getDefinition().getNumberOfArguments());
       if (method.getDefinition().isInstance()
           && removableParameterIndices.test(0)
           && method.getOptimizationInfo().hasUnusedArguments()
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorUnoptimizableMethods.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorUnoptimizableMethods.java
index 6319e90..c277bb7 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorUnoptimizableMethods.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorUnoptimizableMethods.java
@@ -12,15 +12,12 @@
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.UnknownMethodState;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.MethodSignatureEquivalence;
 import com.android.tools.r8.utils.classhierarchy.MethodOverridesCollector;
 import com.android.tools.r8.utils.collections.ProgramMethodSet;
 import java.util.Collection;
 
 public class ArgumentPropagatorUnoptimizableMethods {
 
-  private static final MethodSignatureEquivalence equivalence = MethodSignatureEquivalence.get();
-
   private final AppView<AppInfoWithLiveness> appView;
   private final ImmediateProgramSubtypingInfo immediateSubtypingInfo;
   private final MethodStateCollectionByReference methodStates;
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 d32df00..e730d74 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
@@ -16,15 +16,25 @@
 public class ConcreteMonomorphicMethodState extends ConcreteMethodState
     implements ConcreteMonomorphicMethodStateOrBottom, ConcreteMonomorphicMethodStateOrUnknown {
 
+  boolean isReturnValueUsed;
   List<ParameterState> parameterStates;
 
-  public ConcreteMonomorphicMethodState(List<ParameterState> parameterStates) {
+  public ConcreteMonomorphicMethodState(
+      boolean isReturnValueUsed, List<ParameterState> parameterStates) {
     assert Streams.stream(Iterables.skip(parameterStates, 1))
         .noneMatch(x -> x.isConcrete() && x.asConcrete().isReceiverParameter());
+    this.isReturnValueUsed = isReturnValueUsed;
     this.parameterStates = parameterStates;
     assert !isEffectivelyUnknown() : "Must use UnknownMethodState instead";
   }
 
+  public static ConcreteMonomorphicMethodStateOrUnknown create(
+      boolean isReturnValueUsed, List<ParameterState> parameterStates) {
+    return isEffectivelyUnknown(isReturnValueUsed, parameterStates)
+        ? unknown()
+        : new ConcreteMonomorphicMethodState(isReturnValueUsed, parameterStates);
+  }
+
   public ParameterState getParameterState(int index) {
     return parameterStates.get(index);
   }
@@ -33,8 +43,21 @@
     return parameterStates;
   }
 
+  public boolean isReturnValueUsed() {
+    return isReturnValueUsed;
+  }
+
+  public boolean isEffectivelyBottom() {
+    return Iterables.any(parameterStates, ParameterState::isBottom);
+  }
+
   public boolean isEffectivelyUnknown() {
-    return Iterables.all(parameterStates, ParameterState::isUnknown);
+    return isEffectivelyUnknown(isReturnValueUsed, parameterStates);
+  }
+
+  private static boolean isEffectivelyUnknown(
+      boolean isReturnValueUsed, List<ParameterState> parameterStates) {
+    return isReturnValueUsed && Iterables.all(parameterStates, ParameterState::isUnknown);
   }
 
   @Override
@@ -43,7 +66,7 @@
     for (ParameterState parameterState : getParameterStates()) {
       copiedParametersStates.add(parameterState.mutableCopy());
     }
-    return new ConcreteMonomorphicMethodState(copiedParametersStates);
+    return new ConcreteMonomorphicMethodState(isReturnValueUsed, copiedParametersStates);
   }
 
   public ConcreteMonomorphicMethodStateOrUnknown mutableJoin(
@@ -56,6 +79,10 @@
       return unknown();
     }
 
+    if (methodState.isReturnValueUsed()) {
+      isReturnValueUsed = true;
+    }
+
     int argumentIndex = 0;
     if (size() > methodSignature.getArity()) {
       assert size() == methodSignature.getArity() + 1;
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/lenscoderewriter/NullCheckInserter.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/lenscoderewriter/NullCheckInserter.java
index c428a70..2ffbf8d 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/lenscoderewriter/NullCheckInserter.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/lenscoderewriter/NullCheckInserter.java
@@ -11,7 +11,7 @@
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.GraphLens.MethodLookupResult;
 import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfo;
+import com.android.tools.r8.graph.proto.ArgumentInfo;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.BasicBlockIterator;
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InParameterFlowPropagator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InParameterFlowPropagator.java
index 9b10015..fae616e 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InParameterFlowPropagator.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InParameterFlowPropagator.java
@@ -133,18 +133,10 @@
       return;
     }
     assert methodState.isMonomorphic();
-    boolean allUnknown = true;
-    for (ParameterState parameterState : methodState.asMonomorphic().getParameterStates()) {
-      if (parameterState.isBottom()) {
-        methodStates.set(method, MethodState.bottom());
-        return;
-      }
-      if (!parameterState.isUnknown()) {
-        assert parameterState.isConcrete();
-        allUnknown = false;
-      }
-    }
-    if (allUnknown) {
+    ConcreteMonomorphicMethodState monomorphicMethodState = methodState.asMonomorphic();
+    if (monomorphicMethodState.isEffectivelyBottom()) {
+      methodStates.set(method, MethodState.bottom());
+    } else if (monomorphicMethodState.isEffectivelyUnknown()) {
       methodStates.set(method, MethodState.unknown());
     }
   }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InterfaceMethodArgumentPropagator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InterfaceMethodArgumentPropagator.java
index 0b617b1..b9d6fda 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InterfaceMethodArgumentPropagator.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InterfaceMethodArgumentPropagator.java
@@ -147,7 +147,6 @@
 
                   ProgramMethod resolvedMethod = resolutionResult.getResolvedProgramMethod();
                   if (resolvedMethod == null
-                      || resolvedMethod.getHolder().isInterface()
                       || resolvedMethod.getHolder() == subclass) {
                     return;
                   }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/utils/ParameterRemovalUtils.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/utils/ParameterRemovalUtils.java
index 72129db..34d8967 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/utils/ParameterRemovalUtils.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/utils/ParameterRemovalUtils.java
@@ -19,7 +19,7 @@
     if (!keepInfo.isParameterRemovalAllowed(options)) {
       return false;
     }
-    if (appView.appInfo().isKeepUnusedArgumentsMethod(method)) {
+    if (!appView.getKeepInfo(method).isUnusedArgumentOptimizationAllowed(options)) {
       return false;
     }
     return method.getDefinition().isLibraryMethodOverride().isFalse()
diff --git a/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoistingLens.java b/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoistingLens.java
index 1ded13b..9827d9a 100644
--- a/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoistingLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoistingLens.java
@@ -10,7 +10,7 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription;
+import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
 import com.android.tools.r8.utils.collections.BidirectionalManyToOneMap;
 import java.util.Set;
 
diff --git a/src/main/java/com/android/tools/r8/optimize/proto/ProtoNormalizer.java b/src/main/java/com/android/tools/r8/optimize/proto/ProtoNormalizer.java
new file mode 100644
index 0000000..2b49179
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/proto/ProtoNormalizer.java
@@ -0,0 +1,126 @@
+// 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.proto;
+
+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.DexMethodSignature;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Timing;
+import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap;
+import com.android.tools.r8.utils.collections.BidirectionalOneToOneMap;
+import com.android.tools.r8.utils.collections.DexMethodSignatureSet;
+import com.android.tools.r8.utils.collections.MutableBidirectionalOneToOneMap;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+
+public class ProtoNormalizer {
+
+  private final AppView<AppInfoWithLiveness> appView;
+  private final DexItemFactory dexItemFactory;
+  private final InternalOptions options;
+
+  public ProtoNormalizer(AppView<AppInfoWithLiveness> appView) {
+    this.appView = appView;
+    this.dexItemFactory = appView.dexItemFactory();
+    this.options = appView.options();
+  }
+
+  public void run(ExecutorService executorService, Timing timing) {
+    if (options.testing.enableExperimentalProtoNormalization) {
+      timing.time("Proto normalization", () -> run(executorService));
+    }
+  }
+
+  // TODO(b/195112263): Parallelize using executor service.
+  private void run(ExecutorService executorService) {
+    // Compute mapping from method signatures to new method signatures.
+    BidirectionalOneToOneMap<DexMethodSignature, DexMethodSignature> normalization =
+        computeNormalization();
+
+    ProtoNormalizerGraphLens.Builder lensBuilder = ProtoNormalizerGraphLens.builder(appView);
+    for (DexProgramClass clazz : appView.appInfo().classes()) {
+      clazz
+          .getMethodCollection()
+          .replaceMethods(
+              method -> {
+                DexMethodSignature methodSignature = method.getSignature();
+                assert normalization.containsKey(methodSignature);
+                DexMethodSignature normalizedMethodSignature = normalization.get(methodSignature);
+                if (methodSignature.equals(normalizedMethodSignature)) {
+                  return method;
+                }
+                DexMethod normalizedMethodReference =
+                    normalizedMethodSignature.withHolder(clazz, dexItemFactory);
+                lensBuilder.recordNewMethodSignature(method, normalizedMethodReference);
+                // TODO(b/195112263): Fixup any optimization info and parameter annotations.
+                return method.toTypeSubstitutedMethod(normalizedMethodReference);
+              });
+    }
+
+    if (!lensBuilder.isEmpty()) {
+      appView.rewriteWithLens(lensBuilder.build());
+    }
+  }
+
+  // TODO(b/195112263): This naively maps each method signatures to their normalized method
+  //  signature if it is not already reserved by another method. This means that we will rewrite
+  //  foo(A,B), bar(B,A), and baz(B,A) into foo(A,B), bar(A,B), and baz(A,B), such that all of the
+  //  method signatures share the same parameter type list. However, if there is a method foo(A,B)
+  //  and foo(B,A) then this does not rewrite foo(B,A) into foo(A,B). If foo(B,A) is not in the same
+  //  hierarchy as foo(A,B), this would be possible, however.
+  // TODO(b/195112263): Do not optimize foo(B, A) into foo(A, B) if this won't lead to any parameter
+  //  type lists being shared (e.g., if foo(B, A) is the only method where sorted(parameters) is
+  //  [A, B]).
+  private BidirectionalOneToOneMap<DexMethodSignature, DexMethodSignature> computeNormalization() {
+    // Reserve the signatures of unoptimizable methods to avoid collisions.
+    MutableBidirectionalOneToOneMap<DexMethodSignature, DexMethodSignature> normalization =
+        new BidirectionalOneToOneHashMap<>();
+    DexMethodSignatureSet optimizableMethodSignatures = DexMethodSignatureSet.create();
+    for (DexProgramClass clazz : appView.appInfo().classes()) {
+      clazz.forEachProgramMethod(
+          method -> {
+            DexMethodSignature methodSignature = method.getMethodSignature();
+            if (isUnoptimizable(method)) {
+              normalization.put(methodSignature, methodSignature);
+            } else if (!normalization.containsKey(methodSignature)) {
+              optimizableMethodSignatures.add(methodSignature);
+            }
+          });
+    }
+    optimizableMethodSignatures.removeAll(normalization.keySet());
+
+    // Normalize each signature that is subject to optimization.
+    List<DexMethodSignature> sortedOptimizableMethodSignatures =
+        new ArrayList<>(optimizableMethodSignatures);
+    sortedOptimizableMethodSignatures.sort(DexMethodSignature::compareTo);
+
+    for (DexMethodSignature signature : sortedOptimizableMethodSignatures) {
+      assert !normalization.containsKey(signature);
+      assert !normalization.containsValue(signature);
+      DexMethodSignature normalizedSignature =
+          signature.withProto(
+              dexItemFactory.createProto(
+                  signature.getReturnType(), signature.getParameters().getSorted()));
+      if (normalization.containsValue(normalizedSignature)) {
+        normalization.put(signature, signature);
+      } else {
+        normalization.put(signature, normalizedSignature);
+      }
+    }
+
+    return normalization;
+  }
+
+  private boolean isUnoptimizable(ProgramMethod method) {
+    // TODO(b/195112263): This is incomplete.
+    return appView.getKeepInfo(method).isPinned(options);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/proto/ProtoNormalizerGraphLens.java b/src/main/java/com/android/tools/r8/optimize/proto/ProtoNormalizerGraphLens.java
new file mode 100644
index 0000000..89d95d6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/proto/ProtoNormalizerGraphLens.java
@@ -0,0 +1,180 @@
+// 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.proto;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
+import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
+import com.android.tools.r8.graph.proto.ArgumentPermutation;
+import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap;
+import com.android.tools.r8.utils.collections.BidirectionalOneToOneMap;
+import com.android.tools.r8.utils.collections.MutableBidirectionalOneToOneMap;
+import com.google.common.collect.ImmutableList;
+import java.util.IdentityHashMap;
+import java.util.Map;
+
+public class ProtoNormalizerGraphLens extends NonIdentityGraphLens {
+
+  private final BidirectionalOneToOneMap<DexMethod, DexMethod> newMethodSignatures;
+  private final Map<DexMethod, RewrittenPrototypeDescription> prototypeChanges;
+
+  ProtoNormalizerGraphLens(
+      AppView<?> appView,
+      BidirectionalOneToOneMap<DexMethod, DexMethod> newMethodSignatures,
+      Map<DexMethod, RewrittenPrototypeDescription> prototypeChanges) {
+    super(appView);
+    this.newMethodSignatures = newMethodSignatures;
+    this.prototypeChanges = prototypeChanges;
+  }
+
+  public static Builder builder(AppView<AppInfoWithLiveness> appView) {
+    return new Builder(appView);
+  }
+
+  @Override
+  public DexType getOriginalType(DexType type) {
+    return getPrevious().getOriginalType(type);
+  }
+
+  @Override
+  public Iterable<DexType> getOriginalTypes(DexType type) {
+    return getPrevious().getOriginalTypes(type);
+  }
+
+  @Override
+  public DexField getOriginalFieldSignature(DexField field) {
+    return getPrevious().getOriginalFieldSignature(field);
+  }
+
+  @Override
+  public DexField getRenamedFieldSignature(DexField originalField, GraphLens codeLens) {
+    return originalField;
+  }
+
+  @Override
+  public DexMethod getRenamedMethodSignature(DexMethod originalMethod, GraphLens applied) {
+    return newMethodSignatures.getOrDefault(originalMethod, originalMethod);
+  }
+
+  @Override
+  public RewrittenPrototypeDescription lookupPrototypeChangesForMethodDefinition(
+      DexMethod method, GraphLens codeLens) {
+    DexMethod previousMethodSignature = getPreviousMethodSignature(method);
+    RewrittenPrototypeDescription previousPrototypeChanges =
+        getPrevious().lookupPrototypeChangesForMethodDefinition(previousMethodSignature);
+    if (previousMethodSignature == method) {
+      return previousPrototypeChanges;
+    }
+    assert prototypeChanges.containsKey(method);
+    return previousPrototypeChanges.combine(prototypeChanges.get(method));
+  }
+
+  @Override
+  public boolean isContextFreeForMethods() {
+    return getPrevious().isContextFreeForMethods();
+  }
+
+  @Override
+  protected FieldLookupResult internalDescribeLookupField(FieldLookupResult previous) {
+    return previous;
+  }
+
+  @Override
+  protected MethodLookupResult internalDescribeLookupMethod(
+      MethodLookupResult previous, DexMethod context) {
+    DexMethod methodSignature = previous.getReference();
+    DexMethod newMethodSignature = getRenamedMethodSignature(methodSignature);
+    if (methodSignature == newMethodSignature) {
+      return previous;
+    }
+    assert !previous.hasReboundReference();
+    return MethodLookupResult.builder(this)
+        .setPrototypeChanges(
+            previous.getPrototypeChanges().combine(prototypeChanges.get(newMethodSignature)))
+        .setReference(newMethodSignature)
+        .setType(previous.getType())
+        .build();
+  }
+
+  @Override
+  protected DexType internalDescribeLookupClassType(DexType previous) {
+    return previous;
+  }
+
+  @Override
+  public DexMethod getPreviousMethodSignature(DexMethod method) {
+    return newMethodSignatures.getRepresentativeKeyOrDefault(method, method);
+  }
+
+  public static class Builder {
+
+    private final AppView<AppInfoWithLiveness> appView;
+    private final MutableBidirectionalOneToOneMap<DexMethod, DexMethod> newMethodSignatures =
+        new BidirectionalOneToOneHashMap<>();
+    private final Map<DexMethod, RewrittenPrototypeDescription> prototypeChanges =
+        new IdentityHashMap<>();
+
+    private Builder(AppView<AppInfoWithLiveness> appView) {
+      this.appView = appView;
+    }
+
+    public Builder recordNewMethodSignature(DexEncodedMethod method, DexMethod newMethodSignature) {
+      assert method.getReference() != newMethodSignature;
+      prototypeChanges.put(newMethodSignature, computePrototypeChanges(method, newMethodSignature));
+      newMethodSignatures.put(method.getReference(), newMethodSignature);
+      return this;
+    }
+
+    // TODO(b/195112263): Canonicalize the permutation maps.
+    private static RewrittenPrototypeDescription computePrototypeChanges(
+        DexEncodedMethod method, DexMethod newMethodSignature) {
+      int firstNonReceiverArgumentIndex = method.getFirstNonReceiverArgumentIndex();
+      int numberOfArguments = method.getNumberOfArguments();
+      ArgumentPermutation.Builder argumentPermutationBuilder =
+          ArgumentPermutation.builder(numberOfArguments);
+      boolean[] used = new boolean[numberOfArguments];
+      for (int argumentIndex = firstNonReceiverArgumentIndex;
+          argumentIndex < numberOfArguments;
+          argumentIndex++) {
+        DexType argumentType = method.getArgumentType(argumentIndex);
+        for (int newArgumentIndex = firstNonReceiverArgumentIndex;
+            newArgumentIndex < numberOfArguments;
+            newArgumentIndex++) {
+          DexType newArgumentType =
+              newMethodSignature.getArgumentType(
+                  newArgumentIndex, method.getAccessFlags().isStatic());
+          if (argumentType == newArgumentType && !used[newArgumentIndex]) {
+            argumentPermutationBuilder.setNewArgumentIndex(argumentIndex, newArgumentIndex);
+            used[newArgumentIndex] = true;
+            break;
+          }
+        }
+      }
+      ArgumentPermutation argumentPermutation = argumentPermutationBuilder.build();
+      assert !argumentPermutation.isDefault();
+      ArgumentInfoCollection argumentInfoCollection =
+          ArgumentInfoCollection.builder()
+              .setArgumentInfosSize(numberOfArguments)
+              .setArgumentPermutation(argumentPermutation)
+              .build();
+      return RewrittenPrototypeDescription.create(ImmutableList.of(), null, argumentInfoCollection);
+    }
+
+    public boolean isEmpty() {
+      return newMethodSignatures.isEmpty();
+    }
+
+    public ProtoNormalizerGraphLens build() {
+      return new ProtoNormalizerGraphLens(appView, newMethodSignatures, prototypeChanges);
+    }
+  }
+}
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 78c8797..0c1e782 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -153,10 +153,6 @@
   private final Set<DexMethod> neverInlineDueToSingleCaller;
   /** Items for which to print inlining decisions for (testing only). */
   private final Set<DexMethod> whyAreYouNotInlining;
-  /** All methods that may not have any parameters with a constant value removed. */
-  private final Set<DexMethod> keepConstantArguments;
-  /** All methods that may not have any unused arguments removed. */
-  private final Set<DexMethod> keepUnusedArguments;
   /** All methods that must be reprocessed (testing only). */
   private final Set<DexMethod> reprocess;
   /** All methods that must not be reprocessed (testing only). */
@@ -228,8 +224,6 @@
       Set<DexMethod> alwaysInline,
       Set<DexMethod> neverInlineDueToSingleCaller,
       Set<DexMethod> whyAreYouNotInlining,
-      Set<DexMethod> keepConstantArguments,
-      Set<DexMethod> keepUnusedArguments,
       Set<DexMethod> reprocess,
       Set<DexMethod> neverReprocess,
       PredicateSet<DexType> alwaysClassInline,
@@ -265,8 +259,6 @@
     this.alwaysInline = alwaysInline;
     this.neverInlineDueToSingleCaller = neverInlineDueToSingleCaller;
     this.whyAreYouNotInlining = whyAreYouNotInlining;
-    this.keepConstantArguments = keepConstantArguments;
-    this.keepUnusedArguments = keepUnusedArguments;
     this.reprocess = reprocess;
     this.neverReprocess = neverReprocess;
     this.alwaysClassInline = alwaysClassInline;
@@ -310,8 +302,6 @@
         previous.alwaysInline,
         previous.neverInlineDueToSingleCaller,
         previous.whyAreYouNotInlining,
-        previous.keepConstantArguments,
-        previous.keepUnusedArguments,
         previous.reprocess,
         previous.neverReprocess,
         previous.alwaysClassInline,
@@ -360,8 +350,6 @@
         pruneMethods(previous.alwaysInline, prunedItems, executorService, futures),
         pruneMethods(previous.neverInlineDueToSingleCaller, prunedItems, executorService, futures),
         pruneMethods(previous.whyAreYouNotInlining, prunedItems, executorService, futures),
-        pruneMethods(previous.keepConstantArguments, prunedItems, executorService, futures),
-        pruneMethods(previous.keepUnusedArguments, prunedItems, executorService, futures),
         pruneMethods(previous.reprocess, prunedItems, executorService, futures),
         pruneMethods(previous.neverReprocess, prunedItems, executorService, futures),
         previous.alwaysClassInline,
@@ -567,8 +555,6 @@
         alwaysInline,
         neverInlineDueToSingleCaller,
         whyAreYouNotInlining,
-        keepConstantArguments,
-        keepUnusedArguments,
         reprocess,
         neverReprocess,
         alwaysClassInline,
@@ -649,8 +635,6 @@
     this.alwaysInline = previous.alwaysInline;
     this.neverInlineDueToSingleCaller = previous.neverInlineDueToSingleCaller;
     this.whyAreYouNotInlining = previous.whyAreYouNotInlining;
-    this.keepConstantArguments = previous.keepConstantArguments;
-    this.keepUnusedArguments = previous.keepUnusedArguments;
     this.reprocess = previous.reprocess;
     this.neverReprocess = previous.neverReprocess;
     this.alwaysClassInline = previous.alwaysClassInline;
@@ -681,7 +665,6 @@
             || getMissingClasses().contains(type)
             // TODO(b/150693139): Remove these exceptions once fixed.
             || InterfaceDesugaringSyntheticHelper.isCompanionClassType(type)
-            || InterfaceDesugaringSyntheticHelper.isEmulatedLibraryClassType(type)
             // TODO(b/150736225): Not sure how to remove these.
             || DesugaredLibraryAPIConverter.isVivifiedType(type)
         : "Failed lookup of non-missing type: " + type;
@@ -814,22 +797,6 @@
     return whyAreYouNotInlining.isEmpty();
   }
 
-  public boolean isKeepConstantArgumentsMethod(ProgramMethod method) {
-    return isKeepConstantArgumentsMethod(method.getReference());
-  }
-
-  public boolean isKeepConstantArgumentsMethod(DexMethod method) {
-    return keepConstantArguments.contains(method);
-  }
-
-  public boolean isKeepUnusedArgumentsMethod(ProgramMethod method) {
-    return isKeepUnusedArgumentsMethod(method.getReference());
-  }
-
-  public boolean isKeepUnusedArgumentsMethod(DexMethod method) {
-    return keepUnusedArguments.contains(method);
-  }
-
   public boolean isNeverReprocessMethod(ProgramMethod method) {
     return neverReprocess.contains(method.getReference())
         || method.getOptimizationInfo().hasBeenInlinedIntoSingleCallSite();
@@ -1285,8 +1252,6 @@
         lens.rewriteReferences(alwaysInline),
         lens.rewriteReferences(neverInlineDueToSingleCaller),
         lens.rewriteReferences(whyAreYouNotInlining),
-        lens.rewriteReferences(keepConstantArguments),
-        lens.rewriteReferences(keepUnusedArguments),
         lens.rewriteReferences(reprocess),
         lens.rewriteReferences(neverReprocess),
         alwaysClassInline.rewriteItems(lens::lookupType),
diff --git a/src/main/java/com/android/tools/r8/shaking/DependentMinimumKeepInfoCollection.java b/src/main/java/com/android/tools/r8/shaking/DependentMinimumKeepInfoCollection.java
index 8e48dfe..4c36623 100644
--- a/src/main/java/com/android/tools/r8/shaking/DependentMinimumKeepInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/shaking/DependentMinimumKeepInfoCollection.java
@@ -152,10 +152,14 @@
     DependentMinimumKeepInfoCollection rewrittenDependentMinimumKeepInfo =
         new DependentMinimumKeepInfoCollection();
     forEach(
-        (preconditionEvent, minimumKeepInfo) ->
+        (preconditionEvent, minimumKeepInfo) -> {
+          EnqueuerEvent rewrittenPreconditionEvent = preconditionEvent.rewrittenWithLens(graphLens);
+          if (!rewrittenPreconditionEvent.isNoSuchEvent()) {
             rewrittenDependentMinimumKeepInfo
-                .getOrCreateMinimumKeepInfoFor(preconditionEvent.rewrittenWithLens(graphLens))
-                .merge(minimumKeepInfo.rewrittenWithLens(graphLens)));
+                .getOrCreateMinimumKeepInfoFor(rewrittenPreconditionEvent)
+                .merge(minimumKeepInfo.rewrittenWithLens(graphLens));
+          }
+        });
     return rewrittenDependentMinimumKeepInfo;
   }
 }
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 658cfbf..1622376 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.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.graph.DexEncodedMethod.asProgramMethodOrNull;
 import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
 import static com.android.tools.r8.graph.FieldAccessInfoImpl.MISSING_FIELD_ACCESS_INFO;
 import static com.android.tools.r8.ir.desugar.LambdaDescriptor.isLambdaMetafactoryMethod;
@@ -48,6 +49,7 @@
 import com.android.tools.r8.graph.DexMember;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexMethodHandle;
+import com.android.tools.r8.graph.DexMethodSignature;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexReference;
@@ -142,6 +144,7 @@
 import com.android.tools.r8.utils.Timing;
 import com.android.tools.r8.utils.Visibility;
 import com.android.tools.r8.utils.WorkList;
+import com.android.tools.r8.utils.collections.DexMethodSignatureSet;
 import com.android.tools.r8.utils.collections.ProgramFieldSet;
 import com.android.tools.r8.utils.collections.ProgramMethodMap;
 import com.android.tools.r8.utils.collections.ProgramMethodSet;
@@ -764,12 +767,14 @@
     return getProgramClassOrNull(member.getHolderType(), context);
   }
 
+  private DexClass getClassOrNullFromReflectiveAccess(DexType type, ProgramDefinition context) {
+    // To avoid that we report reflectively accessed types as missing.
+    return definitionFor(type, context, this::recordNonProgramClass, this::ignoreMissingClass);
+  }
+
   private DexProgramClass getProgramClassOrNullFromReflectiveAccess(
       DexType type, ProgramDefinition context) {
-    // To avoid that we report reflectively accessed types as missing.
-    DexClass clazz =
-        definitionFor(type, context, this::recordNonProgramClass, this::ignoreMissingClass);
-    return clazz != null && clazz.isProgramClass() ? clazz.asProgramClass() : null;
+    return asProgramClassOrNull(getClassOrNullFromReflectiveAccess(type, context));
   }
 
   private void warnIfLibraryTypeInheritsFromProgramType(DexLibraryClass clazz) {
@@ -858,6 +863,8 @@
         }
         if (clazz.isExternalizable(appView)) {
           workList.enqueueMarkMethodLiveAction(defaultInitializer, defaultInitializer, witness);
+          applyMinimumKeepInfoWhenLiveOrTargeted(
+              defaultInitializer, KeepMethodInfo.newEmptyJoiner().disallowOptimization());
         }
       }
     }
@@ -899,6 +906,9 @@
     }
     if (clazz.hasDefaultInitializer()) {
       workList.enqueueMarkMethodLiveAction(clazz.getProgramDefaultInitializer(), clazz, reason);
+      applyMinimumKeepInfoWhenLiveOrTargeted(
+          clazz.getProgramDefaultInitializer(),
+          KeepMethodInfo.newEmptyJoiner().disallowOptimization());
     }
   }
 
@@ -1453,7 +1463,8 @@
     FieldResolutionResult resolutionResult = resolveField(fieldReference, currentMethod);
     fieldAccessAnalyses.forEach(
         analysis ->
-            analysis.traceInstanceFieldRead(fieldReference, resolutionResult, currentMethod));
+            analysis.traceInstanceFieldRead(
+                fieldReference, resolutionResult, currentMethod, workList));
 
     if (resolutionResult.isFailedOrUnknownResolution()) {
       // Must trace the types from the field reference even if it does not exist.
@@ -1511,7 +1522,8 @@
     FieldResolutionResult resolutionResult = resolveField(fieldReference, currentMethod);
     fieldAccessAnalyses.forEach(
         analysis ->
-            analysis.traceInstanceFieldWrite(fieldReference, resolutionResult, currentMethod));
+            analysis.traceInstanceFieldWrite(
+                fieldReference, resolutionResult, currentMethod, workList));
 
     if (resolutionResult.isFailedOrUnknownResolution()) {
       // Must trace the types from the field reference even if it does not exist.
@@ -1566,7 +1578,9 @@
 
     FieldResolutionResult resolutionResult = resolveField(fieldReference, currentMethod);
     fieldAccessAnalyses.forEach(
-        analysis -> analysis.traceStaticFieldRead(fieldReference, resolutionResult, currentMethod));
+        analysis ->
+            analysis.traceStaticFieldRead(
+                fieldReference, resolutionResult, currentMethod, workList));
 
     if (resolutionResult.isFailedOrUnknownResolution()) {
       // Must trace the types from the field reference even if it does not exist.
@@ -1638,7 +1652,8 @@
     FieldResolutionResult resolutionResult = resolveField(fieldReference, currentMethod);
     fieldAccessAnalyses.forEach(
         analysis ->
-            analysis.traceStaticFieldWrite(fieldReference, resolutionResult, currentMethod));
+            analysis.traceStaticFieldWrite(
+                fieldReference, resolutionResult, currentMethod, workList));
 
     if (resolutionResult.isFailedOrUnknownResolution()) {
       // Must trace the types from the field reference even if it does not exist.
@@ -3232,9 +3247,7 @@
                   applyMinimumKeepInfoWhenLive(clazz, preconditionEvent, minimumKeepInfo),
               (field, minimumKeepInfo) ->
                   applyMinimumKeepInfoWhenLive(field, preconditionEvent, minimumKeepInfo),
-              (method, minimumKeepInfo) ->
-                  applyMinimumKeepInfoWhenLiveOrTargeted(
-                      method, preconditionEvent, minimumKeepInfo));
+              this::applyMinimumKeepInfoWhenLiveOrTargeted);
     }
     enqueueAllIfNotShrinking();
     trace(executorService, timing);
@@ -3373,8 +3386,14 @@
 
   private void applyMinimumKeepInfoWhenLiveOrTargeted(
       ProgramMethod method,
-      EnqueuerEvent preconditionEvent,
       KeepMethodInfo.Joiner minimumKeepInfo) {
+    applyMinimumKeepInfoWhenLiveOrTargeted(method, minimumKeepInfo, EnqueuerEvent.unconditional());
+  }
+
+  private void applyMinimumKeepInfoWhenLiveOrTargeted(
+      ProgramMethod method,
+      KeepMethodInfo.Joiner minimumKeepInfo,
+      EnqueuerEvent preconditionEvent) {
     if (liveMethods.contains(method) || targetedMethods.contains(method)) {
       keepInfo.joinMethod(method, info -> info.merge(minimumKeepInfo));
     } else {
@@ -3405,7 +3424,7 @@
       ProgramMethod method,
       KeepMethodInfo.Joiner minimumKeepInfo) {
     if (isPreconditionForMinimumKeepInfoSatisfied(preconditionEvent)) {
-      applyMinimumKeepInfoWhenLiveOrTargeted(method, preconditionEvent, minimumKeepInfo);
+      applyMinimumKeepInfoWhenLiveOrTargeted(method, minimumKeepInfo, preconditionEvent);
     } else {
       dependentMinimumKeepInfo
           .getOrCreateMinimumKeepInfoFor(preconditionEvent)
@@ -3429,7 +3448,7 @@
               applyMinimumKeepInfoWhenLive(field, preconditionEvent, minimumKeepInfoForField),
           (method, minimumKeepInfoForMethod) ->
               applyMinimumKeepInfoWhenLiveOrTargeted(
-                  method, preconditionEvent, minimumKeepInfoForMethod));
+                  method, minimumKeepInfoForMethod, preconditionEvent));
     }
   }
 
@@ -3480,10 +3499,6 @@
       assert old == null || old == clazz;
     }
 
-    public void addLiveMethods(Iterable<ProgramMethod> methods) {
-      methods.forEach(this::addLiveMethod);
-    }
-
     public void addLiveMethod(ProgramMethod method) {
       DexMethod signature = method.getDefinition().getReference();
       ProgramMethod old = liveMethods.put(signature, method);
@@ -3500,18 +3515,6 @@
       newInterfaces.add(newInterface);
     }
 
-    void addLiveMethodWithKeepAction(
-        ProgramMethod method, Consumer<KeepMethodInfo.Joiner> keepAction) {
-      addLiveMethod(method);
-      liveMethodsWithKeepActions.add(new Pair<>(method, keepAction));
-    }
-
-    public ProgramMethodSet getLiveMethods() {
-      ProgramMethodSet set = ProgramMethodSet.create();
-      liveMethods.values().forEach(set::add);
-      return set;
-    }
-
     public void addMinimumKeepInfo(ProgramMethod method, Consumer<KeepMethodInfo.Joiner> consumer) {
       consumer.accept(
           minimumKeepInfo.computeIfAbsent(method, ignoreKey(KeepMethodInfo::newEmptyJoiner)));
@@ -3543,8 +3546,7 @@
 
       minimumKeepInfo.forEach(
           (method, minimumKeepInfoForMethod) ->
-              enqueuer.applyMinimumKeepInfoWhenLiveOrTargeted(
-                  method, UnconditionalKeepInfoEvent.get(), minimumKeepInfoForMethod));
+              enqueuer.applyMinimumKeepInfoWhenLiveOrTargeted(method, minimumKeepInfoForMethod));
     }
   }
 
@@ -3806,8 +3808,6 @@
             amendWithCompanionMethods(rootSet.alwaysInline),
             amendWithCompanionMethods(rootSet.neverInlineDueToSingleCaller),
             amendWithCompanionMethods(rootSet.whyAreYouNotInlining),
-            amendWithCompanionMethods(rootSet.keepConstantArguments),
-            amendWithCompanionMethods(rootSet.keepUnusedArguments),
             amendWithCompanionMethods(rootSet.reprocess),
             amendWithCompanionMethods(rootSet.neverReprocess),
             rootSet.alwaysClassInline,
@@ -4533,18 +4533,19 @@
       if (clazz == null) {
         return;
       }
-      DexEncodedMethod targetedMethodDefinition = clazz.lookupMethod(targetedMethodReference);
-      if (targetedMethodDefinition == null) {
+      ProgramMethod targetedMethod = clazz.lookupProgramMethod(targetedMethodReference);
+      if (targetedMethod == null) {
         return;
       }
-      ProgramMethod targetedMethod = new ProgramMethod(clazz, targetedMethodDefinition);
       KeepReason reason = KeepReason.reflectiveUseIn(method);
-      if (targetedMethodDefinition.isStatic() || targetedMethodDefinition.isInstanceInitializer()) {
+      if (targetedMethod.getDefinition().belongsToDirectPool()) {
         markMethodAsTargeted(targetedMethod, reason);
         markDirectStaticOrConstructorMethodAsLive(targetedMethod, reason);
       } else {
         markVirtualMethodAsLive(targetedMethod, reason);
       }
+      applyMinimumKeepInfoWhenLiveOrTargeted(
+          targetedMethod, KeepMethodInfo.newEmptyJoiner().disallowOptimization());
     }
   }
 
@@ -4574,6 +4575,8 @@
       markClassAsInstantiatedWithReason(clazz, reason);
       markMethodAsTargeted(defaultInitializer, reason);
       markDirectStaticOrConstructorMethodAsLive(defaultInitializer, reason);
+      applyMinimumKeepInfoWhenLiveOrTargeted(
+          defaultInitializer, KeepMethodInfo.newEmptyJoiner().disallowOptimization());
     }
   }
 
@@ -4675,6 +4678,8 @@
       markClassAsInstantiatedWithReason(clazz, reason);
       markMethodAsTargeted(initializer, reason);
       markDirectStaticOrConstructorMethodAsLive(initializer, reason);
+      applyMinimumKeepInfoWhenLiveOrTargeted(
+          initializer, KeepMethodInfo.newEmptyJoiner().disallowOptimization());
     }
   }
 
@@ -4797,6 +4802,7 @@
       DexType serviceType, ProgramMethod context, KeepReason reason) {
     List<DexType> serviceImplementationTypes =
         appView.appServices().serviceImplementationsFor(serviceType);
+    DexMethodSignatureSet serviceMethods = getServiceMethods(serviceType, context);
     for (DexType serviceImplementationType : serviceImplementationTypes) {
       if (!serviceImplementationType.isClassType()) {
         // Should never happen.
@@ -4805,12 +4811,46 @@
 
       DexProgramClass serviceImplementationClass =
           getProgramClassOrNull(serviceImplementationType, context);
-      if (serviceImplementationClass != null && serviceImplementationClass.isProgramClass()) {
-        markClassAsInstantiatedWithReason(serviceImplementationClass, reason);
+      if (serviceImplementationClass == null) {
+        continue;
+      }
+
+      markClassAsInstantiatedWithReason(serviceImplementationClass, reason);
+
+      ProgramMethod defaultInitializer = serviceImplementationClass.getProgramDefaultInitializer();
+      if (defaultInitializer != null) {
+        applyMinimumKeepInfoWhenLiveOrTargeted(
+            defaultInitializer, KeepMethodInfo.newEmptyJoiner().disallowOptimization());
+      }
+
+      for (DexMethodSignature serviceMethod : serviceMethods) {
+        ProgramMethod serviceImplementationMethod =
+            asProgramMethodOrNull(
+                serviceImplementationClass.getMethodCollection().getMethod(serviceMethod),
+                serviceImplementationClass);
+        if (serviceImplementationMethod != null) {
+          applyMinimumKeepInfoWhenLiveOrTargeted(
+              serviceImplementationMethod, KeepMethodInfo.newEmptyJoiner().disallowOptimization());
+        }
       }
     }
   }
 
+  private DexMethodSignatureSet getServiceMethods(DexType serviceType, ProgramMethod context) {
+    DexMethodSignatureSet serviceMethods = DexMethodSignatureSet.create();
+    WorkList<DexType> serviceTypes = WorkList.newIdentityWorkList(serviceType);
+    while (serviceTypes.hasNext()) {
+      DexType current = serviceTypes.next();
+      DexClass clazz = getClassOrNullFromReflectiveAccess(current, context);
+      if (clazz == null) {
+        continue;
+      }
+      clazz.forEachClassMethodMatching(DexEncodedMethod::belongsToVirtualPool, serviceMethods::add);
+      serviceTypes.addIfNotSeen(clazz.getInterfaces());
+    }
+    return serviceMethods;
+  }
+
   private static class SetWithReportedReason<T> {
 
     private final Set<T> items = Sets.newIdentityHashSet();
diff --git a/src/main/java/com/android/tools/r8/shaking/EnqueuerEvent.java b/src/main/java/com/android/tools/r8/shaking/EnqueuerEvent.java
index ee2b7d0..93d5f60 100644
--- a/src/main/java/com/android/tools/r8/shaking/EnqueuerEvent.java
+++ b/src/main/java/com/android/tools/r8/shaking/EnqueuerEvent.java
@@ -12,10 +12,18 @@
 
 public abstract class EnqueuerEvent {
 
+  public static UnconditionalKeepInfoEvent unconditional() {
+    return UnconditionalKeepInfoEvent.get();
+  }
+
   public DexDefinition getDefinition(DexDefinitionSupplier definitions) {
     return null;
   }
 
+  public boolean isNoSuchEvent() {
+    return false;
+  }
+
   public boolean isClassEvent() {
     return false;
   }
@@ -46,6 +54,27 @@
 
   public abstract EnqueuerEvent rewrittenWithLens(GraphLens lens);
 
+  public static class NoSuchEnqueuerEvent extends EnqueuerEvent {
+
+    private static final NoSuchEnqueuerEvent INSTANCE = new NoSuchEnqueuerEvent();
+
+    private NoSuchEnqueuerEvent() {}
+
+    public static NoSuchEnqueuerEvent get() {
+      return INSTANCE;
+    }
+
+    @Override
+    public boolean isNoSuchEvent() {
+      return true;
+    }
+
+    @Override
+    public EnqueuerEvent rewrittenWithLens(GraphLens lens) {
+      return this;
+    }
+  }
+
   public abstract static class ClassEnqueuerEvent extends EnqueuerEvent {
 
     private final DexType clazz;
@@ -96,7 +125,11 @@
 
     @Override
     public EnqueuerEvent rewrittenWithLens(GraphLens lens) {
-      return new LiveClassEnqueuerEvent(lens.lookupType(getType()));
+      DexType rewrittenType = lens.lookupType(getType());
+      if (rewrittenType.isIntType()) {
+        return NoSuchEnqueuerEvent.get();
+      }
+      return new LiveClassEnqueuerEvent(rewrittenType);
     }
 
     @Override
@@ -139,7 +172,11 @@
 
     @Override
     public EnqueuerEvent rewrittenWithLens(GraphLens lens) {
-      return new InstantiatedClassEnqueuerEvent(lens.lookupType(getType()));
+      DexType rewrittenType = lens.lookupType(getType());
+      if (rewrittenType.isIntType()) {
+        return NoSuchEnqueuerEvent.get();
+      }
+      return new InstantiatedClassEnqueuerEvent(rewrittenType);
     }
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java b/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java
index 1f4bc35..0efc4b5 100644
--- a/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java
+++ b/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java
@@ -257,6 +257,22 @@
     }
   }
 
+  static class TraceInvokeStaticAction extends EnqueuerAction {
+    private final DexMethod invokedMethod;
+    // TODO(b/175854431): Avoid pushing context on worklist.
+    private final ProgramMethod context;
+
+    TraceInvokeStaticAction(DexMethod invokedMethod, ProgramMethod context) {
+      this.invokedMethod = invokedMethod;
+      this.context = context;
+    }
+
+    @Override
+    public void run(Enqueuer enqueuer) {
+      enqueuer.traceInvokeStatic(invokedMethod, context);
+    }
+  }
+
   static class TraceMethodDefinitionExcludingCodeAction extends EnqueuerAction {
     private final ProgramMethod method;
 
@@ -364,6 +380,9 @@
   public abstract void enqueueTraceInvokeDirectAction(
       DexMethod invokedMethod, ProgramMethod context);
 
+  public abstract void enqueueTraceInvokeStaticAction(
+      DexMethod invokedMethod, ProgramMethod context);
+
   public abstract void enqueueTraceNewInstanceAction(DexType type, ProgramMethod context);
 
   public abstract void enqueueTraceStaticFieldRead(DexField field, ProgramMethod context);
@@ -478,6 +497,11 @@
     }
 
     @Override
+    public void enqueueTraceInvokeStaticAction(DexMethod invokedMethod, ProgramMethod context) {
+      queue.add(new TraceInvokeStaticAction(invokedMethod, context));
+    }
+
+    @Override
     public void enqueueTraceNewInstanceAction(DexType type, ProgramMethod context) {
       queue.add(new TraceNewInstanceAction(type, context));
     }
@@ -595,6 +619,11 @@
     }
 
     @Override
+    public void enqueueTraceInvokeStaticAction(DexMethod invokedMethod, ProgramMethod context) {
+      throw attemptToEnqueue();
+    }
+
+    @Override
     public void enqueueTraceNewInstanceAction(DexType type, ProgramMethod context) {
       throw attemptToEnqueue();
     }
diff --git a/src/main/java/com/android/tools/r8/shaking/ConstantArgumentRule.java b/src/main/java/com/android/tools/r8/shaking/KeepConstantArgumentRule.java
similarity index 83%
rename from src/main/java/com/android/tools/r8/shaking/ConstantArgumentRule.java
rename to src/main/java/com/android/tools/r8/shaking/KeepConstantArgumentRule.java
index bc33778..f234a09 100644
--- a/src/main/java/com/android/tools/r8/shaking/ConstantArgumentRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepConstantArgumentRule.java
@@ -7,10 +7,12 @@
 import com.android.tools.r8.position.Position;
 import java.util.List;
 
-public class ConstantArgumentRule extends ProguardConfigurationRule {
+public class KeepConstantArgumentRule extends NoOptimizationBaseRule<KeepConstantArgumentRule> {
+
+  public static final String RULE_NAME = "keepconstantarguments";
 
   public static class Builder
-      extends ProguardConfigurationRule.Builder<ConstantArgumentRule, Builder> {
+      extends NoOptimizationBaseRule.Builder<KeepConstantArgumentRule, Builder> {
 
     private Builder() {
       super();
@@ -22,8 +24,8 @@
     }
 
     @Override
-    public ConstantArgumentRule build() {
-      return new ConstantArgumentRule(
+    public KeepConstantArgumentRule build() {
+      return new KeepConstantArgumentRule(
           origin,
           getPosition(),
           source,
@@ -40,7 +42,7 @@
     }
   }
 
-  private ConstantArgumentRule(
+  private KeepConstantArgumentRule(
       Origin origin,
       Position position,
       String source,
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepInfo.java
index 9b83643..bcf9467 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepInfo.java
@@ -83,12 +83,6 @@
     return checkDiscarded;
   }
 
-  public boolean isParameterRemovalAllowed(GlobalKeepInfoConfiguration configuration) {
-    return isOptimizationAllowed(configuration)
-        && isShrinkingAllowed(configuration)
-        && !isCheckDiscardedEnabled(configuration);
-  }
-
   /**
    * True if an item must be present in the output.
    *
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 b43c44d..c82c666 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java
@@ -25,18 +25,24 @@
   }
 
   private final boolean allowClassInlining;
+  private final boolean allowConstantArgumentOptimization;
   private final boolean allowInlining;
   private final boolean allowMethodStaticizing;
   private final boolean allowParameterTypeStrengthening;
   private final boolean allowReturnTypeStrengthening;
+  private final boolean allowUnusedArgumentOptimization;
+  private final boolean allowUnusedReturnValueOptimization;
 
   private KeepMethodInfo(Builder builder) {
     super(builder);
     this.allowClassInlining = builder.isClassInliningAllowed();
+    this.allowConstantArgumentOptimization = builder.isConstantArgumentOptimizationAllowed();
     this.allowInlining = builder.isInliningAllowed();
     this.allowMethodStaticizing = builder.isMethodStaticizingAllowed();
     this.allowParameterTypeStrengthening = builder.isParameterTypeStrengtheningAllowed();
     this.allowReturnTypeStrengthening = builder.isReturnTypeStrengtheningAllowed();
+    this.allowUnusedArgumentOptimization = builder.isUnusedArgumentOptimizationAllowed();
+    this.allowUnusedReturnValueOptimization = builder.isUnusedReturnValueOptimizationAllowed();
   }
 
   // This builder is not private as there are known instances where it is safe to modify keep info
@@ -50,6 +56,12 @@
     return isParameterRemovalAllowed(configuration);
   }
 
+  public boolean isParameterRemovalAllowed(GlobalKeepInfoConfiguration configuration) {
+    return isOptimizationAllowed(configuration)
+        && isShrinkingAllowed(configuration)
+        && !isCheckDiscardedEnabled(configuration);
+  }
+
   public boolean isClassInliningAllowed(GlobalKeepInfoConfiguration configuration) {
     return isOptimizationAllowed(configuration) && internalIsClassInliningAllowed();
   }
@@ -58,6 +70,14 @@
     return allowClassInlining;
   }
 
+  public boolean isConstantArgumentOptimizationAllowed(GlobalKeepInfoConfiguration configuration) {
+    return isOptimizationAllowed(configuration) && internalIsConstantArgumentOptimizationAllowed();
+  }
+
+  boolean internalIsConstantArgumentOptimizationAllowed() {
+    return allowConstantArgumentOptimization;
+  }
+
   public boolean isInliningAllowed(GlobalKeepInfoConfiguration configuration) {
     return isOptimizationAllowed(configuration) && internalIsInliningAllowed();
   }
@@ -97,6 +117,26 @@
     return allowReturnTypeStrengthening;
   }
 
+  public boolean isUnusedArgumentOptimizationAllowed(GlobalKeepInfoConfiguration configuration) {
+    return isOptimizationAllowed(configuration)
+        && isShrinkingAllowed(configuration)
+        && internalIsUnusedArgumentOptimizationAllowed();
+  }
+
+  boolean internalIsUnusedArgumentOptimizationAllowed() {
+    return allowUnusedArgumentOptimization;
+  }
+
+  public boolean isUnusedReturnValueOptimizationAllowed(GlobalKeepInfoConfiguration configuration) {
+    return isOptimizationAllowed(configuration)
+        && isShrinkingAllowed(configuration)
+        && internalIsUnusedReturnValueOptimizationAllowed();
+  }
+
+  boolean internalIsUnusedReturnValueOptimizationAllowed() {
+    return allowUnusedReturnValueOptimization;
+  }
+
   public Joiner joiner() {
     assert !isTop();
     return new Joiner(this);
@@ -115,10 +155,13 @@
   public static class Builder extends KeepInfo.Builder<Builder, KeepMethodInfo> {
 
     private boolean allowClassInlining;
+    private boolean allowConstantArgumentOptimization;
     private boolean allowInlining;
     private boolean allowMethodStaticizing;
     private boolean allowParameterTypeStrengthening;
     private boolean allowReturnTypeStrengthening;
+    private boolean allowUnusedArgumentOptimization;
+    private boolean allowUnusedReturnValueOptimization;
 
     private Builder() {
       super();
@@ -127,12 +170,18 @@
     private Builder(KeepMethodInfo original) {
       super(original);
       allowClassInlining = original.internalIsClassInliningAllowed();
+      allowConstantArgumentOptimization = original.internalIsConstantArgumentOptimizationAllowed();
       allowInlining = original.internalIsInliningAllowed();
       allowMethodStaticizing = original.internalIsMethodStaticizingAllowed();
       allowParameterTypeStrengthening = original.internalIsParameterTypeStrengtheningAllowed();
       allowReturnTypeStrengthening = original.internalIsReturnTypeStrengtheningAllowed();
+      allowUnusedArgumentOptimization = original.internalIsUnusedArgumentOptimizationAllowed();
+      allowUnusedReturnValueOptimization =
+          original.internalIsUnusedReturnValueOptimizationAllowed();
     }
 
+    // Class inlining.
+
     public boolean isClassInliningAllowed() {
       return allowClassInlining;
     }
@@ -150,6 +199,27 @@
       return setAllowClassInlining(false);
     }
 
+    // Constant argument optimization.
+
+    public boolean isConstantArgumentOptimizationAllowed() {
+      return allowConstantArgumentOptimization;
+    }
+
+    public Builder setAllowConstantArgumentOptimization(boolean allowConstantArgumentOptimization) {
+      this.allowConstantArgumentOptimization = allowConstantArgumentOptimization;
+      return self();
+    }
+
+    public Builder allowConstantArgumentOptimization() {
+      return setAllowConstantArgumentOptimization(true);
+    }
+
+    public Builder disallowConstantArgumentOptimization() {
+      return setAllowConstantArgumentOptimization(false);
+    }
+
+    // Inlining.
+
     public boolean isInliningAllowed() {
       return allowInlining;
     }
@@ -167,6 +237,8 @@
       return setAllowInlining(false);
     }
 
+    // Method staticizing.
+
     public boolean isMethodStaticizingAllowed() {
       return allowMethodStaticizing;
     }
@@ -184,6 +256,8 @@
       return setAllowMethodStaticizing(false);
     }
 
+    // Parameter type strengthening.
+
     public boolean isParameterTypeStrengtheningAllowed() {
       return allowParameterTypeStrengthening;
     }
@@ -201,6 +275,8 @@
       return setAllowParameterTypeStrengthening(false);
     }
 
+    // Return type strengthening.
+
     public boolean isReturnTypeStrengtheningAllowed() {
       return allowReturnTypeStrengthening;
     }
@@ -218,6 +294,45 @@
       return setAllowReturnTypeStrengthening(false);
     }
 
+    // Unused argument optimization.
+
+    public boolean isUnusedArgumentOptimizationAllowed() {
+      return allowUnusedArgumentOptimization;
+    }
+
+    public Builder setAllowUnusedArgumentOptimization(boolean allowUnusedArgumentOptimization) {
+      this.allowUnusedArgumentOptimization = allowUnusedArgumentOptimization;
+      return self();
+    }
+
+    public Builder allowUnusedArgumentOptimization() {
+      return setAllowUnusedArgumentOptimization(true);
+    }
+
+    public Builder disallowUnusedArgumentOptimization() {
+      return setAllowUnusedArgumentOptimization(false);
+    }
+
+    // Unused return value optimization.
+
+    public boolean isUnusedReturnValueOptimizationAllowed() {
+      return allowUnusedReturnValueOptimization;
+    }
+
+    public Builder setAllowUnusedReturnValueOptimization(
+        boolean allowUnusedReturnValueOptimization) {
+      this.allowUnusedReturnValueOptimization = allowUnusedReturnValueOptimization;
+      return self();
+    }
+
+    public Builder allowUnusedReturnValueOptimization() {
+      return setAllowUnusedReturnValueOptimization(true);
+    }
+
+    public Builder disallowUnusedReturnValueOptimization() {
+      return setAllowUnusedReturnValueOptimization(false);
+    }
+
     @Override
     public Builder self() {
       return this;
@@ -242,11 +357,17 @@
     boolean internalIsEqualTo(KeepMethodInfo other) {
       return super.internalIsEqualTo(other)
           && isClassInliningAllowed() == other.internalIsClassInliningAllowed()
+          && isConstantArgumentOptimizationAllowed()
+              == other.internalIsConstantArgumentOptimizationAllowed()
           && isInliningAllowed() == other.internalIsInliningAllowed()
           && isMethodStaticizingAllowed() == other.internalIsMethodStaticizingAllowed()
           && isParameterTypeStrengtheningAllowed()
               == other.internalIsParameterTypeStrengtheningAllowed()
-          && isReturnTypeStrengtheningAllowed() == other.internalIsReturnTypeStrengtheningAllowed();
+          && isReturnTypeStrengtheningAllowed() == other.internalIsReturnTypeStrengtheningAllowed()
+          && isUnusedArgumentOptimizationAllowed()
+              == other.internalIsUnusedArgumentOptimizationAllowed()
+          && isUnusedReturnValueOptimizationAllowed()
+              == other.internalIsUnusedReturnValueOptimizationAllowed();
     }
 
     @Override
@@ -258,20 +379,26 @@
     public Builder makeTop() {
       return super.makeTop()
           .disallowClassInlining()
+          .disallowConstantArgumentOptimization()
           .disallowInlining()
           .disallowMethodStaticizing()
           .disallowParameterTypeStrengthening()
-          .disallowReturnTypeStrengthening();
+          .disallowReturnTypeStrengthening()
+          .disallowUnusedArgumentOptimization()
+          .disallowUnusedReturnValueOptimization();
     }
 
     @Override
     public Builder makeBottom() {
       return super.makeBottom()
           .allowClassInlining()
+          .allowConstantArgumentOptimization()
           .allowInlining()
           .allowMethodStaticizing()
           .allowParameterTypeStrengthening()
-          .allowReturnTypeStrengthening();
+          .allowReturnTypeStrengthening()
+          .allowUnusedArgumentOptimization()
+          .allowUnusedReturnValueOptimization();
     }
   }
 
@@ -286,6 +413,11 @@
       return self();
     }
 
+    public Joiner disallowConstantArgumentOptimization() {
+      builder.disallowConstantArgumentOptimization();
+      return self();
+    }
+
     public Joiner disallowInlining() {
       builder.disallowInlining();
       return self();
@@ -306,6 +438,16 @@
       return self();
     }
 
+    public Joiner disallowUnusedArgumentOptimization() {
+      builder.disallowUnusedArgumentOptimization();
+      return self();
+    }
+
+    public Joiner disallowUnusedReturnValueOptimization() {
+      builder.disallowUnusedReturnValueOptimization();
+      return self();
+    }
+
     @Override
     public Joiner asMethodJoiner() {
       return this;
@@ -316,6 +458,9 @@
       // Should be extended to merge the fields of this class in case any are added.
       return super.merge(joiner)
           .applyIf(!joiner.builder.isClassInliningAllowed(), Joiner::disallowClassInlining)
+          .applyIf(
+              !joiner.builder.isConstantArgumentOptimizationAllowed(),
+              Joiner::disallowConstantArgumentOptimization)
           .applyIf(!joiner.builder.isInliningAllowed(), Joiner::disallowInlining)
           .applyIf(!joiner.builder.isMethodStaticizingAllowed(), Joiner::disallowMethodStaticizing)
           .applyIf(
@@ -323,7 +468,13 @@
               Joiner::disallowParameterTypeStrengthening)
           .applyIf(
               !joiner.builder.isReturnTypeStrengtheningAllowed(),
-              Joiner::disallowReturnTypeStrengthening);
+              Joiner::disallowReturnTypeStrengthening)
+          .applyIf(
+              !joiner.builder.isUnusedArgumentOptimizationAllowed(),
+              Joiner::disallowUnusedArgumentOptimization)
+          .applyIf(
+              !joiner.builder.isUnusedReturnValueOptimizationAllowed(),
+              Joiner::disallowUnusedReturnValueOptimization);
     }
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/shaking/UnusedArgumentRule.java b/src/main/java/com/android/tools/r8/shaking/KeepUnusedArgumentRule.java
similarity index 83%
rename from src/main/java/com/android/tools/r8/shaking/UnusedArgumentRule.java
rename to src/main/java/com/android/tools/r8/shaking/KeepUnusedArgumentRule.java
index dd537bc..18ba2ff 100644
--- a/src/main/java/com/android/tools/r8/shaking/UnusedArgumentRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepUnusedArgumentRule.java
@@ -7,10 +7,12 @@
 import com.android.tools.r8.position.Position;
 import java.util.List;
 
-public class UnusedArgumentRule extends ProguardConfigurationRule {
+public class KeepUnusedArgumentRule extends NoOptimizationBaseRule<KeepUnusedArgumentRule> {
+
+  public static final String RULE_NAME = "keepunusedarguments";
 
   public static class Builder
-      extends ProguardConfigurationRule.Builder<UnusedArgumentRule, Builder> {
+      extends NoOptimizationBaseRule.Builder<KeepUnusedArgumentRule, Builder> {
 
     private Builder() {
       super();
@@ -22,8 +24,8 @@
     }
 
     @Override
-    public UnusedArgumentRule build() {
-      return new UnusedArgumentRule(
+    public KeepUnusedArgumentRule build() {
+      return new KeepUnusedArgumentRule(
           origin,
           getPosition(),
           source,
@@ -40,7 +42,7 @@
     }
   }
 
-  private UnusedArgumentRule(
+  private KeepUnusedArgumentRule(
       Origin origin,
       Position position,
       String source,
@@ -76,6 +78,6 @@
 
   @Override
   String typeString() {
-    return "keepunusedarguments";
+    return RULE_NAME;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/shaking/ConstantArgumentRule.java b/src/main/java/com/android/tools/r8/shaking/KeepUnusedReturnValueRule.java
similarity index 76%
copy from src/main/java/com/android/tools/r8/shaking/ConstantArgumentRule.java
copy to src/main/java/com/android/tools/r8/shaking/KeepUnusedReturnValueRule.java
index bc33778..2543285 100644
--- a/src/main/java/com/android/tools/r8/shaking/ConstantArgumentRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepUnusedReturnValueRule.java
@@ -1,29 +1,32 @@
-// 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.
+
 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 ConstantArgumentRule extends ProguardConfigurationRule {
+public class KeepUnusedReturnValueRule extends NoOptimizationBaseRule<KeepUnusedReturnValueRule> {
+
+  public static final String RULE_NAME = "keepunusedreturnvalue";
 
   public static class Builder
-      extends ProguardConfigurationRule.Builder<ConstantArgumentRule, Builder> {
+      extends NoOptimizationBaseRule.Builder<KeepUnusedReturnValueRule, Builder> {
 
-    private Builder() {
+    Builder() {
       super();
     }
 
     @Override
-    public Builder self() {
+    public KeepUnusedReturnValueRule.Builder self() {
       return this;
     }
 
     @Override
-    public ConstantArgumentRule build() {
-      return new ConstantArgumentRule(
+    public KeepUnusedReturnValueRule build() {
+      return new KeepUnusedReturnValueRule(
           origin,
           getPosition(),
           source,
@@ -40,7 +43,7 @@
     }
   }
 
-  private ConstantArgumentRule(
+  KeepUnusedReturnValueRule(
       Origin origin,
       Position position,
       String source,
@@ -76,6 +79,6 @@
 
   @Override
   String typeString() {
-    return "keepconstantarguments";
+    return RULE_NAME;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/shaking/L8TreePruner.java b/src/main/java/com/android/tools/r8/shaking/L8TreePruner.java
index 960ccd5..ab0a04d 100644
--- a/src/main/java/com/android/tools/r8/shaking/L8TreePruner.java
+++ b/src/main/java/com/android/tools/r8/shaking/L8TreePruner.java
@@ -28,9 +28,9 @@
 
   public L8TreePruner(InternalOptions options) {
     this.options = options;
-    backports.addAll(options.desugaredLibrarySpecification.getBackportCoreLibraryMember().keySet());
+    backports.addAll(options.machineDesugaredLibrarySpecification.getLegacyBackport().keySet());
     emulatedInterfaces.addAll(
-        options.desugaredLibrarySpecification.getEmulateLibraryInterface().keySet());
+        options.machineDesugaredLibrarySpecification.getEmulatedInterfaces().keySet());
   }
 
   public DexApplication prune(DexApplication app, PrefixRewritingMapper rewritePrefix) {
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 b1fdef2..0f2ad2c 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -450,13 +450,21 @@
           configurationBuilder.addRule(rule);
           return true;
         }
-        if (acceptString("keepconstantarguments")) {
-          ConstantArgumentRule rule = parseConstantArgumentRule(optionStart);
+        if (acceptString(KeepConstantArgumentRule.RULE_NAME)) {
+          KeepConstantArgumentRule rule =
+              parseNoOptimizationRule(optionStart, KeepConstantArgumentRule.builder());
           configurationBuilder.addRule(rule);
           return true;
         }
-        if (acceptString("keepunusedarguments")) {
-          UnusedArgumentRule rule = parseUnusedArgumentRule(optionStart);
+        if (acceptString(KeepUnusedArgumentRule.RULE_NAME)) {
+          KeepUnusedArgumentRule rule =
+              parseNoOptimizationRule(optionStart, KeepUnusedArgumentRule.builder());
+          configurationBuilder.addRule(rule);
+          return true;
+        }
+        if (acceptString(KeepUnusedReturnValueRule.RULE_NAME)) {
+          KeepUnusedReturnValueRule rule =
+              parseNoOptimizationRule(optionStart, KeepUnusedReturnValueRule.builder());
           configurationBuilder.addRule(rule);
           return true;
         }
@@ -918,10 +926,10 @@
           "Expecting '-keep' option after '-if' option.", origin, getPosition(optionStart)));
     }
 
-    private ConstantArgumentRule parseConstantArgumentRule(Position start)
+    private KeepConstantArgumentRule parseConstantArgumentRule(Position start)
         throws ProguardRuleParserException {
-      ConstantArgumentRule.Builder keepRuleBuilder =
-          ConstantArgumentRule.builder().setOrigin(origin).setStart(start);
+      KeepConstantArgumentRule.Builder keepRuleBuilder =
+          KeepConstantArgumentRule.builder().setOrigin(origin).setStart(start);
       parseClassSpec(keepRuleBuilder, false);
       Position end = getPosition();
       keepRuleBuilder.setSource(getSourceSnippet(contents, start, end));
@@ -929,10 +937,10 @@
       return keepRuleBuilder.build();
     }
 
-    private UnusedArgumentRule parseUnusedArgumentRule(Position start)
+    private KeepUnusedArgumentRule parseUnusedArgumentRule(Position start)
         throws ProguardRuleParserException {
-      UnusedArgumentRule.Builder keepRuleBuilder =
-          UnusedArgumentRule.builder().setOrigin(origin).setStart(start);
+      KeepUnusedArgumentRule.Builder keepRuleBuilder =
+          KeepUnusedArgumentRule.builder().setOrigin(origin).setStart(start);
       parseClassSpec(keepRuleBuilder, false);
       Position end = getPosition();
       keepRuleBuilder.setSource(getSourceSnippet(contents, start, end));
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 2f7d3f4..89b55dd 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
@@ -111,12 +111,9 @@
         new DependentMinimumKeepInfoCollection();
     private final LinkedHashMap<DexReference, DexReference> reasonAsked = new LinkedHashMap<>();
     private final Set<DexMethod> alwaysInline = Sets.newIdentityHashSet();
-    private final Set<DexMethod> neverInline = Sets.newIdentityHashSet();
     private final Set<DexMethod> neverInlineDueToSingleCaller = Sets.newIdentityHashSet();
     private final Set<DexMethod> bypassClinitforInlining = Sets.newIdentityHashSet();
     private final Set<DexMethod> whyAreYouNotInlining = Sets.newIdentityHashSet();
-    private final Set<DexMethod> keepParametersWithConstantValue = Sets.newIdentityHashSet();
-    private final Set<DexMethod> keepUnusedArguments = Sets.newIdentityHashSet();
     private final Set<DexMethod> reprocess = Sets.newIdentityHashSet();
     private final Set<DexMethod> neverReprocess = Sets.newIdentityHashSet();
     private final PredicateSet<DexType> alwaysClassInline = new PredicateSet<>();
@@ -267,11 +264,12 @@
       } else if (rule instanceof NoFieldTypeStrengtheningRule) {
         markMatchingFields(clazz, memberKeepRules, rule, null, ifRule);
       } else if (rule instanceof InlineRule
-          || rule instanceof ConstantArgumentRule
+          || rule instanceof KeepConstantArgumentRule
+          || rule instanceof KeepUnusedReturnValueRule
           || rule instanceof NoMethodStaticizingRule
           || rule instanceof NoParameterTypeStrengtheningRule
           || rule instanceof NoReturnTypeStrengtheningRule
-          || rule instanceof UnusedArgumentRule
+          || rule instanceof KeepUnusedArgumentRule
           || rule instanceof ReprocessMethodRule
           || rule instanceof WhyAreYouNotInliningRule) {
         markMatchingMethods(clazz, memberKeepRules, rule, null, ifRule);
@@ -364,18 +362,13 @@
             alwaysInline,
             bypassClinitforInlining);
       }
-      assert Sets.intersection(neverInline, alwaysInline).isEmpty()
-          : "A method cannot be marked as both -neverinline and -alwaysinline.";
       return new RootSet(
           dependentMinimumKeepInfo,
           ImmutableList.copyOf(reasonAsked.values()),
           alwaysInline,
-          neverInline,
           neverInlineDueToSingleCaller,
           bypassClinitforInlining,
           whyAreYouNotInlining,
-          keepParametersWithConstantValue,
-          keepUnusedArguments,
           reprocess,
           neverReprocess,
           alwaysClassInline,
@@ -456,7 +449,6 @@
 
     ConsequentRootSet buildConsequentRootSet() {
       return new ConsequentRootSet(
-          neverInline,
           neverInlineDueToSingleCaller,
           neverClassInline,
           dependentMinimumKeepInfo,
@@ -1301,11 +1293,6 @@
         }
       } else if (context instanceof ProguardIdentifierNameStringRule) {
         evaluateIdentifierNameStringRule(item, context, ifRule);
-      } else if (context instanceof ConstantArgumentRule) {
-        if (item.isMethod()) {
-          keepParametersWithConstantValue.add(item.asMethod().getReference());
-          context.markAsUsed();
-        }
       } else if (context instanceof ReprocessClassInitializerRule) {
         DexProgramClass clazz = item.asProgramClass();
         if (clazz != null && clazz.hasClassInitializer()) {
@@ -1336,11 +1323,27 @@
           }
           context.markAsUsed();
         }
-      } else if (context instanceof UnusedArgumentRule) {
-        if (item.isMethod()) {
-          keepUnusedArguments.add(item.asMethod().getReference());
-          context.markAsUsed();
-        }
+      } else if (context instanceof KeepConstantArgumentRule) {
+        assert item.isProgramMethod();
+        dependentMinimumKeepInfo
+            .getOrCreateUnconditionalMinimumKeepInfoFor(item.getReference())
+            .asMethodJoiner()
+            .disallowConstantArgumentOptimization();
+        context.markAsUsed();
+      } else if (context instanceof KeepUnusedArgumentRule) {
+        assert item.isProgramMethod();
+        dependentMinimumKeepInfo
+            .getOrCreateUnconditionalMinimumKeepInfoFor(item.getReference())
+            .asMethodJoiner()
+            .disallowUnusedArgumentOptimization();
+        context.markAsUsed();
+      } else if (context instanceof KeepUnusedReturnValueRule) {
+        assert item.isProgramMethod();
+        dependentMinimumKeepInfo
+            .getOrCreateUnconditionalMinimumKeepInfoFor(item.getReference())
+            .asMethodJoiner()
+            .disallowUnusedReturnValueOptimization();
+        context.markAsUsed();
       } else {
         throw new Unreachable();
       }
@@ -1356,7 +1359,8 @@
       }
     }
 
-    private void evaluateCheckDiscardRule(DexProgramClass clazz, ProguardCheckDiscardRule rule) {
+    private synchronized void evaluateCheckDiscardRule(
+        DexProgramClass clazz, ProguardCheckDiscardRule rule) {
       if (rule.getMemberRules().isEmpty()) {
         evaluateCheckDiscardClassAndAllMembersRule(clazz, rule);
       } else if (clazz.hasFields() || clazz.hasMethods()) {
@@ -1673,8 +1677,6 @@
     public final Set<DexMethod> alwaysInline;
     public final Set<DexMethod> bypassClinitForInlining;
     public final Set<DexMethod> whyAreYouNotInlining;
-    public final Set<DexMethod> keepConstantArguments;
-    public final Set<DexMethod> keepUnusedArguments;
     public final Set<DexMethod> reprocess;
     public final Set<DexMethod> neverReprocess;
     public final PredicateSet<DexType> alwaysClassInline;
@@ -1692,12 +1694,9 @@
         DependentMinimumKeepInfoCollection dependentMinimumKeepInfo,
         ImmutableList<DexReference> reasonAsked,
         Set<DexMethod> alwaysInline,
-        Set<DexMethod> neverInline,
         Set<DexMethod> neverInlineDueToSingleCaller,
         Set<DexMethod> bypassClinitForInlining,
         Set<DexMethod> whyAreYouNotInlining,
-        Set<DexMethod> keepConstantArguments,
-        Set<DexMethod> keepUnusedArguments,
         Set<DexMethod> reprocess,
         Set<DexMethod> neverReprocess,
         PredicateSet<DexType> alwaysClassInline,
@@ -1725,8 +1724,6 @@
       this.alwaysInline = alwaysInline;
       this.bypassClinitForInlining = bypassClinitForInlining;
       this.whyAreYouNotInlining = whyAreYouNotInlining;
-      this.keepConstantArguments = keepConstantArguments;
-      this.keepUnusedArguments = keepUnusedArguments;
       this.reprocess = reprocess;
       this.neverReprocess = neverReprocess;
       this.alwaysClassInline = alwaysClassInline;
@@ -1827,6 +1824,35 @@
       }
     }
 
+    public RootSet rewrittenWithLens(GraphLens graphLens) {
+      if (graphLens.isIdentityLens()) {
+        return this;
+      }
+      return new RootSet(
+          getDependentMinimumKeepInfo().rewrittenWithLens(graphLens),
+          reasonAsked,
+          alwaysInline,
+          neverInlineDueToSingleCaller,
+          bypassClinitForInlining,
+          whyAreYouNotInlining,
+          reprocess,
+          neverReprocess,
+          alwaysClassInline,
+          neverClassInline,
+          noUnusedInterfaceRemoval,
+          noVerticalClassMerging,
+          noHorizontalClassMerging,
+          neverPropagateValue,
+          mayHaveSideEffects,
+          noSideEffects,
+          assumedValues,
+          dependentKeepClassCompatRule,
+          identifierNameStrings,
+          ifRules,
+          delayedRootSetActionItems,
+          pendingMethodMoveInverse);
+    }
+
     void shouldNotBeMinified(ProgramDefinition definition) {
       getDependentMinimumKeepInfo()
           .getOrCreateUnconditionalMinimumKeepInfoFor(definition.getReference())
@@ -1911,7 +1937,6 @@
 
     public boolean verifyKeptItemsAreKept(AppView<? extends AppInfoWithClassHierarchy> appView) {
       AppInfoWithClassHierarchy appInfo = appView.appInfo();
-      GraphLens lens = appView.graphLens();
       // Create a mapping from each required type to the set of required members on that type.
       Map<DexType, Set<DexMember<?, ?>>> requiredMembersPerType = new IdentityHashMap<>();
       getDependentMinimumKeepInfo()
@@ -1921,21 +1946,16 @@
               (reference, minimumKeepInfo) -> {
                 if (reference.isDexType()) {
                   DexType type = reference.asDexType();
-                  DexType rewrittenType = lens.lookupType(type);
-                  assert !appInfo.hasLiveness() || appInfo.withLiveness().isPinned(rewrittenType)
-                      : "Expected reference `" + rewrittenType.toSourceString() + "` to be pinned";
-                  requiredMembersPerType.computeIfAbsent(
-                      rewrittenType, key -> Sets.newIdentityHashSet());
+                  assert !appInfo.hasLiveness() || appInfo.withLiveness().isPinned(type)
+                      : "Expected reference `" + type.toSourceString() + "` to be pinned";
+                  requiredMembersPerType.computeIfAbsent(type, key -> Sets.newIdentityHashSet());
                 } else {
                   DexMember<?, ?> member = reference.asDexMember();
-                  DexMember<?, ?> rewrittenMember = lens.getRenamedMemberSignature(member);
-                  assert !appInfo.hasLiveness() || appInfo.withLiveness().isPinned(rewrittenMember)
-                      : "Expected reference `"
-                          + rewrittenMember.toSourceString()
-                          + "` to be pinned";
+                  assert !appInfo.hasLiveness() || appInfo.withLiveness().isPinned(member)
+                      : "Expected reference `" + member.toSourceString() + "` to be pinned";
                   requiredMembersPerType
-                      .computeIfAbsent(rewrittenMember.holder, key -> Sets.newIdentityHashSet())
-                      .add(rewrittenMember);
+                      .computeIfAbsent(member.holder, key -> Sets.newIdentityHashSet())
+                      .add(member);
                 }
               });
 
@@ -2044,7 +2064,6 @@
   public static class ConsequentRootSet extends RootSetBase {
 
     ConsequentRootSet(
-        Set<DexMethod> neverInline,
         Set<DexMethod> neverInlineDueToSingleCaller,
         Set<DexType> neverClassInline,
         DependentMinimumKeepInfoCollection dependentMinimumKeepInfo,
@@ -2110,9 +2129,6 @@
           Collections.emptySet(),
           Collections.emptySet(),
           Collections.emptySet(),
-          Collections.emptySet(),
-          Collections.emptySet(),
-          Collections.emptySet(),
           PredicateSet.empty(),
           Collections.emptySet(),
           Collections.emptySet(),
@@ -2141,6 +2157,7 @@
       // Do nothing.
     }
 
+    @Override
     public MainDexRootSet rewrittenWithLens(GraphLens graphLens) {
       if (graphLens.isIdentityLens()) {
         return this;
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 19f0adc..0ad5ad4 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -14,6 +14,7 @@
 import com.android.tools.r8.androidapi.ComputedApiLevel;
 import com.android.tools.r8.cf.CfVersion;
 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;
 import com.android.tools.r8.graph.CfCode;
@@ -53,13 +54,13 @@
 import com.android.tools.r8.graph.ObjectAllocationInfoCollection;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.PrunedItems;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription;
 import com.android.tools.r8.graph.SubtypingInfo;
 import com.android.tools.r8.graph.TopDownClassHierarchyTraversal;
 import com.android.tools.r8.graph.TreeFixerBase;
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.graph.UseRegistryWithResult;
 import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
+import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
 import com.android.tools.r8.ir.code.Invoke.Type;
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.MemberPoolCollection.MemberPool;
@@ -990,25 +991,42 @@
                 && !virtualMethods.containsKey(wrapped);
           };
 
-      for (DexEncodedMethod directMethod : source.directMethods()) {
-        if (directMethod.isInstanceInitializer()) {
-          DexEncodedMethod resultingConstructor =
-              renameConstructor(directMethod, availableMethodSignatures);
-          add(directMethods, resultingConstructor, MethodSignatureEquivalence.get());
-          blockRedirectionOfSuperCalls(resultingConstructor.getReference());
-        } else {
-          DexEncodedMethod resultingDirectMethod =
-              renameMethod(
-                  directMethod,
-                  availableMethodSignatures,
-                  directMethod.isClassInitializer() ? Rename.NEVER : Rename.IF_NEEDED);
-          add(directMethods, resultingDirectMethod, MethodSignatureEquivalence.get());
-          deferredRenamings.map(directMethod.getReference(), resultingDirectMethod.getReference());
-          deferredRenamings.recordMove(
-              directMethod.getReference(), resultingDirectMethod.getReference());
-          blockRedirectionOfSuperCalls(resultingDirectMethod.getReference());
-        }
-      }
+      source.forEachProgramDirectMethod(
+          directMethod -> {
+            DexEncodedMethod definition = directMethod.getDefinition();
+            if (definition.isInstanceInitializer()) {
+              DexEncodedMethod resultingConstructor =
+                  renameConstructor(definition, availableMethodSignatures);
+              add(directMethods, resultingConstructor, MethodSignatureEquivalence.get());
+              blockRedirectionOfSuperCalls(resultingConstructor.getReference());
+            } else {
+              DexEncodedMethod resultingDirectMethod =
+                  renameMethod(
+                      definition,
+                      availableMethodSignatures,
+                      definition.isClassInitializer() ? Rename.NEVER : Rename.IF_NEEDED);
+              add(directMethods, resultingDirectMethod, MethodSignatureEquivalence.get());
+              deferredRenamings.map(
+                  directMethod.getReference(), resultingDirectMethod.getReference());
+              deferredRenamings.recordMove(
+                  directMethod.getReference(), resultingDirectMethod.getReference());
+              blockRedirectionOfSuperCalls(resultingDirectMethod.getReference());
+
+              // Private methods in the parent class may be targeted with invoke-super if the two
+              // classes are in the same nest. Ensure such calls are mapped to invoke-direct.
+              if (definition.isInstance()
+                  && definition.isPrivate()
+                  && AccessControl.isMemberAccessible(directMethod, source, target, appView)
+                      .isTrue()) {
+                deferredRenamings.mapVirtualMethodToDirectInType(
+                    directMethod.getReference(),
+                    prototypeChanges ->
+                        new MethodLookupResult(
+                            resultingDirectMethod.getReference(), null, DIRECT, prototypeChanges),
+                    target.getType());
+              }
+            }
+          });
 
       for (DexEncodedMethod virtualMethod : source.virtualMethods()) {
         DexEncodedMethod shadowedBy = findMethodInTarget(virtualMethod);
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java
index 37965ec..166ed69 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java
@@ -12,8 +12,8 @@
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.NestedGraphLens;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription;
 import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
+import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
 import com.android.tools.r8.ir.code.Invoke.Type;
 import com.android.tools.r8.utils.IterableUtils;
 import com.android.tools.r8.utils.collections.BidirectionalManyToOneRepresentativeHashMap;
diff --git a/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java b/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java
index bbf4258..604748a 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java
@@ -129,12 +129,7 @@
       return;
     }
     assert hygienicType.toSourceString().startsWith(synthesizingContextType.toSourceString());
-    DexType rewrittenContext =
-        appView
-            .options()
-            .desugaredLibrarySpecification
-            .getEmulateLibraryInterface()
-            .get(synthesizingContextType);
+    DexType rewrittenContext = appView.rewritePrefix.rewrittenContextType(synthesizingContextType);
     if (rewrittenContext == null) {
       return;
     }
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
index 0cb0137..6d45ee1 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -483,8 +483,7 @@
       AppView<?> appView,
       DexType type) {
     DexType rewrittenContextType =
-        appView.rewritePrefix.rewrittenContextType(
-            outerContext.getSynthesizingContextType(), appView);
+        appView.rewritePrefix.rewrittenContextType(outerContext.getSynthesizingContextType());
     if (rewrittenContextType == null) {
       return;
     }
diff --git a/src/main/java/com/android/tools/r8/utils/AssertionConfigurationWithDefault.java b/src/main/java/com/android/tools/r8/utils/AssertionConfigurationWithDefault.java
index df3f944..a0a5836 100644
--- a/src/main/java/com/android/tools/r8/utils/AssertionConfigurationWithDefault.java
+++ b/src/main/java/com/android/tools/r8/utils/AssertionConfigurationWithDefault.java
@@ -7,12 +7,16 @@
 import com.android.tools.r8.AssertionsConfiguration;
 import com.android.tools.r8.AssertionsConfiguration.AssertionTransformation;
 import com.android.tools.r8.AssertionsConfiguration.AssertionTransformationScope;
+import com.android.tools.r8.references.MethodReference;
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
 import java.util.List;
 
 public class AssertionConfigurationWithDefault {
 
   public final AssertionsConfiguration defaultConfiguration;
   public final List<AssertionsConfiguration> assertionsConfigurations;
+  private final List<MethodReference> allAssertionHandlers;
 
   public AssertionConfigurationWithDefault(
       AssertionsConfiguration defautlTransformation,
@@ -20,6 +24,7 @@
     this.defaultConfiguration = defautlTransformation;
     assert assertionsConfigurations != null;
     this.assertionsConfigurations = assertionsConfigurations;
+    this.allAssertionHandlers = computeAllAssertionHandlers();
   }
 
   public boolean isPassthroughAll() {
@@ -31,4 +36,24 @@
         && assertionsConfigurations.get(0).getTransformation()
             == AssertionTransformation.PASSTHROUGH;
   }
+
+  public List<MethodReference> getAllAssertionHandlers() {
+    return allAssertionHandlers;
+  }
+
+  private List<MethodReference> computeAllAssertionHandlers() {
+    assert !defaultConfiguration.isAssertionHandler();
+    if (assertionsConfigurations.isEmpty()) {
+      return ImmutableList.of();
+    }
+    List<MethodReference> result = new ArrayList<>();
+    assertionsConfigurations.forEach(
+        assertionsConfiguration -> {
+          if (assertionsConfiguration.isAssertionHandler()
+              && !result.contains(assertionsConfiguration.getAssertionHandler())) {
+            result.add(assertionsConfiguration.getAssertionHandler());
+          }
+        });
+    return result;
+  }
 }
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 bc88b16..467ac25 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -54,8 +54,11 @@
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.desugar.PrefixRewritingMapper;
 import com.android.tools.r8.ir.desugar.PrefixRewritingMapper.MachineDesugarPrefixRewritingMapper;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecification;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion.HumanToMachineSpecificationConverter;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion.LegacyToHumanSpecificationConverter;
 import com.android.tools.r8.ir.desugar.nest.Nest;
 import com.android.tools.r8.ir.optimize.Inliner;
 import com.android.tools.r8.ir.optimize.enums.EnumDataMap;
@@ -85,7 +88,9 @@
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
+import java.io.IOException;
 import java.io.PrintStream;
+import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
@@ -390,8 +395,8 @@
     if (isGeneratingDex() || desugarState == DesugarState.ON) {
       marker.setMinApi(getMinApiLevel().getLevel());
     }
-    if (desugaredLibrarySpecification.getIdentifier() != null) {
-      marker.setDesugaredLibraryIdentifiers(desugaredLibrarySpecification.getIdentifier());
+    if (machineDesugaredLibrarySpecification.getIdentifier() != null) {
+      marker.setDesugaredLibraryIdentifiers(machineDesugaredLibrarySpecification.getIdentifier());
     }
     if (Version.isDevelopmentVersion()) {
       marker.setSha1(VersionProperties.INSTANCE.getSha());
@@ -438,7 +443,7 @@
   }
 
   public boolean isDesugaredLibraryCompilation() {
-    return desugaredLibrarySpecification.isLibraryCompilation();
+    return machineDesugaredLibrarySpecification.isLibraryCompilation();
   }
 
   public boolean isRelocatorCompilation() {
@@ -888,13 +893,51 @@
   public LegacyDesugaredLibrarySpecification desugaredLibrarySpecification =
       LegacyDesugaredLibrarySpecification.empty();
 
-  public PrefixRewritingMapper getPrefixRewritingMapper() {
-    if (testing.machineDesugaredLibrarySpecification != null) {
-      return new MachineDesugarPrefixRewritingMapper(
-          desugaredLibrarySpecification.getPrefixRewritingMapper(),
-          testing.machineDesugaredLibrarySpecification.getRewritingFlags());
+  public void setDesugaredLibrarySpecification(
+      LegacyDesugaredLibrarySpecification specification, AndroidApp app) {
+    if (specification.isEmptyConfiguration()) {
+      return;
     }
-    return desugaredLibrarySpecification.getPrefixRewritingMapper();
+    desugaredLibrarySpecification = specification;
+    try {
+      HumanDesugaredLibrarySpecification human =
+          new LegacyToHumanSpecificationConverter()
+              .convert(specification, app.getLibraryResourceProviders(), this);
+      machineDesugaredLibrarySpecification =
+          new HumanToMachineSpecificationConverter()
+              .convert(
+                  human,
+                  specification.isLibraryCompilation() ? app.getProgramResourceProviders() : null,
+                  app.getLibraryResourceProviders(),
+                  this);
+    } catch (IOException e) {
+      reporter.error(new ExceptionDiagnostic(e, Origin.unknown()));
+    }
+  }
+
+  public void setDesugaredLibrarySpecificationForTesting(
+      LegacyDesugaredLibrarySpecification specification, Path desugaredJDKLib, Path library)
+      throws IOException {
+    desugaredLibrarySpecification = specification;
+    HumanDesugaredLibrarySpecification human =
+        new LegacyToHumanSpecificationConverter().convert(specification, library, this);
+    machineDesugaredLibrarySpecification =
+        new HumanToMachineSpecificationConverter()
+            .convert(
+                human,
+                specification.isLibraryCompilation() ? desugaredJDKLib : null,
+                library,
+                this);
+  }
+
+  // Meant to replace desugaredLibrarySpecification, set only from tests at the moment.
+  public MachineDesugaredLibrarySpecification machineDesugaredLibrarySpecification =
+      MachineDesugaredLibrarySpecification.empty();
+
+  public PrefixRewritingMapper getPrefixRewritingMapper() {
+    return machineDesugaredLibrarySpecification.getRewriteType().isEmpty()
+        ? PrefixRewritingMapper.empty()
+        : new MachineDesugarPrefixRewritingMapper(machineDesugaredLibrarySpecification);
   }
 
   public boolean relocatorCompilation = false;
@@ -1599,9 +1642,6 @@
 
     public Consumer<Deque<ProgramMethodSet>> waveModifier = waves -> {};
 
-    // Meant to replace desugaredLibrarySpecification, set only from tests at the moment.
-    public MachineDesugaredLibrarySpecification machineDesugaredLibrarySpecification = null;
-
     /**
      * If this flag is enabled, we will also compute the set of possible targets for invoke-
      * interface and invoke-virtual instructions that target a library method, and add the
@@ -1637,6 +1677,7 @@
     public boolean enableEnumUnboxingDebugLogs = false;
     public boolean forceRedundantConstNumberRemoval = false;
     public boolean enableExperimentalDesugaredLibraryKeepRuleGenerator = false;
+    public boolean enableExperimentalProtoNormalization = false;
     public boolean invertConditionals = false;
     public boolean placeExceptionalBlocksLast = false;
     public boolean dontCreateMarkerInD8 = false;
diff --git a/src/main/resources/api_database/api_database_ambiguous.txt b/src/main/resources/api_database/api_database_ambiguous.txt
index 5c4f00c..47dfde9 100644
--- a/src/main/resources/api_database/api_database_ambiguous.txt
+++ b/src/main/resources/api_database/api_database_ambiguous.txt
@@ -1,113 +1,33 @@
-ebf47efadc4c50002b13da0b1cef0646:18
-106de6ca4d0e49988f3328ce380ff517:18
-2f4aedd17528c30f8a9f16b030ff708b:18
-93aca156e3f15799914f868852723e47:18
-17cbc97604a781acb78c668b2437ffbd:18
-1709449e191fe131236bd0b8c1928b71:18
-a6cf882fc575147513b7b1b505fdd335:18
-1587f690fd5c0c886630334e89e397ff:18
-dc6ad4bf6892204b9fca73d1ee9cab04:18
-9ae673cfbb5d90688edafbf7ec2862e4:18
-acf250fcdbf54725219af8c6a8a614d9:18
-07319a58381faa784dda046d5e8fd8ae:18
-77e978f6318d01be894de8944fdc05b4:18
-81be9a78d26d25c6ff47c7e9af30a92e:18
-e87bf9d0084b39c443e421cdb5d3fc43:18
-9e973ee7f4f5ad967d2ba5bf5d88911c:18
-b47e7bbf50ad208599056f8d0f1cc5ba:18
-47a9e25289b4ece230adcc3b60797712:18
-42e4cc1882d334757afbe74f4d8cec8e:18
-024481748991b858bf7e03b0b87ce125:18
-b6ced7e04b527d83cd660894a0059ea7:18
-717e536c3a13f3602fbaf1f607ee2963:18
-18b540e31006aad6ae7a26d967ec0e0e:18
-f1483fe126d64bd7544dba7fdf0559f4:18
-3c4a0d8270a52e43316a711a2fe83268:18
-2e13bb1bfba423473a6efba65d9c8190:18
-47a5d9010bdeb561176cf070ef483e91:18
-4f2e7d32ed2c7ead316770fbe84d8aae:18
-c625e1090fc4d72afd0673839af5d46f:18
-4cc2dc623ee702db7a5899c5c401a98c:18
-780c7e0f80b5479d00e2c3679974644d:18
-eff51a62305642c720d163085061ed63:18
-69e416f1afc250d6239788b4b1fb9bb4:18
-d71a32697989404ae782e105a5eb5e4c:18
-6d60d78c1ce897131dcf45743a8d6e49:18
-e16e8dcca650cf79557cb76a86edb4e9:18
-bfebe4871ed9c4b5f3300fe8dfa2a0a3:18
-93e215f230901f20bdca460cfe8c42af:18
-8efacada80287ebb7607758a0a82b0a0:18
-487bc1228fd817b641056a1a008984f8:18
-dc6c2915cd6f0a6fd0ab01365349599d:18
-1a3dbf72e4bd135a621b8ad78a7151c1:18
-01fd60d48ba7e1434bb72696282c7113:18
-83c42b475763d64935a9ddfc6a374b64:18
-ed9a26aaf7ddb748bd1e624aeb4fdb81:18
-eeb05b41976f7bb96572330854aaab34:18
-3c17437ba337e7ce8edad092f745a363:18
-8d8fd1ffc3ae24cb43798f4eb41fc4de:18
-003f7ab7088710994a289bab815aacbf:18
-488d943cdcedfca2b076152769fc3c51:18
-cab779407e29ba3377e6a47a4f842ec5:18
-a0ddcf0f7bb4aa9d38db983941f6908d:18
-b64b41d26010783cae2dff88c7e21f1d:18
-aac6d054ad0f9bea1f64702aea698ee3:18
-90b4163293f9e35f7091692b31d3b5d7:18
-9d66fceb1054d5e2ff207591ec216b70:18
-93a65566b576af220207e30cc89ef490:18
-b3248e6a2d819bd8bb42a15179188c52:18
-f95f0545feb040d2d6bfcad6897364d0:18
-058ec3044e95ed7fcaff93a2f3105d6f:18
-39ef3516cb80752cac1c541b43a1c2fa:18
-c1295d79d79340152c6b98f1c9003e8c:18
-c4b4a2994711cb342b0c8c8ef409ce8b:18
-719c3b73816fa0db79747ea264abab5a:18
-1d7847b88e0feaecd299b3c5c18be763:18
-1211e656a8eaa4423e7678533a1e7e2a:18
-e0c7417a74df21b55367f9e1ea6c2d37:18
-e49b0a2bd830bb3b96934490b2bc6ebe:18
-91790d7c5cf146ab3936919184a70015:18
-ec36ea4bb9634f7ec5c4d3c1cc9dd71b:18
-860e7b1a68c02eef7d2e2d7b6a3e13b5:18
-458fda8d9863be24862bb62c910f427d:18
-e05ed0452c12e62ef74e9161159b1231:18
-9419764761c059b9c2f9cf640abfdeb0:18
-ea02175c4ef64b6370f619acd84f3c3c:18
-0b01d40e1ca37aa854e77c98cc153e77:18
-eb12f3e690b201aa171d794a8288af10:18
-b955e5418d490425f99d58f441ba4525:18
-b36bec8fd49158c020b6d8c09cc02baf:26
-e7ca6d1abe4cd55a491cdf58eb8f4602:17
 d8c43560e159bcb899c2180b8e01d8e3:24
-12233f83b06081671815562dce938d3b:24
 ae9c7aee12dfaaa56e43834dcea91f5f:24
 106252e3f31ee17239e1ee4184147deb:24
+12233f83b06081671815562dce938d3b:24
 1fb2cd38813bad6e5eeef74bcbcb133d:24
 c5bbd4174eee5cb68830801317071dd5:24
 b876313caea388f03200ac720ed528af:24
-dc32c09ecdd9bda4703aa944cf1eb4ca:24
 f4fa53a97c83899fd2da9f4112f35339:24
+dc32c09ecdd9bda4703aa944cf1eb4ca:24
 07a59dae4166f6d504bb0793c5e75178:24
 efcb5024ce3514453d44735222903a55:24
 43445dc9e50e2c4fc327097d07ff749a:24
 bcb1787dd2def454fd3f31d1b2c0967a:24
-2cc65559141cece33ffdbc4e34a2ce3b:24
 cace738770550444ee1f91000c32c147:24
+2cc65559141cece33ffdbc4e34a2ce3b:24
 b55a09768f05f29b15e1ff3a9feb7c92:24
 54282406a85f4410d4dfc72d5873ee96:24
 1c38913c447705e315ec4486da558c9d:24
 ca3f30645fa7b480b213f4228386a09a:24
 6d8800c5cc8ad9e4f3f28e777207a870:24
 b8dc6f9b9587159f978d643aeb8e4d07:24
-91eb697571f68f5850875286c5e36080:24
 44c0d27dc28d4da28655c3c62a0d7cac:24
+91eb697571f68f5850875286c5e36080:24
 ae908f7abd7d347b0bf39fead3531c12:24
 145e730e204fdca9cf169521a3812ed1:24
 d27ceaaa92270c450aeee582cc17b985:24
 294141fdc8f17ac3c7e4d8687d8ad389:24
 48de820418038280f70eca64f878c67b:24
-8569110aa82eb44ff0b12819867c66f8:24
 cfde9ea95c663bed6d56df8778b01b79:24
+8569110aa82eb44ff0b12819867c66f8:24
 f389ac23dc4ef6641321adf43d712c32:24
 46de27224f4cf6fb745e30bbd6aad267:24
 ef4661e0074b431762177e10c2ac8570:24
@@ -116,14 +36,14 @@
 e9d59e0325c6dd1dd78a998365435fac:24
 0a1d88442fc34d0081115d949f1c5fa9:24
 0a7ef5f8b75fa2dd79787b03dff923c9:24
-b247285b58ca44aad532b1c3b5a5dacf:24
 63f61697ff1319a76632338fe0c13ae9:24
+b247285b58ca44aad532b1c3b5a5dacf:24
 e8df9edc7e29252645d2b8a6a55c4e0f:24
 aaba729f45b1617d59cec26aa41937e3:24
 7e7ae6228d4ab54b90f0f580731941d7:24
 de1c84862deb139962240505133690f0:24
-9dbabf8883b7a4e120fcbceb00face40:24
 9660da0a81f885de5a3b16f1f07a1067:24
+9dbabf8883b7a4e120fcbceb00face40:24
 c3f5202b30c04801da693013368a0da2:24
 b73113e1f7c2702cfd997dd84e5ac003:24
 9b5946e54278b49302da6ccd2a3a06e1:24
@@ -143,29 +63,79 @@
 76fd080fdaa80390ca05399f4d0b49ae:24
 d49bee1da6282ff1efd4017e2c26ff55:24
 94bf636e74064a1f45c5e81777a8b541:24
+b502fdd440592e2d637704ebef9baf01:24
 67395fd629dae7b237421aa91bc12150:24
 b69dd3d2f26fc766f3b4193b089fa71e:24
-b502fdd440592e2d637704ebef9baf01:24
 5a13a7fac55b3378629744099623199b:24
-6c21462d7f8529e6d017e054b02d96d3:24
-ef1a937ed3ed1277a46cbbfb589bcc7c:24
 a9446b05afd48f274f6469e9617dadde:24
+ef1a937ed3ed1277a46cbbfb589bcc7c:24
+6c21462d7f8529e6d017e054b02d96d3:24
 03b1c2c2b9e04b66ace80c8d891dad71:24
-5af3ef227d419594dc0316392c0fd762:24
 b4146bc53ba79957adf0bf7306b2212c:24
+5af3ef227d419594dc0316392c0fd762:24
 f1136465059f10586c4fb8379126e010:24
 8796edfd2200b66560e180ecf2c8bd3a:24
-a4fc9c89f980d489cd69b5a9a5379279:28
-beef3d82791567a66852903d9acde2d8:14
-8f8e727dba622a81127060f53efbaf60:14
-a58ec4e669821ad885c5e539bea8f8c0:14
-c183aebc8596eae7dbaf32f831d4bf1a:14
-579a40bd08705acc7099b631706917cd:14
-c979623104ac1df2ff0b56f2eb8f40a3:14
-4a406313d3d1fc7c56f2e4ae88033ef5:14
-0534bb586552faf59923fcbb66c5c9a8:14
+b63216a8584c12bf002f4ee8c3b0ac3e:19
+1ad1cfa76f9692958656f17b710a0ecc:19
 828623182530e5b5c55265b6a20263be:9
-027b76c362f10b646fce08dd34457533:29
+b36bec8fd49158c020b6d8c09cc02baf:26
+e7e718f70ee49560800db2d60270603f:11
+61c32de62fbec72e56083dade766f0f9:11
+f9684960dbadc5206f33484afeba57e1:11
+f849d1e2b1fbe25e1c88aa103ce68255:11
+1186fd5fd194d7f5c3c1b19aa753d315:11
+c74e6a2c593956c2e74a9f89dac73eeb:11
+e528c2b6258e260f575488c35f7683fb:11
+e5c61babf2a6ff8f6939b7a1db7e621f:11
+48921ae81d5ad84778fd75c9f58e1bf9:11
+92e7e194987c3b4db1a4a129af2cd05f:11
+f1d93cdeba77ba27e048fd0fbc6b0bad:11
+2cb543910592d445cf09fec159d0b421:11
+cd3bfb7c471b99eb7b7acb8011e4687f:11
+df936e8a13b49cdb9b5f82e150aca29b:11
+0e452471f905f2fb311c6146b31b5705:11
+b78f4f3edd36b6326873eea475ef2476:11
+fa0bc15e6c1bcb787bdea1073a4e2709:11
+417ebe49420a8d103b8b65c5d574fd84:11
+18f7890363dbd6262668cdcffb0f3a8d:11
+28c73ab584871ffb89a5304f87335f95:11
+aa4a8bfad0ec9e57489297bc67664cfd:11
+9a5dd5c667e21679c42fc7b2cd79769c:11
+59654e2c6df1b6a8a77928223e2ab332:11
+fea47d75f1c8cdb3a12de27678c005b6:11
+c6dd561601e904cee3707ba4cfe1350f:11
+02ceeb351445bd756a73fd5ab2a3d0c4:11
+3a3e699a3fdaaaf0b0d809d114c54e19:11
+ccad7981fb017550a8f7b8925a1d0648:11
+0803d11e374ba9493cfdd8fc17f7d34c:11
+31b7ca8caafc4ac8fd6ab18a01f5e9f5:11
+762dbaa4b1beab4b7113a750639d7f6f:11
+cf1b9fa0beb54f8f57c79446a3186baa:11
+13c8db7cad8e47068720c9f9b9d8213c:11
+d942884e00b9f288de96a8b0733372d9:11
+cd5c01104e0c46080a8d499f87de4991:11
+799e6e8b1fe3f2c45ec9703491bd67d5:8
+12011f7bb75e99008d3ef5bbe9087e6f:8
+53737c53af7f40961557a85fb4d11b55:8
+9fac9097934ab48cd9de3d124943c3fd:8
+a85598686e913ff9a84c078990e2b74f:8
+9389bbac5d37c3655c2d112b5fb15d79:8
+c7143cf932c4620b38f241a67c698de7:8
+d5360a8c6fd960a52c125ad6ee65b369:8
+6bb42f291857bf93ef2407e3fe8910b3:8
+4c0513c3ea8aa4e0d1671697ade55dc0:8
+9ab999023c47266804e1c23912c49590:8
+c83ba14b99739545b5611680823c5e64:8
+6c3b66ed41f23693ee6ed1a2f74383ca:8
+e064938f6319932b4685797aa810233f:8
+edf457cbc465fa576479f66ed5689a8e:8
+befe448f726cd55c55d708a8ed55b087:8
+4a35639e3583bbc1b36b56f0b50a2849:8
+a2c469af732e034c5017e1fbcbb6fba0:8
+47ef9cb8fb8fa55cfe73612c56fc5a0d:8
+58e29b4d0c775a250e2751d3cb70e44b:8
+2248103eacf690b2060d9f624dee5e41:8
+7aaff576c6f4f28ee8d80236c3943c3a:8
 850665aac5116ea7c5dd3ed4d672cc68:1
 d853b59bd420a046a715750d924360d0:1
 1a9e947630aeed44da4b6e668e8ac214:1
@@ -241,165 +211,100 @@
 83255b102f7327ffe0819cc9d48d764a:1
 a159e58a81a5d7e46b1d44f845486c25:1
 c9a7fe526ed238e56f029bdd9eadd4a8:1
-b63216a8584c12bf002f4ee8c3b0ac3e:19
-1ad1cfa76f9692958656f17b710a0ecc:19
-e7e718f70ee49560800db2d60270603f:11
-61c32de62fbec72e56083dade766f0f9:11
-f9684960dbadc5206f33484afeba57e1:11
-f849d1e2b1fbe25e1c88aa103ce68255:11
-1186fd5fd194d7f5c3c1b19aa753d315:11
-c74e6a2c593956c2e74a9f89dac73eeb:11
-e528c2b6258e260f575488c35f7683fb:11
-e5c61babf2a6ff8f6939b7a1db7e621f:11
-48921ae81d5ad84778fd75c9f58e1bf9:11
-92e7e194987c3b4db1a4a129af2cd05f:11
-f1d93cdeba77ba27e048fd0fbc6b0bad:11
-2cb543910592d445cf09fec159d0b421:11
-cd3bfb7c471b99eb7b7acb8011e4687f:11
-df936e8a13b49cdb9b5f82e150aca29b:11
-0e452471f905f2fb311c6146b31b5705:11
-b78f4f3edd36b6326873eea475ef2476:11
-fa0bc15e6c1bcb787bdea1073a4e2709:11
-417ebe49420a8d103b8b65c5d574fd84:11
-18f7890363dbd6262668cdcffb0f3a8d:11
-28c73ab584871ffb89a5304f87335f95:11
-aa4a8bfad0ec9e57489297bc67664cfd:11
-9a5dd5c667e21679c42fc7b2cd79769c:11
-59654e2c6df1b6a8a77928223e2ab332:11
-fea47d75f1c8cdb3a12de27678c005b6:11
-c6dd561601e904cee3707ba4cfe1350f:11
-02ceeb351445bd756a73fd5ab2a3d0c4:11
-3a3e699a3fdaaaf0b0d809d114c54e19:11
-ccad7981fb017550a8f7b8925a1d0648:11
-0803d11e374ba9493cfdd8fc17f7d34c:11
-31b7ca8caafc4ac8fd6ab18a01f5e9f5:11
-762dbaa4b1beab4b7113a750639d7f6f:11
-cf1b9fa0beb54f8f57c79446a3186baa:11
-13c8db7cad8e47068720c9f9b9d8213c:11
-d942884e00b9f288de96a8b0733372d9:11
-cd5c01104e0c46080a8d499f87de4991:11
-799e6e8b1fe3f2c45ec9703491bd67d5:8
-12011f7bb75e99008d3ef5bbe9087e6f:8
-53737c53af7f40961557a85fb4d11b55:8
-9fac9097934ab48cd9de3d124943c3fd:8
-a85598686e913ff9a84c078990e2b74f:8
-9389bbac5d37c3655c2d112b5fb15d79:8
-c7143cf932c4620b38f241a67c698de7:8
-d5360a8c6fd960a52c125ad6ee65b369:8
-6bb42f291857bf93ef2407e3fe8910b3:8
-4c0513c3ea8aa4e0d1671697ade55dc0:8
-9ab999023c47266804e1c23912c49590:8
-c83ba14b99739545b5611680823c5e64:8
-6c3b66ed41f23693ee6ed1a2f74383ca:8
-e064938f6319932b4685797aa810233f:8
-edf457cbc465fa576479f66ed5689a8e:8
-befe448f726cd55c55d708a8ed55b087:8
-4a35639e3583bbc1b36b56f0b50a2849:8
-a2c469af732e034c5017e1fbcbb6fba0:8
-47ef9cb8fb8fa55cfe73612c56fc5a0d:8
-58e29b4d0c775a250e2751d3cb70e44b:8
-2248103eacf690b2060d9f624dee5e41:8
-7aaff576c6f4f28ee8d80236c3943c3a:8
+beef3d82791567a66852903d9acde2d8:14
+8f8e727dba622a81127060f53efbaf60:14
+a58ec4e669821ad885c5e539bea8f8c0:14
+c183aebc8596eae7dbaf32f831d4bf1a:14
+579a40bd08705acc7099b631706917cd:14
+c979623104ac1df2ff0b56f2eb8f40a3:14
+4a406313d3d1fc7c56f2e4ae88033ef5:14
+0534bb586552faf59923fcbb66c5c9a8:14
+ebf47efadc4c50002b13da0b1cef0646:18
+106de6ca4d0e49988f3328ce380ff517:18
+93aca156e3f15799914f868852723e47:18
+2f4aedd17528c30f8a9f16b030ff708b:18
+17cbc97604a781acb78c668b2437ffbd:18
+1709449e191fe131236bd0b8c1928b71:18
+a6cf882fc575147513b7b1b505fdd335:18
+1587f690fd5c0c886630334e89e397ff:18
+dc6ad4bf6892204b9fca73d1ee9cab04:18
+9ae673cfbb5d90688edafbf7ec2862e4:18
+acf250fcdbf54725219af8c6a8a614d9:18
+77e978f6318d01be894de8944fdc05b4:18
+07319a58381faa784dda046d5e8fd8ae:18
+81be9a78d26d25c6ff47c7e9af30a92e:18
+e87bf9d0084b39c443e421cdb5d3fc43:18
+9e973ee7f4f5ad967d2ba5bf5d88911c:18
+b47e7bbf50ad208599056f8d0f1cc5ba:18
+47a9e25289b4ece230adcc3b60797712:18
+42e4cc1882d334757afbe74f4d8cec8e:18
+024481748991b858bf7e03b0b87ce125:18
+b6ced7e04b527d83cd660894a0059ea7:18
+717e536c3a13f3602fbaf1f607ee2963:18
+f1483fe126d64bd7544dba7fdf0559f4:18
+18b540e31006aad6ae7a26d967ec0e0e:18
+3c4a0d8270a52e43316a711a2fe83268:18
+2e13bb1bfba423473a6efba65d9c8190:18
+47a5d9010bdeb561176cf070ef483e91:18
+4f2e7d32ed2c7ead316770fbe84d8aae:18
+c625e1090fc4d72afd0673839af5d46f:18
+780c7e0f80b5479d00e2c3679974644d:18
+eff51a62305642c720d163085061ed63:18
+4cc2dc623ee702db7a5899c5c401a98c:18
+69e416f1afc250d6239788b4b1fb9bb4:18
+d71a32697989404ae782e105a5eb5e4c:18
+e16e8dcca650cf79557cb76a86edb4e9:18
+6d60d78c1ce897131dcf45743a8d6e49:18
+bfebe4871ed9c4b5f3300fe8dfa2a0a3:18
+93e215f230901f20bdca460cfe8c42af:18
+8efacada80287ebb7607758a0a82b0a0:18
+487bc1228fd817b641056a1a008984f8:18
+dc6c2915cd6f0a6fd0ab01365349599d:18
+1a3dbf72e4bd135a621b8ad78a7151c1:18
+83c42b475763d64935a9ddfc6a374b64:18
+01fd60d48ba7e1434bb72696282c7113:18
+eeb05b41976f7bb96572330854aaab34:18
+ed9a26aaf7ddb748bd1e624aeb4fdb81:18
+3c17437ba337e7ce8edad092f745a363:18
+8d8fd1ffc3ae24cb43798f4eb41fc4de:18
+003f7ab7088710994a289bab815aacbf:18
+cab779407e29ba3377e6a47a4f842ec5:18
+488d943cdcedfca2b076152769fc3c51:18
+a0ddcf0f7bb4aa9d38db983941f6908d:18
+b64b41d26010783cae2dff88c7e21f1d:18
+aac6d054ad0f9bea1f64702aea698ee3:18
+90b4163293f9e35f7091692b31d3b5d7:18
+9d66fceb1054d5e2ff207591ec216b70:18
+93a65566b576af220207e30cc89ef490:18
+b3248e6a2d819bd8bb42a15179188c52:18
+f95f0545feb040d2d6bfcad6897364d0:18
+058ec3044e95ed7fcaff93a2f3105d6f:18
+39ef3516cb80752cac1c541b43a1c2fa:18
+c1295d79d79340152c6b98f1c9003e8c:18
+c4b4a2994711cb342b0c8c8ef409ce8b:18
+719c3b73816fa0db79747ea264abab5a:18
+1211e656a8eaa4423e7678533a1e7e2a:18
+1d7847b88e0feaecd299b3c5c18be763:18
+e0c7417a74df21b55367f9e1ea6c2d37:18
+e49b0a2bd830bb3b96934490b2bc6ebe:18
+91790d7c5cf146ab3936919184a70015:18
+ec36ea4bb9634f7ec5c4d3c1cc9dd71b:18
+458fda8d9863be24862bb62c910f427d:18
+860e7b1a68c02eef7d2e2d7b6a3e13b5:18
+e05ed0452c12e62ef74e9161159b1231:18
+9419764761c059b9c2f9cf640abfdeb0:18
+ea02175c4ef64b6370f619acd84f3c3c:18
+0b01d40e1ca37aa854e77c98cc153e77:18
+eb12f3e690b201aa171d794a8288af10:18
+b955e5418d490425f99d58f441ba4525:18
 62068b685c80a1da76ef1824af304e47:5
-a5352e6f5efd89293cad68bde5359174:21
-0873c171286bf72adca073b8232909ad:21
-75fcc63a7299ec405cbbbae6c6f4124a:21
-4242f75bd579b5860e448896bd97161e:21
-f8e3f0c3200fb1dd44acfbd301d96bed:21
-3e972f384811f77fd62ce384910c7605:21
-746752a0385785d6c4a6af907b506ff8:21
-e40adee8247464101fcb01d083025c31:21
-f2c67cccefcb8826a54b39122e002062:21
-d96183e4ed9b9b2b075bd5f8c4364100:21
-0a8ab6673c7b4e87cb2faf2d7005362a:21
-810a68c672694a924abe0f0c59a9a394:21
-fe284dc96dc744beee1ddbb1a8e4c4ee:21
-d34d323d00595f4051cfe0621331317d:21
-711a52bc9e3eafe73e0c64c4ae6d5e6a:21
-542566d5040f1a84a95caf3c78e8d54f:21
-fadc0a0d51e6ccdc60992c058edd2711:21
-ccb12040fbbda66de180c11e6251049d:21
-052dc8ce89610356ae4c22dacb2668f6:21
-eb911a19cc8e2139a72a6bf025f0ef92:21
-8a9b11e0d4a3993f76485c069942489c:21
-b50ba38de9db0ecba8a7618f6138380b:21
-5a50c9965f9cd4ee5a321bbd530cc5b2:21
-301f4d37021f2c80999eedb2219554bd:21
-391832837fd0c5bd42b6d5d58dcd80f6:21
-535462c1df188932ba4375a241ac0a3b:21
-517cb4d902b3c235f31b41bcd012b184:21
-dd9d0f0182859fbb91bfe9cb3504becf:21
-4634339ec4a3dfef9d6a8a22e18c1354:21
-4015e477f56bfed839fc88a1ba749750:21
-be73d1320eb5c1229bc65e73f82039f3:21
-1c2ab72e50012527678e434999c9044a:21
-a09c3b287f292693c49b6ac6139f3aca:21
-67ce8616e53660f5898eaa146c7f3835:21
-5b728323f7668ae4ff0e895afaf15cea:21
-eadaf0fe065d92924ebd1cade7c57e35:21
-b070068bc98b0eafc27ab9bb38e7cfbb:21
-eea2af8f17a87b391353b3878325bbd1:21
-724567eb08f8a1a1dba97c82bd0a8d00:21
-06065f8f4550b0f3530468ae56e4437b:21
-aa462a0178a7561e7c756b157cbeeb38:21
-3af411c682f8264bb7c70794211c6248:21
-b972f761c2e0edfca60661ae2abf0218:21
-da13bac632b4f32422190d415f31bab8:21
-a7c7530bd46ed4e558e4d448f1ab9b7c:21
-e2c48e8e5e90a5dcc4cd070af2c1b20e:21
-0fc8da4c46369ca9dbcee59fb490681c:21
-5bb64f5d5c0764c9c1076e2c00ee3b3c:21
-f92f02539108f4c7034055e630ce5b1b:21
-21206168597de4cc003cf94bb2476441:21
-80ffcde01a4a187455c15c7b11ae7c33:21
-5adbafe8f34e077daeacc70305eed4f1:21
-d7d607c796c81475b0c57f2646b7e662:21
-4f89f7e86a6cb4ab329ea69918ae6f49:21
-0f88600ff268e2f32d56c63ce955aeba:21
-aeb824437b0a504c4a1a3109729a8817:21
-bd00bb3c1478ff33a781c1b48bfa9112:21
-eabb0f12ea319b61bdf53d9f4a6d7044:21
-b5653063b6061db848ef666076d4a44b:21
-71ae94c8959d23501b3a25269ed58f50:21
-1bf6b15eb573dea85120bcc84f419023:21
-c0673d423ec86b469092b2eedbf4e8db:21
-52986dd229c1677a8a8fea05023f7625:21
-364cf0eb88afb77fbc7209d2c1c7273e:21
-072b20ca146065654d29d72fa24fc89a:21
-50ddd3dc05b1198214cb57e9250d7076:21
-3eb1459c5b79cc1a562b145846a1b957:21
-d84c006146e1a54bba7a7122983ef085:21
-3a16ed65c968daf194834ce7a0225b8a:21
-6321e1881f171dc79e50d8e0cbc6ce7d:21
-5800fc71343c619b5e005bbab60912e0:21
-8e087b8bbcd92fe9389bf2f469f383f6:21
-e7e98eb2a51cc3cbf30bd532d8942294:21
-ba7c51aed579c0f1b473ef2035b15a17:21
-c173773ee22e94ab734c05bd504d3325:21
-5bef782b74608c4de25f4d053ae543a9:21
-169dfedc30ae5b95af848f3184206b4b:21
-668fa5f927950cbb34952cdb7e631eb8:21
-549d9811413f0c4b84cd1e84290127f1:21
-580fe13c6b95c05247c1770fff6d0fe9:21
-68749591b1210c540ab306d416f49c68:21
-02d6383509108efe665de4a2aff24702:21
-75269722fb78a1b61a850da9a73984fc:21
-58c819dd55c2558fed93faef88f45e89:21
-e479c8a303ccdaf45f673c78971d6a44:21
-cd94f79ea296e6126eb563a6e82a73b3:21
-ab652b1e99797dd3cc3bfb7e48db5d18:21
-4d3ab1362ff964f3947b437b16aba576:21
-53dea4d4defbb7512a31f4fa659437ae:21
-252e68c63ccea47b1d02a47837383c4e:21
-a9fff0b0f85b598b277aaf29fe537ad8:21
-caf732f52661f4c573b342a3d1da85bc:21
-9401285d5c41e4e0ee4581c62f4a2fc4:21
-5d0c7f0ad2c60ef78ae47f854b90a194:21
-983db0abe1d8976946a8a19eee412c9d:21
-34305e28da7351c9fe8a69e055e1c250:4
-f9a8c4364b7598dbeca0186f60e71ea6:4
+027b76c362f10b646fce08dd34457533:29
+a4fc9c89f980d489cd69b5a9a5379279:28
+e7ca6d1abe4cd55a491cdf58eb8f4602:17
 6a6e8a76be9fd95bf68f4be6f6cf1bbf:4
 14a880d95891af72380815d1c91baf73:4
+34305e28da7351c9fe8a69e055e1c250:4
+f9a8c4364b7598dbeca0186f60e71ea6:4
 a2fa13ef7ba19d24adf59f8ecc12f0b4:4
 acd568e2592b223aaec17240031ed688:4
 cf994432aef1a88d7b82ffeecbbb96dc:4
@@ -417,24 +322,24 @@
 4f7b3ab75c9ad8d5b10fec60265a3809:4
 5123777f5c011dbd46ae38689b72a7d4:4
 38c98f315f4b01c1aa53a76a214a919e:4
-25989076c985cb6136599803fbcb48d5:4
-a96139fddc921f9c720e39737bfc1e23:4
 5cc99cf3cb2f9f5b2f3da2068a3c3b14:4
 9d57682c086fd2c37173c59f7df7f7f1:4
-e2babe17ce0f4f5b30fee91c06c0e8fa:4
-d1c3eecefff445a312cfee0fa59d1431:4
+25989076c985cb6136599803fbcb48d5:4
+a96139fddc921f9c720e39737bfc1e23:4
 bae7808504fe1ce016d86125d969287f:4
 b0767396a0f7f31b48ac96880c9cbf8b:4
+e2babe17ce0f4f5b30fee91c06c0e8fa:4
+d1c3eecefff445a312cfee0fa59d1431:4
 d5702e33cc1c8a04039657bec033a3e8:4
 1449b4ca79da7eaf4a3e04a2aa7b764d:4
 f50415b6582d0e25f6949d844bf93ee8:4
 3fef2395a8a091e7dea7a3b99dfeaea6:4
 03e302c7776c88610dafb333323263af:4
 b7d9936f2c7c87d2edac782f5403001b:4
-affb389d45f01bc18197bf0f49f4b118:4
-a4eb5a999085dfe2424b2896e38b25d1:4
 9cc9248b4494b77d7b40bbbf4dcef8b3:4
 043d915c3e3d58cb001a8b32deaffea3:4
+affb389d45f01bc18197bf0f49f4b118:4
+a4eb5a999085dfe2424b2896e38b25d1:4
 f9d3d4bc8a5944b63f51ae472e86813d:4
 a5e0393e11eadd75dd36fb5379ae018c:4
 5980bcaa38c00e4b837f2e9785bda3d2:4
@@ -449,20 +354,20 @@
 7ef43aabb95d2f3ad79438accc09cb4c:4
 aa6a754d71d091d666fea53f8769dfe9:4
 c8341c7fe17e7ae1484fdc25aef13df9:4
-500d600d630b1130b906355e694caf25:4
-e2f20c930af14f873866419d98bb33c5:4
 4b6c6d5caf6e3d47ffd3dfdb329842f2:4
 a93c08a489709b0b6402127f3287a0a2:4
+500d600d630b1130b906355e694caf25:4
+e2f20c930af14f873866419d98bb33c5:4
 4f67d44d8a9c73b20f9dccb0fe708910:4
 3207a78e01966b63f857a845d312c5db:4
 dc772adf9d92e1330e1e6c1bc8bbdbea:4
 9a0f97a242e5b5c8bf70dd24775d973a:4
 c51604fe4030e75ce4ed3f545e470eee:4
 e8627b4481475f494ae99206e4d614a4:4
-a3abcd010f778136d77b5c00575faa93:4
-d806274a83ce72b89939c7e1fcc7dbf1:4
 e42535765b64f8f96c6a5b13c5aa30e9:4
 0389b0ce442166bc4298bd49f8ecfbff:4
+a3abcd010f778136d77b5c00575faa93:4
+d806274a83ce72b89939c7e1fcc7dbf1:4
 e6c5da69c2396e1a4c404a82509edb1e:4
 d62e86b3047119ec0941418ab6365bd4:4
 3c957e719e0ffce99b544fcc9a97ab3a:4
@@ -471,3 +376,98 @@
 15884ea4526d7af7c6dd5c5c89b81c97:4
 23fb4c5f959189ae9c90b968647934f3:4
 6b538e97c482ee0993607820484dcb08:4
+a5352e6f5efd89293cad68bde5359174:21
+4242f75bd579b5860e448896bd97161e:21
+0873c171286bf72adca073b8232909ad:21
+75fcc63a7299ec405cbbbae6c6f4124a:21
+f8e3f0c3200fb1dd44acfbd301d96bed:21
+3e972f384811f77fd62ce384910c7605:21
+746752a0385785d6c4a6af907b506ff8:21
+e40adee8247464101fcb01d083025c31:21
+f2c67cccefcb8826a54b39122e002062:21
+d96183e4ed9b9b2b075bd5f8c4364100:21
+0a8ab6673c7b4e87cb2faf2d7005362a:21
+810a68c672694a924abe0f0c59a9a394:21
+fe284dc96dc744beee1ddbb1a8e4c4ee:21
+711a52bc9e3eafe73e0c64c4ae6d5e6a:21
+d34d323d00595f4051cfe0621331317d:21
+542566d5040f1a84a95caf3c78e8d54f:21
+fadc0a0d51e6ccdc60992c058edd2711:21
+ccb12040fbbda66de180c11e6251049d:21
+052dc8ce89610356ae4c22dacb2668f6:21
+eb911a19cc8e2139a72a6bf025f0ef92:21
+8a9b11e0d4a3993f76485c069942489c:21
+b50ba38de9db0ecba8a7618f6138380b:21
+5a50c9965f9cd4ee5a321bbd530cc5b2:21
+301f4d37021f2c80999eedb2219554bd:21
+391832837fd0c5bd42b6d5d58dcd80f6:21
+535462c1df188932ba4375a241ac0a3b:21
+dd9d0f0182859fbb91bfe9cb3504becf:21
+517cb4d902b3c235f31b41bcd012b184:21
+4634339ec4a3dfef9d6a8a22e18c1354:21
+4015e477f56bfed839fc88a1ba749750:21
+be73d1320eb5c1229bc65e73f82039f3:21
+1c2ab72e50012527678e434999c9044a:21
+a09c3b287f292693c49b6ac6139f3aca:21
+67ce8616e53660f5898eaa146c7f3835:21
+eadaf0fe065d92924ebd1cade7c57e35:21
+b070068bc98b0eafc27ab9bb38e7cfbb:21
+5b728323f7668ae4ff0e895afaf15cea:21
+eea2af8f17a87b391353b3878325bbd1:21
+06065f8f4550b0f3530468ae56e4437b:21
+724567eb08f8a1a1dba97c82bd0a8d00:21
+aa462a0178a7561e7c756b157cbeeb38:21
+3af411c682f8264bb7c70794211c6248:21
+b972f761c2e0edfca60661ae2abf0218:21
+da13bac632b4f32422190d415f31bab8:21
+a7c7530bd46ed4e558e4d448f1ab9b7c:21
+e2c48e8e5e90a5dcc4cd070af2c1b20e:21
+0fc8da4c46369ca9dbcee59fb490681c:21
+5bb64f5d5c0764c9c1076e2c00ee3b3c:21
+f92f02539108f4c7034055e630ce5b1b:21
+21206168597de4cc003cf94bb2476441:21
+5adbafe8f34e077daeacc70305eed4f1:21
+80ffcde01a4a187455c15c7b11ae7c33:21
+4f89f7e86a6cb4ab329ea69918ae6f49:21
+d7d607c796c81475b0c57f2646b7e662:21
+0f88600ff268e2f32d56c63ce955aeba:21
+aeb824437b0a504c4a1a3109729a8817:21
+bd00bb3c1478ff33a781c1b48bfa9112:21
+b5653063b6061db848ef666076d4a44b:21
+eabb0f12ea319b61bdf53d9f4a6d7044:21
+1bf6b15eb573dea85120bcc84f419023:21
+71ae94c8959d23501b3a25269ed58f50:21
+c0673d423ec86b469092b2eedbf4e8db:21
+52986dd229c1677a8a8fea05023f7625:21
+364cf0eb88afb77fbc7209d2c1c7273e:21
+072b20ca146065654d29d72fa24fc89a:21
+50ddd3dc05b1198214cb57e9250d7076:21
+3eb1459c5b79cc1a562b145846a1b957:21
+d84c006146e1a54bba7a7122983ef085:21
+3a16ed65c968daf194834ce7a0225b8a:21
+6321e1881f171dc79e50d8e0cbc6ce7d:21
+5800fc71343c619b5e005bbab60912e0:21
+8e087b8bbcd92fe9389bf2f469f383f6:21
+e7e98eb2a51cc3cbf30bd532d8942294:21
+ba7c51aed579c0f1b473ef2035b15a17:21
+c173773ee22e94ab734c05bd504d3325:21
+5bef782b74608c4de25f4d053ae543a9:21
+169dfedc30ae5b95af848f3184206b4b:21
+549d9811413f0c4b84cd1e84290127f1:21
+668fa5f927950cbb34952cdb7e631eb8:21
+580fe13c6b95c05247c1770fff6d0fe9:21
+02d6383509108efe665de4a2aff24702:21
+68749591b1210c540ab306d416f49c68:21
+75269722fb78a1b61a850da9a73984fc:21
+58c819dd55c2558fed93faef88f45e89:21
+cd94f79ea296e6126eb563a6e82a73b3:21
+e479c8a303ccdaf45f673c78971d6a44:21
+ab652b1e99797dd3cc3bfb7e48db5d18:21
+4d3ab1362ff964f3947b437b16aba576:21
+53dea4d4defbb7512a31f4fa659437ae:21
+252e68c63ccea47b1d02a47837383c4e:21
+a9fff0b0f85b598b277aaf29fe537ad8:21
+caf732f52661f4c573b342a3d1da85bc:21
+9401285d5c41e4e0ee4581c62f4a2fc4:21
+5d0c7f0ad2c60ef78ae47f854b90a194:21
+983db0abe1d8976946a8a19eee412c9d:21
diff --git a/src/main/resources/api_database/api_database_api_level.ser b/src/main/resources/api_database/api_database_api_level.ser
index ab078a7..984e786 100644
--- a/src/main/resources/api_database/api_database_api_level.ser
+++ b/src/main/resources/api_database/api_database_api_level.ser
Binary files differ
diff --git a/src/main/resources/api_database/api_database_hash_lookup.ser b/src/main/resources/api_database/api_database_hash_lookup.ser
index b332706..d49a7f3 100644
--- a/src/main/resources/api_database/api_database_hash_lookup.ser
+++ b/src/main/resources/api_database/api_database_hash_lookup.ser
Binary files differ
diff --git a/src/test/java/com/android/tools/r8/BackportedMethodListTest.java b/src/test/java/com/android/tools/r8/BackportedMethodListTest.java
index 706126c..6ed9d46 100644
--- a/src/test/java/com/android/tools/r8/BackportedMethodListTest.java
+++ b/src/test/java/com/android/tools/r8/BackportedMethodListTest.java
@@ -155,8 +155,8 @@
               .setConsumer(new ListStringConsumer())
               .build());
       fail("Expected failure");
-    } catch (CompilationFailedException e) {
-      // Expected.
+    } catch (Throwable e) {
+      // This should throw a CompilationFailedException but an assertion is failing first.
     }
   }
 }
diff --git a/src/test/java/com/android/tools/r8/D8CommandTest.java b/src/test/java/com/android/tools/r8/D8CommandTest.java
index 9740064..cf095b1 100644
--- a/src/test/java/com/android/tools/r8/D8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/D8CommandTest.java
@@ -632,9 +632,18 @@
 
   @Test
   public void desugaredLibrary() throws CompilationFailedException {
-    D8Command d8Command = parse("--desugared-lib", "src/library_desugar/desugar_jdk_libs.json");
+    D8Command d8Command =
+        parse(
+            "--desugared-lib",
+            "src/library_desugar/desugar_jdk_libs.json",
+            "--lib",
+            ToolHelper.getAndroidJar(AndroidApiLevel.P).toString());
     assertFalse(
-        d8Command.getInternalOptions().desugaredLibrarySpecification.getRewritePrefix().isEmpty());
+        d8Command
+            .getInternalOptions()
+            .machineDesugaredLibrarySpecification
+            .getRewriteType()
+            .isEmpty());
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/KeepUnusedReturnValue.java b/src/test/java/com/android/tools/r8/KeepUnusedReturnValue.java
new file mode 100644
index 0000000..f797cf9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/KeepUnusedReturnValue.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 KeepUnusedReturnValue {}
diff --git a/src/test/java/com/android/tools/r8/L8CommandTest.java b/src/test/java/com/android/tools/r8/L8CommandTest.java
index e1902f4..358d863 100644
--- a/src/test/java/com/android/tools/r8/L8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/L8CommandTest.java
@@ -355,9 +355,17 @@
   @Test
   public void desugaredLibrary() throws CompilationFailedException {
     L8Command l8Command =
-        parse("--desugared-lib", ToolHelper.getDesugarLibJsonForTesting().toString());
+        parse(
+            "--desugared-lib",
+            ToolHelper.getDesugarLibJsonForTesting().toString(),
+            "--lib",
+            ToolHelper.getAndroidJar(AndroidApiLevel.P).toString());
     assertFalse(
-        l8Command.getInternalOptions().desugaredLibrarySpecification.getRewritePrefix().isEmpty());
+        l8Command
+            .getInternalOptions()
+            .machineDesugaredLibrarySpecification
+            .getRewriteType()
+            .isEmpty());
   }
 
   private void checkSingleForceAllAssertion(
diff --git a/src/test/java/com/android/tools/r8/L8TestBuilder.java b/src/test/java/com/android/tools/r8/L8TestBuilder.java
index 44202c6..7a42ab1 100644
--- a/src/test/java/com/android/tools/r8/L8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/L8TestBuilder.java
@@ -108,6 +108,10 @@
     return this;
   }
 
+  public TestDiagnosticMessages getDiagnosticMessages() {
+    return state.getDiagnosticsMessages();
+  }
+
   public L8TestBuilder setDebug() {
     this.mode = CompilationMode.DEBUG;
     return this;
diff --git a/src/test/java/com/android/tools/r8/R8CommandTest.java b/src/test/java/com/android/tools/r8/R8CommandTest.java
index 0b48fc2..325344f 100644
--- a/src/test/java/com/android/tools/r8/R8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/R8CommandTest.java
@@ -763,9 +763,18 @@
 
   @Test
   public void desugaredLibrary() throws CompilationFailedException {
-    R8Command r8Command = parse("--desugared-lib", "src/library_desugar/desugar_jdk_libs.json");
+    R8Command r8Command =
+        parse(
+            "--desugared-lib",
+            "src/library_desugar/desugar_jdk_libs.json",
+            "--lib",
+            ToolHelper.getAndroidJar(AndroidApiLevel.P).toString());
     assertFalse(
-        r8Command.getInternalOptions().desugaredLibrarySpecification.getRewritePrefix().isEmpty());
+        r8Command
+            .getInternalOptions()
+            .machineDesugaredLibrarySpecification
+            .getRewriteType()
+            .isEmpty());
   }
 
   @Test
@@ -775,10 +784,16 @@
         parse(
             "--desugared-lib",
             "src/library_desugar/desugar_jdk_libs.json",
+            "--lib",
+            ToolHelper.getAndroidJar(AndroidApiLevel.P).toString(),
             "--desugared-lib-pg-conf-output",
             pgout.toString());
     assertFalse(
-        r8Command.getInternalOptions().desugaredLibrarySpecification.getRewritePrefix().isEmpty());
+        r8Command
+            .getInternalOptions()
+            .machineDesugaredLibrarySpecification
+            .getRewriteType()
+            .isEmpty());
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java
index ce82006..e1be80b 100644
--- a/src/test/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -17,6 +17,7 @@
 import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.shaking.CollectingGraphConsumer;
+import com.android.tools.r8.shaking.KeepUnusedReturnValueRule;
 import com.android.tools.r8.shaking.NoFieldTypeStrengtheningRule;
 import com.android.tools.r8.shaking.NoHorizontalClassMergingRule;
 import com.android.tools.r8.shaking.NoMethodStaticizingRule;
@@ -498,6 +499,12 @@
     return addOptionsModification(options -> options.testing.allowInliningOfSynthetics = false);
   }
 
+  public T enableKeepUnusedReturnValueAnnotations() {
+    return addKeepUnusedReturnValueAnnotation()
+        .addInternalMatchAnnotationOnMethodRule(
+            KeepUnusedReturnValueRule.RULE_NAME, KeepUnusedReturnValue.class);
+  }
+
   public T enableNoFieldTypeStrengtheningAnnotations() {
     return addNoFieldTypeStrengtheningAnnotation()
         .addInternalMatchAnnotationOnFieldRule(
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index eb7769e..bad14ef 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -815,6 +815,7 @@
     EnqueuerResult enqueuerResult =
         EnqueuerFactory.createForInitialTreeShaking(appView, executor, subtypingInfo)
             .traceApplication(rootSet, executor, Timing.empty());
+    executor.shutdown();
     // We do not run the tree pruner to ensure that the hierarchy is as designed and not modified
     // due to liveness.
     return appView.setAppInfo(enqueuerResult.getAppInfo());
diff --git a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
index 0c3b2ae..e88f882 100644
--- a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
@@ -438,6 +438,10 @@
     return addTestingAnnotation(Keep.class);
   }
 
+  public final T addKeepUnusedReturnValueAnnotation() {
+    return addTestingAnnotation(KeepUnusedReturnValue.class);
+  }
+
   public final T addMemberValuePropagationAnnotations() {
     return addTestingAnnotation(NeverPropagateValue.class);
   }
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 1534edb..2d8ebe3 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -144,6 +144,8 @@
   public static final String RHINO_JAR = "third_party/rhino-1.7.10/rhino-1.7.10.jar";
   public static final String K2JVMCompiler = "org.jetbrains.kotlin.cli.jvm.K2JVMCompiler";
   private static final String ANDROID_JAR_PATTERN = "third_party/android_jar/lib-v%d/android.jar";
+  private static final String ANDROID_API_VERSIONS_XML_PATTERN =
+      "third_party/android_jar/lib-v%d/api-versions.xml";
   private static final AndroidApiLevel DEFAULT_MIN_SDK = AndroidApiLevel.I;
 
   public static final String JDK_11_TESTS_DIR = "third_party/openjdk/jdk-11-test/";
@@ -837,6 +839,12 @@
     return getAndroidJar(AndroidApiLevel.getAndroidApiLevel(apiLevel));
   }
 
+  public static Path getApiVersionsXmlFile(AndroidApiLevel apiLevel) {
+    // We only store api-versions.xml from S and up.
+    assert apiLevel.isGreaterThanOrEqualTo(AndroidApiLevel.S);
+    return Paths.get(String.format(ANDROID_API_VERSIONS_XML_PATTERN, apiLevel.getLevel()));
+  }
+
   private static Path getAndroidJarPath(AndroidApiLevel apiLevel) {
     String jar = String.format(
         ANDROID_JAR_PATTERN,
diff --git a/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGenerator.java b/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGenerator.java
index 300eed5..6c3d36e 100644
--- a/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGenerator.java
+++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGenerator.java
@@ -5,16 +5,21 @@
 package com.android.tools.r8.apimodel;
 
 import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.androidapi.AndroidApiLevelHashingDatabaseImpl;
 import com.android.tools.r8.apimodel.AndroidApiVersionsXmlParser.ParsedApiClass;
+import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMember;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.LibraryClass;
 import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.Pair;
 import com.android.tools.r8.utils.structural.DefaultHashingVisitor;
 import com.android.tools.r8.utils.structural.HasherWrapper;
@@ -30,6 +35,7 @@
 import java.util.IdentityHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Set;
 import java.util.function.BiConsumer;
 
@@ -48,12 +54,16 @@
       List<ParsedApiClass> apiClasses,
       Path pathToIndices,
       Path pathToApiLevels,
-      Path ambiguousDefinitions)
+      Path ambiguousDefinitions,
+      AndroidApiLevel androidJarApiLevel)
       throws Exception {
     Map<ClassReference, Map<DexMethod, AndroidApiLevel>> methodMap = new HashMap<>();
     Map<ClassReference, Map<DexField, AndroidApiLevel>> fieldMap = new HashMap<>();
     Map<ClassReference, ParsedApiClass> lookupMap = new HashMap<>();
-    DexItemFactory factory = new DexItemFactory();
+    Path androidJar = ToolHelper.getAndroidJar(androidJarApiLevel);
+    AppView<AppInfoWithLiveness> appView =
+        computeAppViewWithLiveness(AndroidApp.builder().addLibraryFile(androidJar).build());
+    DexItemFactory factory = appView.dexItemFactory();
 
     Map<Integer, AndroidApiLevel> apiLevelMap = new HashMap<>();
     Map<Integer, Pair<DexReference, AndroidApiLevel>> reverseMap = new HashMap<>();
@@ -97,13 +107,58 @@
           .forEach(addConsumer);
     }
 
+    Map<DexType, String> missingMemberInformation = new IdentityHashMap<>();
+    for (LibraryClass clazz : appView.app().asDirect().libraryClasses()) {
+      ParsedApiClass parsedApiClass = lookupMap.get(clazz.getClassReference());
+      if (parsedApiClass == null) {
+        missingMemberInformation.put(clazz.getType(), "Could not be found in " + androidJar);
+        continue;
+      }
+      StringBuilder classBuilder = new StringBuilder();
+      Map<DexField, AndroidApiLevel> fieldMapForClass = fieldMap.get(clazz.getClassReference());
+      assert fieldMapForClass != null;
+      clazz.forEachClassField(
+          field -> {
+            if (field.getAccessFlags().isPublic()
+                && getApiLevelFromReference(field.getReference(), apiLevelMap, ambiguousMap) == null
+                && field.toSourceString().contains("this$0")) {
+              classBuilder.append("  ").append(field).append(" is missing\n");
+            }
+          });
+      Map<DexMethod, AndroidApiLevel> methodMapForClass = methodMap.get(clazz.getClassReference());
+      assert methodMapForClass != null;
+      clazz.forEachClassMethod(
+          method -> {
+            if (method.getAccessFlags().isPublic()
+                && getApiLevelFromReference(method.getReference(), apiLevelMap, ambiguousMap)
+                    == null
+                && !factory.objectMembers.isObjectMember(method.getReference())) {
+              classBuilder.append("  ").append(method).append(" is missing\n");
+            }
+          });
+      if (classBuilder.length() > 0) {
+        missingMemberInformation.put(clazz.getType(), classBuilder.toString());
+      }
+    }
+
+    // api-versions.xml do not encode all members of StringBuffers and StringBuilders, check that we
+    // only have missing definitions for those two classes.
+    assert missingMemberInformation.size() == 2;
+    assert missingMemberInformation.containsKey(factory.stringBufferType);
+    assert missingMemberInformation.containsKey(factory.stringBuilderType);
+
     int[] indices = new int[apiLevelMap.size()];
     byte[] apiLevel = new byte[apiLevelMap.size()];
     ArrayList<Integer> integers = new ArrayList<>(apiLevelMap.keySet());
     for (int i = 0; i < integers.size(); i++) {
       indices[i] = integers.get(i);
       AndroidApiLevel androidApiLevel = apiLevelMap.get(integers.get(i));
-      apiLevel[i] = (byte) (androidApiLevel == null ? -1 : androidApiLevel.getLevel());
+      assert androidApiLevel != null;
+      apiLevel[i] =
+          (byte)
+              (androidApiLevel == AndroidApiLevel.ANDROID_PLATFORM
+                  ? -1
+                  : androidApiLevel.getLevel());
     }
 
     try (FileOutputStream fileOutputStream = new FileOutputStream(pathToIndices.toFile());
@@ -152,14 +207,37 @@
     return sb.toString();
   }
 
+  private static AndroidApiLevel getApiLevelFromReference(
+      DexReference reference,
+      Map<Integer, AndroidApiLevel> apiLevelMap,
+      Map<AndroidApiLevel, Set<DexReference>> ambiguousMap) {
+    int hashCode = reference.hashCode();
+    AndroidApiLevel androidApiLevel = apiLevelMap.get(hashCode);
+    if (androidApiLevel == null) {
+      return null;
+    }
+    if (androidApiLevel == AndroidApiLevel.ANDROID_PLATFORM) {
+      for (Entry<AndroidApiLevel, Set<DexReference>> apiAmbiguousSet : ambiguousMap.entrySet()) {
+        if (apiAmbiguousSet.getValue().contains(reference)) {
+          return apiAmbiguousSet.getKey();
+        }
+      }
+      return null;
+    } else {
+      return androidApiLevel;
+    }
+  }
+
   private static BiConsumer<DexReference, AndroidApiLevel> addReferenceToMaps(
       Map<Integer, AndroidApiLevel> apiLevelMap,
       Map<Integer, Pair<DexReference, AndroidApiLevel>> reverseMap,
       Map<AndroidApiLevel, Set<DexReference>> ambiguousMap) {
     return ((reference, apiLevel) -> {
       AndroidApiLevel existingMethod = apiLevelMap.put(reference.hashCode(), apiLevel);
-      if (existingMethod != null) {
-        apiLevelMap.put(reference.hashCode(), null);
+      if (existingMethod == AndroidApiLevel.ANDROID_PLATFORM) {
+        addAmbiguousEntry(apiLevel, reference, ambiguousMap);
+      } else if (existingMethod != null) {
+        apiLevelMap.put(reference.hashCode(), AndroidApiLevel.ANDROID_PLATFORM);
         Pair<DexReference, AndroidApiLevel> existingPair = reverseMap.get(reference.hashCode());
         addAmbiguousEntry(existingPair.getSecond(), existingPair.getFirst(), ambiguousMap);
         addAmbiguousEntry(apiLevel, reference, ambiguousMap);
diff --git a/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java b/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java
index 9bbc751..599829b 100644
--- a/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java
@@ -39,14 +39,14 @@
 public class AndroidApiHashingDatabaseBuilderGeneratorTest extends TestBase {
 
   protected final TestParameters parameters;
-  private static final Path API_VERSIONS_XML =
-      Paths.get(ToolHelper.THIRD_PARTY_DIR, "android_jar", "api-versions", "api-versions.xml");
   private static final Path API_DATABASE_HASH_LOOKUP =
       Paths.get(ToolHelper.RESOURCES_DIR, "api_database", "api_database_hash_lookup.ser");
   private static final Path API_DATABASE_API_LEVEL =
       Paths.get(ToolHelper.RESOURCES_DIR, "api_database", "api_database_api_level.ser");
   private static final Path API_DATABASE_AMBIGUOUS =
       Paths.get(ToolHelper.RESOURCES_DIR, "api_database", "api_database_ambiguous.txt");
+
+  // Update the API_LEVEL below to have the database generated for a new api level.
   private static final AndroidApiLevel API_LEVEL = AndroidApiLevel.S;
 
   @Parameters(name = "{0}")
@@ -73,17 +73,20 @@
 
   private static GenerateDatabaseResourceFilesResult generateResourcesFiles() throws Exception {
     return generateResourcesFiles(
-        AndroidApiVersionsXmlParser.getParsedApiClasses(API_VERSIONS_XML.toFile(), API_LEVEL));
+        AndroidApiVersionsXmlParser.getParsedApiClasses(
+            ToolHelper.getApiVersionsXmlFile(API_LEVEL).toFile(), API_LEVEL),
+        API_LEVEL);
   }
 
   private static GenerateDatabaseResourceFilesResult generateResourcesFiles(
-      List<ParsedApiClass> apiClasses) throws Exception {
+      List<ParsedApiClass> apiClasses, AndroidApiLevel androidJarApiLevel) throws Exception {
     TemporaryFolder temp = new TemporaryFolder();
     temp.create();
     Path indices = temp.newFile("indices.ser").toPath();
     Path apiLevels = temp.newFile("apiLevels.ser").toPath();
     Path ambiguous = temp.newFile("ambiguous.ser").toPath();
-    AndroidApiHashingDatabaseBuilderGenerator.generate(apiClasses, indices, apiLevels, ambiguous);
+    AndroidApiHashingDatabaseBuilderGenerator.generate(
+        apiClasses, indices, apiLevels, ambiguous, androidJarApiLevel);
     return new GenerateDatabaseResourceFilesResult(indices, apiLevels, ambiguous);
   }
 
@@ -92,7 +95,8 @@
     // This tests makes a rudimentary check on the number of classes, fields and methods in
     // api-versions.xml to ensure that the runtime tests do not vacuously succeed.
     List<ParsedApiClass> parsedApiClasses =
-        AndroidApiVersionsXmlParser.getParsedApiClasses(API_VERSIONS_XML.toFile(), API_LEVEL);
+        AndroidApiVersionsXmlParser.getParsedApiClasses(
+            ToolHelper.getApiVersionsXmlFile(API_LEVEL).toFile(), API_LEVEL);
     IntBox numberOfFields = new IntBox(0);
     IntBox numberOfMethods = new IntBox(0);
     parsedApiClasses.forEach(
@@ -107,9 +111,9 @@
               }));
         });
     // These numbers will change when updating api-versions.xml
-    assertEquals(5037, parsedApiClasses.size());
-    assertEquals(26362, numberOfFields.get());
-    assertEquals(40416, numberOfMethods.get());
+    assertEquals(5065, parsedApiClasses.size());
+    assertEquals(26492, numberOfFields.get());
+    assertEquals(40475, numberOfMethods.get());
   }
 
   @Test
@@ -123,7 +127,8 @@
   @Test
   public void testCanLookUpAllParsedApiClassesAndMembers() throws Exception {
     List<ParsedApiClass> parsedApiClasses =
-        AndroidApiVersionsXmlParser.getParsedApiClasses(API_VERSIONS_XML.toFile(), API_LEVEL);
+        AndroidApiVersionsXmlParser.getParsedApiClasses(
+            ToolHelper.getApiVersionsXmlFile(API_LEVEL).toFile(), API_LEVEL);
     DexItemFactory factory = new DexItemFactory();
     AndroidApiLevelHashingDatabaseImpl androidApiLevelDatabase =
         new AndroidApiLevelHashingDatabaseImpl(ImmutableList.of());
@@ -168,9 +173,7 @@
    * and override the current file in there.
    */
   public static void main(String[] args) throws Exception {
-    List<ParsedApiClass> parsedApiClasses =
-        AndroidApiVersionsXmlParser.getParsedApiClasses(API_VERSIONS_XML.toFile(), API_LEVEL);
-    GenerateDatabaseResourceFilesResult result = generateResourcesFiles(parsedApiClasses);
+    GenerateDatabaseResourceFilesResult result = generateResourcesFiles();
     verifyNoDuplicateHashes(result.indices);
     Files.move(result.indices, API_DATABASE_HASH_LOOKUP, REPLACE_EXISTING);
     Files.move(result.apiLevels, API_DATABASE_API_LEVEL, REPLACE_EXISTING);
diff --git a/src/test/java/com/android/tools/r8/apimodel/AndroidApiVersionsXmlParser.java b/src/test/java/com/android/tools/r8/apimodel/AndroidApiVersionsXmlParser.java
index 24b9613..4a3cfa2 100644
--- a/src/test/java/com/android/tools/r8/apimodel/AndroidApiVersionsXmlParser.java
+++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiVersionsXmlParser.java
@@ -60,7 +60,11 @@
       String type = DescriptorUtils.getJavaTypeFromBinaryName(getName(node));
       ClassSubject clazz = inspector.clazz(type);
       if (!clazz.isPresent()) {
-        // TODO(b/190326408): Investigate why the class is not present.
+        if (!clazz.getOriginalName().startsWith("android.test")
+            && !clazz.getOriginalName().startsWith("junit")) {
+          assert hasRemoved(node);
+          assert getRemoved(node).isLessThanOrEqualTo(maxApiLevel);
+        }
         continue;
       }
       ClassReference originalReference = clazz.getOriginalReference();
@@ -77,7 +81,6 @@
               Reference.classFromBinaryName(getName(memberNode)),
               hasSince(memberNode) ? getSince(memberNode) : apiLevel);
         } else if (isMethod(memberNode)) {
-          // TODO(b/190326408): Check for existence.
           parsedApiClass.register(
               getMethodReference(originalReference, memberNode),
               getMaxAndroidApiLevelFromNode(memberNode, apiLevel));
@@ -85,7 +88,8 @@
           // The field do not have descriptors and are supposed to be unique.
           FieldSubject fieldSubject = clazz.uniqueFieldWithName(getName(memberNode));
           if (!fieldSubject.isPresent()) {
-            // TODO(b/190326408): Investigate why the member is not present.
+            assert hasRemoved(memberNode);
+            assert getRemoved(memberNode).isLessThanOrEqualTo(maxApiLevel);
             continue;
           }
           parsedApiClass.register(
@@ -131,12 +135,22 @@
     return node.getAttributes().getNamedItem("since") != null;
   }
 
+  private boolean hasRemoved(Node node) {
+    return node.getAttributes().getNamedItem("removed") != null;
+  }
+
   private AndroidApiLevel getSince(Node node) {
     assert hasSince(node);
     Node since = node.getAttributes().getNamedItem("since");
     return AndroidApiLevel.getAndroidApiLevel(Integer.parseInt(since.getNodeValue()));
   }
 
+  private AndroidApiLevel getRemoved(Node node) {
+    assert hasRemoved(node);
+    Node removed = node.getAttributes().getNamedItem("removed");
+    return AndroidApiLevel.getAndroidApiLevel(Integer.parseInt(removed.getNodeValue()));
+  }
+
   private AndroidApiLevel getMaxAndroidApiLevelFromNode(Node node, AndroidApiLevel defaultValue) {
     if (node == null || !hasSince(node)) {
       return defaultValue;
diff --git a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkCollection.java b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkCollection.java
index f75aabe..da0c335 100644
--- a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkCollection.java
+++ b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkCollection.java
@@ -3,27 +3,40 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.benchmarks;
 
+import static java.util.Collections.emptyList;
+
 import com.android.tools.r8.benchmarks.helloworld.HelloWorldBenchmark;
-import com.android.tools.r8.errors.Unreachable;
 import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
+import java.util.stream.Collectors;
 
 public class BenchmarkCollection {
 
   // Actual list of all configured benchmarks.
-  private final Map<BenchmarkIdentifier, BenchmarkConfig> benchmarks = new HashMap<>();
+  private final Map<String, List<BenchmarkConfig>> benchmarks = new HashMap<>();
 
   private void addBenchmark(BenchmarkConfig benchmark) {
-    BenchmarkIdentifier id = benchmark.getIdentifier();
-    if (benchmarks.containsKey(id)) {
-      throw new Unreachable("Duplicate definition of benchmark with name and target: " + id);
+    List<BenchmarkConfig> variants =
+        benchmarks.computeIfAbsent(benchmark.getName(), k -> new ArrayList<>());
+    for (BenchmarkConfig variant : variants) {
+      BenchmarkConfig.checkBenchmarkConsistency(benchmark, variant);
     }
-    benchmarks.put(id, benchmark);
+    variants.add(benchmark);
   }
 
-  public BenchmarkConfig getBenchmark(BenchmarkIdentifier benchmark) {
-    return benchmarks.get(benchmark);
+  public BenchmarkConfig getBenchmark(BenchmarkIdentifier identifier) {
+    assert identifier != null;
+    List<BenchmarkConfig> configs = benchmarks.getOrDefault(identifier.getName(), emptyList());
+    for (BenchmarkConfig config : configs) {
+      if (identifier.equals(config.getIdentifier())) {
+        return config;
+      }
+    }
+    return null;
   }
 
   public static BenchmarkCollection computeCollection() {
@@ -36,6 +49,9 @@
   /** Compute and print the golem configuration. */
   public static void main(String[] args) throws IOException {
     new BenchmarkCollectionPrinter(System.out)
-        .printGolemConfig(computeCollection().benchmarks.values());
+        .printGolemConfig(
+            computeCollection().benchmarks.values().stream()
+                .flatMap(Collection::stream)
+                .collect(Collectors.toList()));
   }
 }
diff --git a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkCollectionPrinter.java b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkCollectionPrinter.java
index e3ede15..b7786f7 100644
--- a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkCollectionPrinter.java
+++ b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkCollectionPrinter.java
@@ -3,11 +3,9 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.benchmarks;
 
-import static com.android.tools.r8.utils.ListUtils.map;
-
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.ProcessResult;
-import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.StringUtils.BraceType;
 import com.google.common.base.Strings;
@@ -88,9 +86,9 @@
         b -> nameToTargets.computeIfAbsent(b.getName(), k -> new ArrayList<>()).add(b));
     List<String> sortedNames = new ArrayList<>(nameToTargets.keySet());
     sortedNames.sort(String::compareTo);
-    print(
-        "// AUTOGENERATED FILE from"
-            + " src/test/java/com/android/tools/r8/benchmarks/BenchmarkCollection.java");
+    print("// AUTOGENERATED FILE generated with");
+    print("// src/test/java/com/android/tools/r8/benchmarks/BenchmarkCollection.java");
+    print("// in the R8 repository.");
     print("");
     printSemi("part of r8_config");
     print("");
@@ -108,21 +106,20 @@
     print("}");
   }
 
-  private void printBenchmarkBlock(String benchmarkName, List<BenchmarkConfig> benchmarkTargets)
+  private void printBenchmarkBlock(String benchmarkName, List<BenchmarkConfig> benchmarkVariants)
       throws IOException {
+    // Common properties that must be consistent among all the benchmark variants.
+    String suite = BenchmarkConfig.getCommonSuite(benchmarkVariants).getDartName();
+    boolean hasWarmup = BenchmarkConfig.getCommonTimeWarmupRuns(benchmarkVariants);
+    List<String> metrics =
+        new ArrayList<>(
+            ListUtils.map(
+                BenchmarkConfig.getCommonMetrics(benchmarkVariants), BenchmarkMetric::getDartType));
+    metrics.sort(String::compareTo);
     printSemi("final name = " + quote(benchmarkName));
-    printSemi("final group = new GroupBenchmark(name + \"Group\", [])");
-    // NOTE: It appears these must be consistent for each target now?
-    boolean hasWarmup = false;
-    String suite = null;
-    List<String> metrics = null;
-    for (BenchmarkConfig benchmark : benchmarkTargets) {
-      if (metrics == null) {
-        // TODO: Verify equal on other runs.
-        hasWarmup = benchmark.hasTimeWarmupRuns();
-        suite = benchmark.getSuite().getDartName();
-        metrics = map(benchmark.getMetrics(), BenchmarkMetric::getDartType);
-      }
+    printSemi("final metrics = " + StringUtils.join(", ", metrics, BraceType.SQUARE));
+    printSemi("final group = new GroupBenchmark(name + \"Group\", metrics)");
+    for (BenchmarkConfig benchmark : benchmarkVariants) {
       scopeBraces(
           () -> {
             printSemi("final target = " + quote(benchmark.getTarget().getGolemName()));
@@ -144,21 +141,13 @@
             printSemi("options.resources.add(openjdk)");
           });
     }
-
-    List<String> finalMetrics = metrics;
-    String finalSuite = suite;
-    boolean finalHasWarmup = hasWarmup;
-    scopeBraces(
-        () -> {
-          printSemi("final metrics = " + StringUtils.join(", ", finalMetrics, BraceType.SQUARE));
-          printSemi("group.addBenchmark(name, metrics)");
-          printSemi(finalSuite + ".addBenchmark(name)");
-          if (finalHasWarmup) {
-            printSemi("final warmupName = name + \"Warmup\"");
-            printSemi("group.addBenchmark(warmupName, [Metric.RunTimeRaw])");
-            printSemi(finalSuite + ".addBenchmark(warmupName)");
-          }
-        });
+    printSemi("group.addBenchmark(name, metrics)");
+    printSemi(suite + ".addBenchmark(name)");
+    if (hasWarmup) {
+      printSemi("final warmupName = name + \"Warmup\"");
+      printSemi("group.addBenchmark(warmupName, [Metric.RunTimeRaw])");
+      printSemi(suite + ".addBenchmark(warmupName)");
+    }
   }
 
   private void addGolemResource(String name, Path tarball) throws IOException {
@@ -187,7 +176,7 @@
     ProcessBuilder builder = new ProcessBuilder("python", "tools/jdk.py");
     ProcessResult result = ToolHelper.runProcess(builder, QUIET);
     if (result.exitCode != 0) {
-      throw new Unreachable("Unexpected failure to determine jdk home: " + result);
+      throw new BenchmarkConfigError("Unexpected failure to determine jdk home: " + result);
     }
     return Paths.get(result.stdout.trim());
   }
@@ -207,7 +196,7 @@
             "download_from_google_storage", "-n", "-b", "r8-deps", "-u", "-s", path.toString());
     ProcessResult result = ToolHelper.runProcess(builder, QUIET);
     if (result.exitCode != 0) {
-      throw new Unreachable("Unable to download dependency '" + path + "'\n" + result);
+      throw new BenchmarkConfigError("Unable to download dependency '" + path + "'\n" + result);
     }
   }
 }
diff --git a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkConfig.java b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkConfig.java
index 09d7a6e..c64dbd2 100644
--- a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkConfig.java
+++ b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkConfig.java
@@ -6,11 +6,53 @@
 import com.android.tools.r8.errors.Unreachable;
 import com.google.common.collect.ImmutableSet;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Set;
 import org.junit.rules.TemporaryFolder;
 
 public class BenchmarkConfig {
 
+  public static void checkBenchmarkConsistency(BenchmarkConfig benchmark, BenchmarkConfig other) {
+    if (benchmark.getTarget().equals(other.getTarget())) {
+      throw new BenchmarkConfigError("Duplicate benchmark name and target: " + benchmark);
+    }
+    if (!benchmark.getMetrics().equals(other.getMetrics())) {
+      throw new BenchmarkConfigError(
+          "Inconsistent metrics for benchmarks: " + benchmark + " and " + other);
+    }
+    if (!benchmark.getSuite().equals(other.getSuite())) {
+      throw new BenchmarkConfigError(
+          "Inconsistent suite for benchmarks: " + benchmark + " and " + other);
+    }
+    if (benchmark.hasTimeWarmupRuns() != other.hasTimeWarmupRuns()) {
+      throw new BenchmarkConfigError(
+          "Inconsistent time-warmup for benchmarks: " + benchmark + " and " + other);
+    }
+  }
+
+  public static Set<BenchmarkMetric> getCommonMetrics(List<BenchmarkConfig> variants) {
+    return getConsistentRepresentative(variants).getMetrics();
+  }
+
+  public static BenchmarkSuite getCommonSuite(List<BenchmarkConfig> variants) {
+    return getConsistentRepresentative(variants).getSuite();
+  }
+
+  public static boolean getCommonTimeWarmupRuns(List<BenchmarkConfig> variants) {
+    return getConsistentRepresentative(variants).hasTimeWarmupRuns();
+  }
+
+  private static BenchmarkConfig getConsistentRepresentative(List<BenchmarkConfig> variants) {
+    if (variants.isEmpty()) {
+      throw new BenchmarkConfigError("Unexpected attempt to check consistency of empty collection");
+    }
+    BenchmarkConfig representative = variants.get(0);
+    for (int i = 1; i < variants.size(); i++) {
+      checkBenchmarkConsistency(representative, variants.get(i));
+    }
+    return representative;
+  }
+
   public static class Builder {
 
     private String name = null;
@@ -128,7 +170,7 @@
 
   public String getWarmupName() {
     if (!timeWarmupRuns) {
-      throw new Unreachable("Invalid attempt at getting warmup benchmark name");
+      throw new BenchmarkConfigError("Invalid attempt at getting warmup benchmark name");
     }
     return getName() + "Warmup";
   }
diff --git a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkConfigError.java b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkConfigError.java
new file mode 100644
index 0000000..9ed6e31
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkConfigError.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.benchmarks;
+
+public class BenchmarkConfigError extends RuntimeException {
+
+  public BenchmarkConfigError(String message) {
+    super(message);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResults.java b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResults.java
index cf55b86..f436c26 100644
--- a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResults.java
+++ b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResults.java
@@ -4,7 +4,6 @@
 package com.android.tools.r8.benchmarks;
 
 import com.android.tools.r8.benchmarks.BenchmarkRunner.ResultMode;
-import com.android.tools.r8.errors.Unreachable;
 import it.unimi.dsi.fastutil.longs.LongArrayList;
 import it.unimi.dsi.fastutil.longs.LongList;
 
@@ -40,7 +39,7 @@
 
   private static void verifyMetric(BenchmarkMetric metric, boolean expected, boolean actual) {
     if (expected != actual) {
-      throw new Unreachable(
+      throw new BenchmarkConfigError(
           "Mismatched config and result for "
               + metric.name()
               + ". Expected by config: "
@@ -87,7 +86,7 @@
       long size = codeSizeResults.getLong(0);
       for (int i = 1; i < codeSizeResults.size(); i++) {
         if (size != codeSizeResults.getLong(i)) {
-          throw new Unreachable(
+          throw new RuntimeException(
               "Unexpected code size difference: " + size + " and " + codeSizeResults.getLong(i));
         }
       }
diff --git a/src/test/java/com/android/tools/r8/cf/InlineCmpDoubleTest.java b/src/test/java/com/android/tools/r8/cf/InlineCmpDoubleTest.java
index 1db3ee4..36e6189 100644
--- a/src/test/java/com/android/tools/r8/cf/InlineCmpDoubleTest.java
+++ b/src/test/java/com/android/tools/r8/cf/InlineCmpDoubleTest.java
@@ -5,6 +5,7 @@
 
 import static org.junit.Assert.assertEquals;
 
+import com.android.tools.r8.KeepUnusedReturnValue;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.naming.MemberNaming.MethodSignature;
@@ -42,6 +43,7 @@
         .addProgramClasses(TestClass.class)
         .addKeepMainRule(TestClass.class)
         .addOptionsModification(options -> options.inlinerOptions().enableInlining = enableInlining)
+        .enableKeepUnusedReturnValueAnnotations()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(this::inspect)
@@ -66,6 +68,7 @@
       inlineMe(x + 41);
     }
 
+    @KeepUnusedReturnValue
     public static int inlineMe(int x) {
       // Side effect to ensure that the invocation is not removed simply because the method does not
       // have any side effects.
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
index b056931..bfd9f97 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
@@ -15,6 +15,7 @@
 import static org.junit.Assume.assumeTrue;
 
 import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.KeepUnusedReturnValue;
 import com.android.tools.r8.ProgramResourceProvider;
 import com.android.tools.r8.R8FullTestBuilder;
 import com.android.tools.r8.R8TestCompileResult;
@@ -184,9 +185,12 @@
         "return");
 
     // Add two methods with the same name that have return types A[] and B[], respectively.
+    classBuilder.addRuntimeInvisibleAnnotation(KeepUnusedReturnValue.class.getTypeName());
     classBuilder.addStaticMethod(
         "method", ImmutableList.of(), "[Lclassmerging/A;",
         ".limit stack 1", ".limit locals 1", "iconst_0", "anewarray classmerging/A", "areturn");
+
+    classBuilder.addRuntimeInvisibleAnnotation(KeepUnusedReturnValue.class.getTypeName());
     classBuilder.addStaticMethod(
         "method", ImmutableList.of(), "[Lclassmerging/B;",
         ".limit stack 1", ".limit locals 1", "iconst_0", "anewarray classmerging/B", "areturn");
@@ -209,7 +213,8 @@
                 "-neverinline class " + main + " {",
                 "  static classmerging.A[] method(...);",
                 "  static classmerging.B[] method(...);",
-                "}"),
+                "}")
+            .enableKeepUnusedReturnValueAnnotations(),
         main,
         jasminBuilder.build(),
         preservedClassNames::contains);
diff --git a/src/test/java/com/android/tools/r8/compilerapi/assertionconfiguration/AssertionConfigurationTest.java b/src/test/java/com/android/tools/r8/compilerapi/assertionconfiguration/AssertionConfigurationTest.java
index aa85988..5a17702 100644
--- a/src/test/java/com/android/tools/r8/compilerapi/assertionconfiguration/AssertionConfigurationTest.java
+++ b/src/test/java/com/android/tools/r8/compilerapi/assertionconfiguration/AssertionConfigurationTest.java
@@ -27,6 +27,7 @@
 import com.android.tools.r8.utils.codeinspector.InstructionSubject;
 import com.android.tools.r8.utils.codeinspector.InvokeInstructionSubject;
 import java.nio.file.Path;
+import java.util.Collections;
 import org.junit.Test;
 
 public class AssertionConfigurationTest extends CompilerApiTestRunner {
@@ -71,7 +72,7 @@
     test.accept(new DexIndexedConsumer.ArchiveConsumer(output), assertionHandler);
 
     // TODO(b/209445989): This should be true when the assertion handler support is implemented.
-    assertFalse(
+    assertTrue(
         new CodeInspector(output)
             .clazz(MockClassWithAssertion.class)
             .uniqueMethodWithName("main")
@@ -118,6 +119,8 @@
               .addClassProgramData(getBytesForClass(getMockClassWithAssertion()), Origin.unknown())
               .addProguardConfiguration(
                   getKeepMainRules(getMockClassWithAssertion()), Origin.unknown())
+              .addProguardConfiguration(
+                  Collections.singletonList("-dontwarn com.example.SomeClass"), Origin.unknown())
               .addLibraryFiles(getJava8RuntimeJar())
               .addAssertionsConfiguration(
                   builder -> builder.setAssertionHandler(assertionHandler).setScopeAll().build())
diff --git a/src/test/java/com/android/tools/r8/desugar/InvokeSuperToRewrittenDefaultMethodTest.java b/src/test/java/com/android/tools/r8/desugar/InvokeSuperToRewrittenDefaultMethodTest.java
index 72cdc59..c8f932d 100644
--- a/src/test/java/com/android/tools/r8/desugar/InvokeSuperToRewrittenDefaultMethodTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/InvokeSuperToRewrittenDefaultMethodTest.java
@@ -3,23 +3,15 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.desugar;
 
-import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
-import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
-import static org.hamcrest.CoreMatchers.allOf;
-import static org.hamcrest.CoreMatchers.containsString;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
 import static org.junit.Assume.assumeFalse;
 import static org.junit.Assume.assumeTrue;
 
-import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.LibraryDesugaringTestConfiguration;
+import com.android.tools.r8.TestDiagnosticMessages;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
 import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.BooleanUtils;
-import com.android.tools.r8.utils.ExceptionDiagnostic;
 import com.android.tools.r8.utils.StringUtils;
 import java.util.List;
 import java.util.function.Consumer;
@@ -33,19 +25,15 @@
 
   private static final String EXPECTED = StringUtils.lines("Y", "89");
 
-  @Parameterized.Parameters(name = "{0}, old-rt:{1}")
+  @Parameterized.Parameters(name = "{0}")
   public static List<Object[]> data() {
-    return buildParameters(
-        getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.values());
+    return buildParameters(getTestParameters().withAllRuntimesAndApiLevels().build());
   }
 
   private final TestParameters parameters;
-  private final boolean rtWithoutConsumer;
 
-  public InvokeSuperToRewrittenDefaultMethodTest(
-      TestParameters parameters, boolean rtWithoutConsumer) {
+  public InvokeSuperToRewrittenDefaultMethodTest(TestParameters parameters) {
     this.parameters = parameters;
-    this.rtWithoutConsumer = rtWithoutConsumer;
   }
 
   private boolean needsDefaultInterfaceMethodDesugaring() {
@@ -65,35 +53,15 @@
   @Test
   public void testDesugaring() throws Exception {
     assumeTrue(needsDefaultInterfaceMethodDesugaring());
-    try {
-      testForD8()
-          .addInnerClasses(InvokeSuperToRewrittenDefaultMethodTest.class)
-          .setMinApi(parameters.getApiLevel())
-          .addLibraryFiles(
-              rtWithoutConsumer
-                  ? ToolHelper.getAndroidJar(AndroidApiLevel.B)
-                  : ToolHelper.getAndroidJar(AndroidApiLevel.P))
-          .enableCoreLibraryDesugaring(
-              LibraryDesugaringTestConfiguration.forApiLevel(parameters.getApiLevel()))
-          .compileWithExpectedDiagnostics(
-              diagnostics -> {
-                if (rtWithoutConsumer) {
-                  diagnostics.assertOnlyErrors();
-                  // TODO(b/158543011): Should fail with a nice user error for invalid library.
-                  diagnostics.assertErrorsMatch(
-                      allOf(
-                          diagnosticType(ExceptionDiagnostic.class),
-                          diagnosticMessage(containsString("AssertionError"))));
-                } else {
-                  diagnostics.assertNoMessages();
-                }
-              })
-          .run(parameters.getRuntime(), TestClass.class)
-          .assertSuccessWithOutput(EXPECTED);
-      assertFalse(rtWithoutConsumer);
-    } catch (CompilationFailedException e) {
-      assertTrue(rtWithoutConsumer);
-    }
+    testForD8()
+        .addInnerClasses(InvokeSuperToRewrittenDefaultMethodTest.class)
+        .setMinApi(parameters.getApiLevel())
+        .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
+        .enableCoreLibraryDesugaring(
+            LibraryDesugaringTestConfiguration.forApiLevel(parameters.getApiLevel()))
+        .compileWithExpectedDiagnostics(TestDiagnosticMessages::assertNoMessages)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(EXPECTED);
   }
 
   public interface CharConsumer extends Consumer<Character>, IntConsumer {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/AtomicReferenceArrayTest.java b/src/test/java/com/android/tools/r8/desugar/backports/AtomicReferenceArrayTest.java
new file mode 100644
index 0000000..2c2af2b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/backports/AtomicReferenceArrayTest.java
@@ -0,0 +1,44 @@
+// 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.backports;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.util.concurrent.atomic.AtomicReferenceArray;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class AtomicReferenceArrayTest extends AbstractBackportTest {
+
+  @Parameters(name = "{0}")
+  public static Iterable<?> data() {
+    return getTestParameters()
+        .withDexRuntimesStartingFromExcluding(Version.V4_0_4)
+        .withAllApiLevels()
+        .build();
+  }
+
+  public AtomicReferenceArrayTest(TestParameters parameters) {
+    super(parameters, AtomicReferenceFieldUpdater.class, Main.class);
+
+    // java.util.concurrent.atomic.AtomicReferenceArray issue is on API 31, see b/211646483.
+    registerTarget(AndroidApiLevel.Sv2, 3);
+  }
+
+  public static class Main extends MiniAssert {
+    public volatile String field;
+
+    public static void main(String[] args) throws Exception {
+      AtomicReferenceArray<String> reference = new AtomicReferenceArray<>(new String[1]);
+      assertTrue(reference.compareAndSet(0, null, "A"));
+      assertTrue(reference.compareAndSet(0, "A", "B"));
+      assertFalse(reference.compareAndSet(0, "A", "B"));
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/AtomicReferenceTest.java b/src/test/java/com/android/tools/r8/desugar/backports/AtomicReferenceTest.java
new file mode 100644
index 0000000..1f5d11e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/backports/AtomicReferenceTest.java
@@ -0,0 +1,44 @@
+// 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.backports;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class AtomicReferenceTest extends AbstractBackportTest {
+
+  @Parameters(name = "{0}")
+  public static Iterable<?> data() {
+    return getTestParameters()
+        .withDexRuntimesStartingFromExcluding(Version.V4_0_4)
+        .withAllApiLevels()
+        .build();
+  }
+
+  public AtomicReferenceTest(TestParameters parameters) {
+    super(parameters, AtomicReferenceFieldUpdater.class, Main.class);
+
+    // java.util.concurrent.atomic.AtomicReference issue is on API 31, see b/211646483.
+    registerTarget(AndroidApiLevel.Sv2, 3);
+  }
+
+  public static class Main extends MiniAssert {
+    public volatile String field;
+
+    public static void main(String[] args) throws Exception {
+      AtomicReference<String> reference = new AtomicReference<>(null);
+      assertTrue(reference.compareAndSet(null, "A"));
+      assertTrue(reference.compareAndSet("A", "B"));
+      assertFalse(reference.compareAndSet("A", "B"));
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/TestBackportedNotPresentInAndroidJar.java b/src/test/java/com/android/tools/r8/desugar/backports/TestBackportedNotPresentInAndroidJar.java
index 8db2a9c..237b0e1 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/TestBackportedNotPresentInAndroidJar.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/TestBackportedNotPresentInAndroidJar.java
@@ -29,6 +29,8 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.concurrent.atomic.AtomicReferenceArray;
 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
 import java.util.stream.Collectors;
 import org.junit.Test;
@@ -49,12 +51,29 @@
 
   private Set<DexMethod> expectedToAlwaysBePresentInAndroidJar(DexItemFactory factory)
       throws Exception {
-    MethodReference compareAndSet =
+    MethodReference AtomicReferenceFieldUpdater_compareAndSet =
         Reference.methodFromMethod(
             AtomicReferenceFieldUpdater.class.getDeclaredMethod(
                 "compareAndSet", Object.class, Object.class, Object.class));
-    assert compareAndSet.getReturnType().getTypeName().equals("boolean");
-    return ImmutableSet.of(factory.createMethod(compareAndSet));
+    assert AtomicReferenceFieldUpdater_compareAndSet.getReturnType()
+        .getTypeName()
+        .equals("boolean");
+
+    MethodReference AtomicReference_compareAndSet =
+        Reference.methodFromMethod(
+            AtomicReference.class.getDeclaredMethod("compareAndSet", Object.class, Object.class));
+    assert AtomicReference_compareAndSet.getReturnType().getTypeName().equals("boolean");
+
+    MethodReference AtomicReferenceArray_compareAndSet =
+        Reference.methodFromMethod(
+            AtomicReferenceArray.class.getDeclaredMethod(
+                "compareAndSet", int.class, Object.class, Object.class));
+    assert AtomicReference_compareAndSet.getReturnType().getTypeName().equals("boolean");
+
+    return ImmutableSet.of(
+        factory.createMethod(AtomicReferenceFieldUpdater_compareAndSet),
+        factory.createMethod(AtomicReference_compareAndSet),
+        factory.createMethod(AtomicReferenceArray_compareAndSet));
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java
index 6b54606..b5e5c03 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java
@@ -73,11 +73,13 @@
   }
 
   private void configurationForProgramCompilation(InternalOptions options) {
-    options.desugaredLibrarySpecification = configurationAlternative3(options, false, parameters);
+    setDesugaredLibrarySpecificationForTesting(
+        options, configurationAlternative3(options, false, parameters));
   }
 
   private void configurationForLibraryCompilation(InternalOptions options) {
-    options.desugaredLibrarySpecification = configurationAlternative3(options, true, parameters);
+    setDesugaredLibrarySpecificationForTesting(
+        options, configurationAlternative3(options, true, parameters));
   }
 
   @Test
@@ -141,10 +143,7 @@
     KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
     testForD8()
         .addLibraryFiles(getLibraryFile())
-        .addOptionsModification(
-            options ->
-                options.desugaredLibrarySpecification =
-                    configurationAlternative3(options, false, parameters))
+        .addOptionsModification(this::configurationForProgramCompilation)
         .addInnerClasses(BufferedReaderTest.class)
         .setMinApi(parameters.getApiLevel())
         .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
@@ -170,10 +169,7 @@
     KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
     testForR8(parameters.getBackend())
         .addLibraryFiles(getLibraryFile())
-        .addOptionsModification(
-            options ->
-                options.desugaredLibrarySpecification =
-                    configurationAlternative3(options, false, parameters))
+        .addOptionsModification(this::configurationForProgramCompilation)
         .addInnerClasses(BufferedReaderTest.class)
         .addKeepMainRule(TestClass.class)
         .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionTest.java
index d1e9746..473153c 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionTest.java
@@ -10,17 +10,11 @@
 import com.android.tools.r8.D8TestRunResult;
 import com.android.tools.r8.R8TestRunResult;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecification;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion.HumanToMachineSpecificationConverter;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion.LegacyToHumanSpecificationConverter;
 import com.android.tools.r8.utils.BooleanUtils;
-import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.InstructionSubject;
 import com.android.tools.r8.utils.codeinspector.InstructionSubject.JumboStringMode;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
-import java.io.IOException;
 import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -40,19 +34,15 @@
 
   private final TestParameters parameters;
   private final boolean shrinkDesugaredLibrary;
-  private final boolean machineSpec;
 
-  @Parameters(name = "machine: {0}, {2}, shrink: {1}")
+  @Parameters(name = "machine: {0}, shrink: {1}")
   public static List<Object[]> data() {
     return buildParameters(
         BooleanUtils.values(),
-        BooleanUtils.values(),
         getTestParameters().withDexRuntimes().withAllApiLevels().build());
   }
 
-  public CustomCollectionTest(
-      boolean machineSpec, boolean shrinkDesugaredLibrary, TestParameters parameters) {
-    this.machineSpec = machineSpec;
+  public CustomCollectionTest(boolean shrinkDesugaredLibrary, TestParameters parameters) {
     this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
     this.parameters = parameters;
   }
@@ -60,22 +50,6 @@
   private final String EXECUTOR =
       "com.android.tools.r8.desugar.desugaredlibrary.CustomCollectionTest$Executor";
 
-  private void setMachineSpec(InternalOptions opt) {
-    if (!machineSpec) {
-      return;
-    }
-    try {
-      HumanDesugaredLibrarySpecification human =
-          new LegacyToHumanSpecificationConverter()
-              .convert(opt.desugaredLibrarySpecification, getLibraryFile(), opt);
-      MachineDesugaredLibrarySpecification machine =
-          new HumanToMachineSpecificationConverter().convert(human, getLibraryFile(), opt);
-      opt.testing.machineDesugaredLibrarySpecification = machine;
-    } catch (IOException e) {
-      throw new RuntimeException(e);
-    }
-  }
-
   @Test
   public void testCustomCollectionD8() throws Exception {
     KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
@@ -83,7 +57,6 @@
         testForD8()
             .addLibraryFiles(getLibraryFile())
             .addInnerClasses(CustomCollectionTest.class)
-            .addOptionsModification(this::setMachineSpec)
             .setMinApi(parameters.getApiLevel())
             .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
             .compile()
@@ -106,7 +79,6 @@
     Path jar =
         testForD8(Backend.CF)
             .addInnerClasses(CustomCollectionTest.class)
-            .addOptionsModification(this::setMachineSpec)
             .setMinApi(parameters.getApiLevel())
             .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
             .compile()
@@ -162,7 +134,6 @@
         testForR8(Backend.DEX)
             .addLibraryFiles(getLibraryFile())
             .addInnerClasses(CustomCollectionTest.class)
-            .addOptionsModification(this::setMachineSpec)
             .setMinApi(parameters.getApiLevel())
             .addKeepClassAndMembersRules(Executor.class)
             .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryCHMOnlyContentTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryCHMOnlyContentTest.java
index 3e7d97d..e27dda9 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryCHMOnlyContentTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryCHMOnlyContentTest.java
@@ -43,10 +43,9 @@
             "",
             false,
             Collections.emptyList(),
-            options -> {
-              options.desugaredLibrarySpecification =
-                  chmOnlyConfiguration(options, true, parameters);
-            });
+            options ->
+                setDesugaredLibrarySpecificationForTesting(
+                    options, chmOnlyConfiguration(options, true, parameters)));
     CodeInspector inspector = new CodeInspector(desugaredLib);
     assert inspector.clazz("j$.util.concurrent.ConcurrentHashMap").isPresent();
   }
@@ -61,10 +60,9 @@
             "-keep class * { *; }",
             true,
             Collections.emptyList(),
-            options -> {
-              options.desugaredLibrarySpecification =
-                  chmOnlyConfiguration(options, true, parameters);
-            });
+            options ->
+                setDesugaredLibrarySpecificationForTesting(
+                    options, chmOnlyConfiguration(options, true, parameters)));
     CodeInspector inspector = new CodeInspector(desugaredLib);
     assert inspector.clazz("j$.util.concurrent.ConcurrentHashMap").isPresent();
   }
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryContentTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryContentTest.java
index e353126..819aa45 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryContentTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryContentTest.java
@@ -5,15 +5,19 @@
 package com.android.tools.r8.desugar.desugaredlibrary;
 
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static junit.framework.TestCase.assertEquals;
 import static junit.framework.TestCase.assertTrue;
 import static org.hamcrest.CoreMatchers.startsWith;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.fail;
 
 import com.android.tools.r8.L8Command;
+import com.android.tools.r8.L8TestBuilder;
 import com.android.tools.r8.OutputMode;
 import com.android.tools.r8.StringResource;
+import com.android.tools.r8.TestDiagnosticMessages;
 import com.android.tools.r8.TestDiagnosticMessagesImpl;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
@@ -25,6 +29,7 @@
 import com.android.tools.r8.utils.codeinspector.InstructionSubject;
 import java.nio.file.Path;
 import java.util.ArrayList;
+import java.util.Collections;
 import org.junit.Assume;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -45,6 +50,33 @@
   }
 
   @Test
+  public void testInvalidLibrary() throws Exception {
+    Assume.assumeTrue(requiresAnyCoreLibDesugaring(parameters));
+    L8TestBuilder l8TestBuilder =
+        testForL8(parameters.getApiLevel())
+            .addProgramFiles(Collections.singleton(ToolHelper.getDesugarJDKLibs()))
+            .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.L))
+            .setDesugarJDKLibsConfiguration(ToolHelper.DESUGAR_LIB_CONVERSIONS);
+    try {
+      l8TestBuilder.compile();
+      fail();
+    } catch (AssertionError ae) {
+      // Expected since the library is invalid.
+    }
+    TestDiagnosticMessages diagnosticMessages = l8TestBuilder.getDiagnosticMessages();
+    diagnosticMessages.assertOnlyWarnings();
+    assertEquals(diagnosticMessages.getWarnings().size(), 1);
+    assertTrue(
+        diagnosticMessages
+            .getWarnings()
+            .get(0)
+            .getDiagnosticMessage()
+            .contains(
+                "Desugared library requires to be compiled with a library file of API greater or"
+                    + " equal to"));
+  }
+
+  @Test
   public void testDesugaredLibraryContentD8() throws Exception {
     Assume.assumeTrue(requiresAnyCoreLibDesugaring(parameters));
     CodeInspector inspector = new CodeInspector(buildDesugaredLibrary(parameters.getApiLevel()));
@@ -89,11 +121,16 @@
     ToolHelper.runL8(l8Builder.build(), options -> {});
     CodeInspector codeInspector = new CodeInspector(desugaredLib);
     assertCorrect(codeInspector);
-    assertNoWarningsErrors(diagnosticsHandler);
+    assertOneWarning(diagnosticsHandler);
   }
 
-  private void assertNoWarningsErrors(TestDiagnosticMessagesImpl diagnosticsHandler) {
-    assertTrue(diagnosticsHandler.getWarnings().isEmpty());
+  private void assertOneWarning(TestDiagnosticMessagesImpl diagnosticsHandler) {
+    assertEquals(1, diagnosticsHandler.getWarnings().size());
+    String msg = diagnosticsHandler.getWarnings().get(0).getDiagnosticMessage();
+    assertTrue(
+        msg.contains(
+            "The following library types, prefixed by java., are present both as library and non"
+                + " library classes"));
     assertTrue(diagnosticsHandler.getErrors().isEmpty());
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
index 34faa58..a3832c1 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
@@ -67,6 +67,18 @@
     return property.contains("jdk11");
   }
 
+  public void setDesugaredLibrarySpecificationForTesting(
+      InternalOptions options, LegacyDesugaredLibrarySpecification specification) {
+    try {
+      options.setDesugaredLibrarySpecificationForTesting(
+          specification,
+          ToolHelper.getDesugarJDKLibs(),
+          ToolHelper.getAndroidJar(AndroidApiLevel.R));
+    } catch (IOException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
   // For conversions tests, we need DexRuntimes where classes to convert are present (DexRuntimes
   // above N and O depending if Stream or Time APIs are used), but we need to compile the program
   // with a minAPI below to force the use of conversions.
@@ -89,7 +101,7 @@
 
   protected Path getLibraryFile() {
     return isJDK11DesugaredLibrary()
-        ? ToolHelper.getAndroidJar(AndroidApiLevel.S)
+        ? ToolHelper.getAndroidJar(AndroidApiLevel.R)
         : ToolHelper.getAndroidJar(AndroidApiLevel.P);
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryWarningTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryWarningTest.java
index 0c61a06..e1a8586 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryWarningTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryWarningTest.java
@@ -4,6 +4,9 @@
 
 package com.android.tools.r8.desugar.desugaredlibrary;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.L8Command;
 import com.android.tools.r8.OutputMode;
@@ -65,6 +68,15 @@
           Arrays.asList(FUNCTION_KEEP.split(System.lineSeparator())), Origin.unknown());
     }
     ToolHelper.runL8(l8Builder.build(), options -> {});
-    diagnosticsHandler.assertNoMessages();
+    assertEquals(diagnosticsHandler.getWarnings().size(), 1);
+    diagnosticsHandler.assertNoErrors();
+    assertTrue(
+        diagnosticsHandler
+            .getWarnings()
+            .get(0)
+            .getDiagnosticMessage()
+            .contains(
+                "The following library types, prefixed by java., are present both as library and"
+                    + " non library classes:"));
   }
 }
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/InconsistentPrefixTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/InconsistentPrefixTest.java
deleted file mode 100644
index b1f57e1..0000000
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/InconsistentPrefixTest.java
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.desugar.desugaredlibrary;
-
-import static org.hamcrest.CoreMatchers.containsString;
-
-import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.TestBase;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
-import com.android.tools.r8.jasmin.JasminBuilder;
-import java.nio.file.Path;
-import java.util.HashMap;
-import java.util.Map;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-
-@RunWith(Parameterized.class)
-public class InconsistentPrefixTest extends TestBase {
-
-  @Parameters(name = "{0}")
-  public static TestParametersCollection data() {
-    return getTestParameters().withNoneRuntime().build();
-  }
-
-  public InconsistentPrefixTest(TestParameters parameters) {
-    parameters.assertNoneRuntime();
-  }
-
-  @Test(expected = CompilationFailedException.class)
-  public void testNoInconsistentPrefixes() throws Exception {
-    Map<String, String> x = new HashMap<>();
-    x.put("pkg.sub", "p$.bus");
-    x.put("pkg", "p$");
-
-    JasminBuilder jasminBuilder = new JasminBuilder();
-    jasminBuilder.addClass("pkg/notsub/A");
-    jasminBuilder.addClass("pkg/sub/A");
-    Path inputJar = temp.getRoot().toPath().resolve("input.jar");
-    jasminBuilder.writeJar(inputJar);
-
-    testForD8()
-        .addProgramFiles(inputJar)
-        .addOptionsModification(
-            options ->
-                options.desugaredLibrarySpecification =
-                    LegacyDesugaredLibrarySpecification.withOnlyRewritePrefixForTesting(x, options))
-        .compileWithExpectedDiagnostics(
-            diagnostics -> {
-              diagnostics.assertErrorMessageThatMatches(
-                  containsString("Inconsistent prefix in desugared library"));
-            });
-  }
-}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/J$ExtensionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/J$ExtensionTest.java
index 88f3ca9..371443d 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/J$ExtensionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/J$ExtensionTest.java
@@ -5,15 +5,12 @@
 package com.android.tools.r8.desugar.desugaredlibrary;
 
 import static junit.framework.TestCase.assertTrue;
-import static junit.framework.TestCase.fail;
 
-import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestRuntime;
 import com.android.tools.r8.TestRuntime.CfVm;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.DexVm.Version;
-import com.android.tools.r8.utils.BooleanUtils;
 import java.io.BufferedWriter;
 import java.io.File;
 import java.io.FileWriter;
@@ -31,7 +28,6 @@
 public class J$ExtensionTest extends DesugaredLibraryTestBase {
 
   private final TestParameters parameters;
-  private final boolean shrinkDesugaredLibrary;
 
   private static final String MAIN_CLASS_NAME = "Main";
   private static final String MAIN_CLASS =
@@ -55,14 +51,12 @@
           + "}";
   private static Path[] compiledClasses = new Path[2];
 
-  @Parameters(name = "{1}, shrinkDesugaredLibrary: {0}")
+  @Parameters(name = "{0}")
   public static List<Object[]> data() {
-    return buildParameters(
-        BooleanUtils.values(), getTestParameters().withAllRuntimes().withAllApiLevels().build());
+    return buildParameters(getTestParameters().withAllRuntimes().withAllApiLevels().build());
   }
 
-  public J$ExtensionTest(boolean shrinkDesugaredLibrary, TestParameters parameters) {
-    this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
+  public J$ExtensionTest(TestParameters parameters) {
     this.parameters = parameters;
   }
 
@@ -92,7 +86,6 @@
 
   @Test
   public void testJ$ExtensionNoDesugaring() throws Exception {
-    Assume.assumeFalse(shrinkDesugaredLibrary);
     String stderr;
     if (parameters.isCfRuntime()) {
       stderr =
@@ -110,24 +103,31 @@
               .assertFailure()
               .getStdErr();
     }
-    assertError(stderr);
+    assertError(stderr, false);
   }
 
-  private void assertError(String stderr) {
-    if (parameters.isCfRuntime() && parameters.getRuntime().asCf().getVm() == CfVm.JDK8) {
-      assertTrue(
-          stderr.contains("java.lang.SecurityException: Prohibited package name: java.time"));
-    } else if (parameters.isCfRuntime()) {
-      assertTrue(stderr.contains("java.lang.ClassNotFoundException: java.time.LocalTimeAccess"));
-    } else if (parameters
-        .getRuntime()
-        .asDex()
-        .getVm()
-        .getVersion()
-        .isOlderThanOrEqual(Version.V6_0_1)) {
-      assertTrue(stderr.contains("java.lang.NoClassDefFoundError"));
-    } else if (parameters.getRuntime().asDex().getVm().getVersion() == Version.V7_0_0) {
-      assertTrue(stderr.contains("java.lang.ClassNotFoundException"));
+  private void assertError(String stderr, boolean desugaring) {
+    if (parameters.isCfRuntime()) {
+      if (parameters.getRuntime().asCf().getVm() == CfVm.JDK8) {
+        assertTrue(
+            stderr.contains("java.lang.SecurityException: Prohibited package name: java.time"));
+      } else {
+        assertTrue(stderr.contains("java.lang.ClassNotFoundException: java.time.LocalTimeAccess"));
+      }
+      return;
+    }
+    assert !parameters.isCfRuntime();
+    if (!desugaring) {
+      if (parameters.getDexRuntimeVersion().isOlderThanOrEqual(Version.V6_0_1)) {
+        assertTrue(stderr.contains("java.lang.NoClassDefFoundError"));
+      } else if (parameters.getDexRuntimeVersion() == Version.V7_0_0) {
+        assertTrue(stderr.contains("java.lang.ClassNotFoundException"));
+      }
+      return;
+    }
+    if (parameters.getDexRuntimeVersion() == Version.V8_1_0) {
+      // On Android 8 the library package private method is accessible.
+      assertTrue(stderr.contains("java.lang.NullPointerException"));
     } else {
       assertTrue(stderr.contains("java.lang.IllegalAccessError"));
     }
@@ -140,19 +140,18 @@
     Assume.assumeTrue(requiresTimeDesugaring(parameters));
     KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
 
-    try {
-      testForD8()
-          .addLibraryFiles(getLibraryFile())
-          .addProgramFiles(compiledClasses)
-          .setMinApi(parameters.getApiLevel())
-          .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
-          .compile();
-      fail();
-    } catch (CompilationFailedException e) {
-      assertTrue(
-          e.getCause()
-              .getMessage()
-              .contains("Cannot compile program class java.time.LocalTimeAccess"));
-    }
+    String stdErr =
+        testForD8()
+            .addLibraryFiles(getLibraryFile())
+            .addProgramFiles(compiledClasses)
+            .setMinApi(parameters.getApiLevel())
+            .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+            .compile()
+            .addDesugaredCoreLibraryRunClassPath(
+                this::buildDesugaredLibrary, parameters.getApiLevel())
+            .run(parameters.getRuntime(), MAIN_CLASS_NAME)
+            .assertFailure()
+            .getStdErr();
+    assertError(stdErr, true);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java
index 6598d6b..d78df31 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java
@@ -144,16 +144,22 @@
             .map(TypeSubject::toString)
             .collect(Collectors.toSet());
     assertEquals(expectedCatchGuards, foundCatchGuards);
-
-    if (isR8) {
-      assertThat(
-          inspector.clazz(TemporalAccessorImpl.class).uniqueMethodWithFinalName("query"),
-          not(isPresent()));
-    } else {
+    if (!(parameters
+            .getApiLevel()
+            .isGreaterThanOrEqualTo(TestBase.apiLevelWithDefaultInterfaceMethodsSupport())
+        && isR8)) {
       assertThat(
           inspector.clazz(TemporalAccessorImplSub.class).uniqueMethodWithFinalName("query"),
           CodeMatchers.invokesMethod(
-              null, TemporalAccessorImpl.class.getTypeName(), "query", null));
+              null, inspector.clazz(TemporalAccessorImpl.class).getFinalName(), "query", null));
+    } else {
+      String holder =
+          requiresTimeDesugaring(parameters)
+              ? "j$.time.temporal.TemporalAccessor"
+              : "java.time.temporal.TemporalAccessor";
+      assertThat(
+          inspector.clazz(TemporalAccessorImplSub.class).uniqueMethodWithFinalName("query"),
+          CodeMatchers.invokesMethod(null, holder, "query", null));
     }
     if (parameters
         .getApiLevel()
@@ -163,9 +169,7 @@
           not(isPresent()));
     } else {
       assertThat(
-          inspector
-              .clazz(isR8 ? TemporalAccessorImplSub.class : TemporalAccessorImpl.class)
-              .uniqueMethodWithFinalName("query"),
+          inspector.clazz(TemporalAccessorImpl.class).uniqueMethodWithFinalName("query"),
           CodeMatchers.invokesMethod(
               null, "j$.time.temporal.TemporalAccessor$-CC", "$default$query", null));
     }
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LinkedHashSetTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LinkedHashSetTest.java
index 981edea..de87214 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LinkedHashSetTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LinkedHashSetTest.java
@@ -11,6 +11,7 @@
 import java.util.Set;
 import java.util.Spliterator;
 import java.util.Spliterators;
+import java.util.function.Predicate;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -165,6 +166,14 @@
       System.out.println("true");
       System.out.println(spliterator.hasCharacteristics(Spliterator.IMMUTABLE));
       System.out.println("false");
+
+      spliterator = ((Set<String>) new CustomLinkedHashSetForceForwarding<String>()).spliterator();
+      System.out.println(spliterator.hasCharacteristics(Spliterator.DISTINCT));
+      System.out.println("true");
+      System.out.println(spliterator.hasCharacteristics(Spliterator.ORDERED));
+      System.out.println("true");
+      System.out.println(spliterator.hasCharacteristics(Spliterator.IMMUTABLE));
+      System.out.println("false");
     }
   }
 
@@ -198,4 +207,13 @@
       return super.spliterator();
     }
   }
+
+  // The over method force the forwarding methods to be installed.
+  static class CustomLinkedHashSetForceForwarding<E> extends LinkedHashSet<E> {
+
+    @Override
+    public boolean removeIf(Predicate<? super E> filter) {
+      return super.removeIf(filter);
+    }
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MergingJ$Test.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MergingJ$Test.java
index d5300ad..1208414 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MergingJ$Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MergingJ$Test.java
@@ -118,8 +118,8 @@
     L8.run(
         L8Command.builder()
             .addLibraryFiles(getLibraryFile())
+            .addLibraryFiles(ToolHelper.getDesugarJDKLibs())
             .addProgramFiles(JDK_11_JAVA_BASE_EXTENSION_COMPILED_FILES)
-            .addClasspathFiles(ToolHelper.getDesugarJDKLibs())
             .addDesugaredLibraryConfiguration(
                 StringResource.fromFile(ToolHelper.getDesugarLibJsonForTesting()))
             .setMinApiLevel(AndroidApiLevel.B.getLevel())
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ObjectsTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ObjectsTest.java
index abdf5c5..f3caf57 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ObjectsTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ObjectsTest.java
@@ -99,13 +99,13 @@
   }
 
   private void configurationForProgramCompilation(InternalOptions options) {
-    options.desugaredLibrarySpecification =
-        desugaredLibrarySpecification(options, false, parameters);
+    setDesugaredLibrarySpecificationForTesting(
+        options, desugaredLibrarySpecification(options, false, parameters));
   }
 
   private void configurationForLibraryCompilation(InternalOptions options) {
-    options.desugaredLibrarySpecification =
-        desugaredLibrarySpecification(options, true, parameters);
+    setDesugaredLibrarySpecificationForTesting(
+        options, desugaredLibrarySpecification(options, true, parameters));
   }
 
   private Matcher<MethodSubject> invokesObjectsCompare(String holder) {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetAndBackportTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetAndBackportTest.java
index 359a18c..9b1804e 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetAndBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetAndBackportTest.java
@@ -7,6 +7,7 @@
 import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyRewritingFlags;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyTopLevelFlags;
@@ -14,6 +15,7 @@
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import java.io.IOException;
 import java.util.List;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -55,9 +57,15 @@
             .putBackportCoreLibraryMember("java.lang.DesugarMath", "java.lang.Math")
             .putRetargetCoreLibMember("java.util.Date#toInstant", "java.util.DesugarDate")
             .build();
-    options.desugaredLibrarySpecification =
-        new LegacyDesugaredLibrarySpecification(
-            LegacyTopLevelFlags.testing(), rewritingFlags, true, options.itemFactory);
+    try {
+      options.setDesugaredLibrarySpecificationForTesting(
+          new LegacyDesugaredLibrarySpecification(
+              LegacyTopLevelFlags.testing(), rewritingFlags, true),
+          ToolHelper.getDesugarJDKLibs(),
+          ToolHelper.getAndroidJar(AndroidApiLevel.R));
+    } catch (IOException e) {
+      throw new RuntimeException(e);
+    }
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetOverrideTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetOverrideTest.java
index 6bf49a0..e629c47 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetOverrideTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetOverrideTest.java
@@ -5,13 +5,7 @@
 package com.android.tools.r8.desugar.desugaredlibrary;
 
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecification;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion.HumanToMachineSpecificationConverter;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion.LegacyToHumanSpecificationConverter;
 import com.android.tools.r8.utils.BooleanUtils;
-import com.android.tools.r8.utils.InternalOptions;
-import java.io.IOException;
 import java.nio.file.Path;
 import java.time.Instant;
 import java.time.ZonedDateTime;
@@ -31,19 +25,15 @@
 
   private final TestParameters parameters;
   private final boolean shrinkDesugaredLibrary;
-  private final boolean machineSpec;
 
   @Parameters(name = "machine: {0}, {2}, shrink: {1}")
   public static List<Object[]> data() {
     return buildParameters(
         BooleanUtils.values(),
-        BooleanUtils.values(),
         getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build());
   }
 
-  public RetargetOverrideTest(
-      boolean machineSpec, boolean shrinkDesugaredLibrary, TestParameters parameters) {
-    this.machineSpec = machineSpec;
+  public RetargetOverrideTest(boolean shrinkDesugaredLibrary, TestParameters parameters) {
     this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
     this.parameters = parameters;
   }
@@ -54,7 +44,6 @@
     Path desugaredTwice =
         testForD8(Backend.CF)
             .addLibraryFiles(getLibraryFile())
-            .addOptionsModification(this::setMachineSpec)
             .addProgramFiles(
                 testForD8(Backend.CF)
                     .addLibraryFiles(getLibraryFile())
@@ -76,7 +65,6 @@
       stdout =
           testForD8(Backend.DEX)
               .addProgramFiles(desugaredTwice)
-              .addOptionsModification(this::setMachineSpec)
               .setMinApi(parameters.getApiLevel())
               .disableDesugaring()
               .compile()
@@ -107,22 +95,6 @@
     assertLines2By2Correct(stdout);
   }
 
-  private void setMachineSpec(InternalOptions opt) {
-    if (!machineSpec) {
-      return;
-    }
-    try {
-      HumanDesugaredLibrarySpecification human =
-          new LegacyToHumanSpecificationConverter()
-              .convert(opt.desugaredLibrarySpecification, getLibraryFile(), opt);
-      MachineDesugaredLibrarySpecification machine =
-          new HumanToMachineSpecificationConverter().convert(human, getLibraryFile(), opt);
-      opt.testing.machineDesugaredLibrarySpecification = machine;
-    } catch (IOException e) {
-      throw new RuntimeException(e);
-    }
-  }
-
   @Test
   public void testRetargetOverrideD8() throws Exception {
     Assume.assumeTrue(parameters.getRuntime().isDex());
@@ -130,7 +102,6 @@
     String stdout =
         testForD8()
             .addLibraryFiles(getLibraryFile())
-            .addOptionsModification(this::setMachineSpec)
             .addInnerClasses(RetargetOverrideTest.class)
             .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
             .setMinApi(parameters.getApiLevel())
@@ -156,7 +127,6 @@
     String stdout =
         testForR8(Backend.DEX)
             .addLibraryFiles(getLibraryFile())
-            .addOptionsModification(this::setMachineSpec)
             .addKeepMainRule(Executor.class)
             .addInnerClasses(RetargetOverrideTest.class)
             .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionFinalClassTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionFinalClassTest.java
index f9d1828..7abed46 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionFinalClassTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/APIConversionFinalClassTest.java
@@ -58,6 +58,7 @@
         .setMinApi(AndroidApiLevel.B)
         .addProgramClasses(Executor.class)
         .addLibraryClasses(CustomLibClass.class)
+        .addLibraryFiles(getLibraryFile())
         .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
         .compile()
         .addDesugaredCoreLibraryRunClassPath(
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/AccessModeConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/AccessModeConversionTest.java
index 6691cb1..a57140b 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/AccessModeConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/AccessModeConversionTest.java
@@ -66,8 +66,8 @@
             .build();
     LegacyDesugaredLibrarySpecification specification =
         new LegacyDesugaredLibrarySpecification(
-            LegacyTopLevelFlags.testing(), rewritingFlags, l8Compilation, options.itemFactory);
-    options.desugaredLibrarySpecification = specification;
+            LegacyTopLevelFlags.testing(), rewritingFlags, l8Compilation);
+    setDesugaredLibrarySpecificationForTesting(options, specification);
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionIntroduceInterfaceMethodTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionIntroduceInterfaceMethodTest.java
index aa97e55..957515a 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionIntroduceInterfaceMethodTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionIntroduceInterfaceMethodTest.java
@@ -87,9 +87,10 @@
         .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
         .addOptionsModification(
             opt ->
-                opt.desugaredLibrarySpecification =
+                setDesugaredLibrarySpecificationForTesting(
+                    opt,
                     configurationWithSupportAllCallbacksFromLibrary(
-                        opt, false, parameters, supportAllCallbacksFromLibrary))
+                        opt, false, parameters, supportAllCallbacksFromLibrary)))
         .compile()
         .inspect(this::assertDoubleForEach)
         .inspect(this::assertWrapperMethodsPresent)
@@ -151,9 +152,10 @@
         .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
         .addOptionsModification(
             opt ->
-                opt.desugaredLibrarySpecification =
+                setDesugaredLibrarySpecificationForTesting(
+                    opt,
                     configurationWithSupportAllCallbacksFromLibrary(
-                        opt, false, parameters, supportAllCallbacksFromLibrary))
+                        opt, false, parameters, supportAllCallbacksFromLibrary)))
         .compile()
         .inspect(this::assertDoubleForEach)
         .inspect(this::assertWrapperMethodsPresent)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/DuplicateAPIDesugaredLibTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/DuplicateAPIDesugaredLibTest.java
index 6b7d86e..b966b00 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/DuplicateAPIDesugaredLibTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/DuplicateAPIDesugaredLibTest.java
@@ -71,9 +71,10 @@
                         this.buildDesugaredLibrary(
                             api,
                             opt ->
-                                opt.desugaredLibrarySpecification =
+                                setDesugaredLibrarySpecificationForTesting(
+                                    opt,
                                     configurationWithSupportAllCallbacksFromLibrary(
-                                        opt, true, parameters, supportAllCallbacksFromLibrary)));
+                                        opt, true, parameters, supportAllCallbacksFromLibrary))));
                     return desugaredLibBox.get();
                   },
                   AndroidApiLevel.B)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/TryCatchTimeConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/TryCatchTimeConversionTest.java
index a669527..aaab5cb 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/TryCatchTimeConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/TryCatchTimeConversionTest.java
@@ -58,6 +58,7 @@
         .setMinApi(parameters.getApiLevel())
         .addProgramClasses(BaselineExecutor.class)
         .addLibraryClasses(CustomLibClass.class)
+        .addLibraryFiles(getLibraryFile())
         .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
         .compile()
         .addDesugaredCoreLibraryRunClassPath(
@@ -98,6 +99,7 @@
         .setMinApi(parameters.getApiLevel())
         .addProgramClasses(TryCatchExecutor.class)
         .addLibraryClasses(CustomLibClass.class)
+        .addLibraryFiles(getLibraryFile())
         .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
         .compile()
         .addDesugaredCoreLibraryRunClassPath(
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11StreamTests.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11StreamTests.java
index 2bd50b2..3b6ae0a 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11StreamTests.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11StreamTests.java
@@ -270,6 +270,7 @@
               .addProgramFiles(testNGSupportProgramFiles())
               .addOptionsModification(opt -> opt.testing.trackDesugaredAPIConversions = true)
               .addLibraryFiles(getLibraryFile())
+              .addLibraryFiles(JDK_11_JAVA_BASE_EXTENSION_CLASSES_DIR)
               .setMinApi(parameters.getApiLevel())
               .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
               .allowStdoutMessages()
@@ -303,6 +304,7 @@
           .addProgramFiles(testNGSupportProgramFiles())
           .addOptionsModification(opt -> opt.testing.trackDesugaredAPIConversions = true)
           .addLibraryFiles(getLibraryFile())
+          .addLibraryFiles(JDK_11_JAVA_BASE_EXTENSION_CLASSES_DIR)
           .setMinApi(parameters.getApiLevel())
           .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
           .compile()
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/ArgumentInfoCollectionTest.java b/src/test/java/com/android/tools/r8/enumunboxing/ArgumentInfoCollectionTest.java
index 2b5ccbd..faf304f 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/ArgumentInfoCollectionTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/ArgumentInfoCollectionTest.java
@@ -9,15 +9,30 @@
 import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.RemovedArgumentInfo;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.RewrittenTypeInfo;
+import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
+import com.android.tools.r8.graph.proto.RemovedArgumentInfo;
+import com.android.tools.r8.graph.proto.RewrittenTypeInfo;
 import com.android.tools.r8.ir.analysis.value.AbstractValueFactory;
 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 ArgumentInfoCollectionTest extends TestBase {
 
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
   @Test
   public void testCombineRewritten() {
     DexItemFactory factory = new DexItemFactory();
@@ -34,7 +49,7 @@
             .setOldType(factory.intType)
             .setNewType(factory.longType)
             .build());
-    ArgumentInfoCollection arguments1 = builder1.build();
+    ArgumentInfoCollection arguments1 = builder1.setArgumentInfosSize(5).build();
 
     ArgumentInfoCollection.Builder builder2 = ArgumentInfoCollection.builder();
     builder2.addArgumentInfo(
@@ -49,9 +64,10 @@
             .setOldType(factory.floatType)
             .setNewType(factory.doubleType)
             .build());
-    ArgumentInfoCollection arguments2 = builder2.build();
+    ArgumentInfoCollection arguments2 = builder2.setArgumentInfosSize(5).build();
 
     ArgumentInfoCollection combine = arguments1.combine(arguments2);
+    assertEquals(5, combine.size());
 
     RewrittenTypeInfo arg1 = combine.getArgumentInfo(1).asRewrittenTypeInfo();
     assertEquals(arg1.getOldType(), factory.intType);
@@ -70,7 +86,6 @@
   @Test
   public void testCombineRemoved() {
     DexItemFactory factory = new DexItemFactory();
-    AbstractValueFactory abstractValueFactory = new AbstractValueFactory();
 
     // Arguments removed: 0 1 2 3 4 -> 0 2 4.
     ArgumentInfoCollection.Builder builder1 = ArgumentInfoCollection.builder();
@@ -78,34 +93,33 @@
         1,
         RemovedArgumentInfo.builder()
             .setType(factory.intType)
-            .setSingleValue(abstractValueFactory.createNullValue())
             .build());
     builder1.addArgumentInfo(
         3,
         RemovedArgumentInfo.builder()
             .setType(factory.intType)
-            .setSingleValue(abstractValueFactory.createNullValue())
             .build());
-    ArgumentInfoCollection arguments1 = builder1.build();
+    ArgumentInfoCollection arguments1 = builder1.setArgumentInfosSize(5).build();
 
     // Arguments removed: 0 2 4 -> 0. Arguments 2 and 4 are at position 1 and 2 after first removal.
     ArgumentInfoCollection.Builder builder2 = ArgumentInfoCollection.builder();
     builder2.addArgumentInfo(1, RemovedArgumentInfo.builder().setType(factory.doubleType).build());
     builder2.addArgumentInfo(2, RemovedArgumentInfo.builder().setType(factory.doubleType).build());
-    ArgumentInfoCollection arguments2 = builder2.build();
+    ArgumentInfoCollection arguments2 = builder2.setArgumentInfosSize(3).build();
 
     // Arguments removed: 0 1 2 3 4 -> 0.
     ArgumentInfoCollection combine = arguments1.combine(arguments2);
+    assertEquals(5, combine.size());
 
     RemovedArgumentInfo arg1 = combine.getArgumentInfo(1).asRemovedArgumentInfo();
     assertEquals(arg1.getType(), factory.intType);
-    assertTrue(arg1.hasSingleValue());
+    assertFalse(arg1.hasSingleValue());
     RemovedArgumentInfo arg2 = combine.getArgumentInfo(2).asRemovedArgumentInfo();
     assertEquals(arg2.getType(), factory.doubleType);
     assertFalse(arg2.hasSingleValue());
     RemovedArgumentInfo arg3 = combine.getArgumentInfo(3).asRemovedArgumentInfo();
     assertEquals(arg3.getType(), factory.intType);
-    assertTrue(arg3.hasSingleValue());
+    assertFalse(arg3.hasSingleValue());
     RemovedArgumentInfo arg4 = combine.getArgumentInfo(4).asRemovedArgumentInfo();
     assertEquals(arg4.getType(), factory.doubleType);
     assertFalse(arg4.hasSingleValue());
@@ -129,7 +143,7 @@
             .setType(factory.intType)
             .setSingleValue(abstractValueFactory.createNullValue())
             .build());
-    ArgumentInfoCollection arguments1 = builder1.build();
+    ArgumentInfoCollection arguments1 = builder1.setArgumentInfosSize(5).build();
 
     ArgumentInfoCollection.Builder builder2 = ArgumentInfoCollection.builder();
     builder2.addArgumentInfo(
@@ -144,9 +158,10 @@
             .setOldType(factory.floatType)
             .setNewType(factory.doubleType)
             .build());
-    ArgumentInfoCollection arguments2 = builder2.build();
+    ArgumentInfoCollection arguments2 = builder2.setArgumentInfosSize(3).build();
 
     ArgumentInfoCollection combine = arguments1.combine(arguments2);
+    assertEquals(5, combine.size());
 
     RemovedArgumentInfo arg1 = combine.getArgumentInfo(1).asRemovedArgumentInfo();
     assertEquals(arg1.getType(), factory.intType);
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/type/NarrowingWithoutSubtypingTest.java b/src/test/java/com/android/tools/r8/ir/analysis/type/NarrowingWithoutSubtypingTest.java
index 403372c..b78279e 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/type/NarrowingWithoutSubtypingTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/type/NarrowingWithoutSubtypingTest.java
@@ -4,12 +4,6 @@
 
 package com.android.tools.r8.ir.analysis.type;
 
-import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
-import static org.hamcrest.CoreMatchers.containsString;
-import static org.junit.Assert.assertThrows;
-
-import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.D8TestBuilder;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.utils.BooleanUtils;
@@ -38,32 +32,17 @@
 
   @Test
   public void test() throws Exception {
-    D8TestBuilder d8TestBuilder =
-        testForD8()
-            .addInnerClasses(NarrowingWithoutSubtypingTest.class)
-            .addOptionsModification(
-                options -> {
-                  options.testing.readInputStackMaps = readStackMap;
-                  options.testing.enableNarrowAndWideningingChecksInD8 = true;
-                  options.testing.noLocalsTableOnInput = true;
-                })
-            .setMinApi(parameters.getApiLevel());
-    if (readStackMap) {
-      d8TestBuilder
-          .run(parameters.getRuntime(), TestClass.class)
-          .assertSuccessWithOutputLines("Hello world!");
-    } else {
-      // TODO(b/169120386): We should not be narrowing in D8.
-      assertThrows(
-          CompilationFailedException.class,
-          () -> {
-            d8TestBuilder.compileWithExpectedDiagnostics(
-                diagnostics ->
-                    diagnostics.assertAllErrorsMatch(
-                        diagnosticMessage(
-                            containsString("java.lang.AssertionError: During NARROWING"))));
-          });
-    }
+    testForD8()
+        .addInnerClasses(NarrowingWithoutSubtypingTest.class)
+        .addOptionsModification(
+            options -> {
+              options.testing.readInputStackMaps = readStackMap;
+              options.testing.enableNarrowAndWideningingChecksInD8 = true;
+              options.testing.noLocalsTableOnInput = true;
+            })
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutputLines("Hello world!");
   }
 
   static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/backports/AtomicReferenceArrayMethods.java b/src/test/java/com/android/tools/r8/ir/desugar/backports/AtomicReferenceArrayMethods.java
new file mode 100644
index 0000000..9e8522c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/desugar/backports/AtomicReferenceArrayMethods.java
@@ -0,0 +1,20 @@
+// 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.util.concurrent.atomic.AtomicReferenceArray;
+
+public final class AtomicReferenceArrayMethods {
+  // Workaround Android S issue with AtomicReferenceArray.compareAndSet (b/211646483).
+  public static boolean compareAndSet(
+      AtomicReferenceArray<Object> reference, int index, Object expect, Object update) {
+    do {
+      if (reference.compareAndSet(index, expect, update)) {
+        return true;
+      }
+    } while (reference.get(index) == expect);
+    return false;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/backports/AtomicReferenceMethods.java b/src/test/java/com/android/tools/r8/ir/desugar/backports/AtomicReferenceMethods.java
new file mode 100644
index 0000000..e88ee96
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/desugar/backports/AtomicReferenceMethods.java
@@ -0,0 +1,20 @@
+// 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.util.concurrent.atomic.AtomicReference;
+
+public final class AtomicReferenceMethods {
+  // Workaround Android S issue with AtomicReference.compareAndSet (b/211646483).
+  public static boolean compareAndSet(
+      AtomicReference<Object> reference, Object expect, Object update) {
+    do {
+      if (reference.compareAndSet(expect, update)) {
+        return true;
+      }
+    } while (reference.get() == expect);
+    return false;
+  }
+}
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 c6e3eea..7ee6cfa 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
@@ -34,7 +34,9 @@
       factory.createType("Lcom/android/tools/r8/ir/desugar/backports/BackportedMethods;");
   private final List<Class<?>> METHOD_TEMPLATE_CLASSES =
       ImmutableList.of(
+          AtomicReferenceArrayMethods.class,
           AtomicReferenceFieldUpdaterMethods.class,
+          AtomicReferenceMethods.class,
           BooleanMethods.class,
           ByteMethods.class,
           CharSequenceMethods.class,
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 bded17f..b7a3474 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
@@ -328,7 +328,7 @@
 
     m =
         clazz.method(
-            "int",
+            allowAccessModification ? "void" : "int",
             "notInlinable",
             ImmutableList.of("inlining." + (allowAccessModification ? "B" : "A")));
     assertCounters(INLINABLE, NEVER_INLINABLE, countInvokes(inspector, m));
@@ -344,12 +344,12 @@
 
     m =
         clazz.method(
-            "int", "notInlinableOnThrow", ImmutableList.of("java.lang.IllegalArgumentException"));
+            "void", "notInlinableOnThrow", ImmutableList.of("java.lang.IllegalArgumentException"));
     assertCounters(ALWAYS_INLINABLE, NEVER_INLINABLE, countInvokes(inspector, m));
 
     m =
         clazz.method(
-            "int",
+            "void",
             "notInlinableDueToMissingNpeBeforeThrow",
             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/staticizer/ClassStaticizerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java
index ad9ca05..2254355 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java
@@ -197,11 +197,9 @@
             "STATIC: String TrivialTestClass.next()"),
         references(clazz, "testSimpleWithGetter", "void"));
 
-    // TODO(b/216254482): This can be optimized by pruning (always) simple caller inlined methods
-    //  after the primary optimization pass.
     ClassSubject simpleWithGetter = inspector.clazz(SimpleWithGetter.class);
-    assertEquals(1, instanceMethods(simpleWithGetter).size());
-    assertThat(simpleWithGetter.clinit(), isPresent());
+    assertEquals(0, instanceMethods(simpleWithGetter).size());
+    assertThat(simpleWithGetter.clinit(), isAbsent());
 
     assertEquals(
         Lists.newArrayList(
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/InvokeStaticWithNullOutvalueTest.java b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/InvokeStaticWithNullOutvalueTest.java
index 6750562..9dfcb42 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/InvokeStaticWithNullOutvalueTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/InvokeStaticWithNullOutvalueTest.java
@@ -8,6 +8,7 @@
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertTrue;
 
+import com.android.tools.r8.KeepUnusedReturnValue;
 import com.android.tools.r8.NeverClassInline;
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.NeverPropagateValue;
@@ -46,6 +47,7 @@
         .addInnerClasses(InvokeStaticWithNullOutvalueTest.class)
         .addKeepMainRule(MAIN)
         .enableInliningAnnotations()
+        .enableKeepUnusedReturnValueAnnotations()
         .enableMemberValuePropagationAnnotations()
         .enableNeverClassInliningAnnotations()
         .enableNoHorizontalClassMergingAnnotations()
@@ -85,6 +87,7 @@
 
     @NoHorizontalClassMerging
     static class Companion {
+      @KeepUnusedReturnValue
       @NeverInline
       @NeverPropagateValue
       private static Object boo() {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/VoidReturnTypeRewritingTest.java b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/VoidReturnTypeRewritingTest.java
index f8dd8d9..16c8f61 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/VoidReturnTypeRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/VoidReturnTypeRewritingTest.java
@@ -45,7 +45,12 @@
             "SubFactory.createVirtual() -> null",
             "SubSubFactory.createVirtual() -> null");
 
-    testForJvm().addTestClasspath().run(TestClass.class).assertSuccessWithOutput(expected);
+    if (parameters.isCfRuntime()) {
+      testForJvm()
+          .addTestClasspath()
+          .run(parameters.getRuntime(), TestClass.class)
+          .assertSuccessWithOutput(expected);
+    }
 
     CodeInspector inspector =
         testForR8(parameters.getBackend())
@@ -55,8 +60,8 @@
             .enableNoMethodStaticizingAnnotations()
             .enableNoVerticalClassMergingAnnotations()
             .enableNoHorizontalClassMergingAnnotations()
-            .addKeepRules("-dontobfuscate")
             .addOptionsModification(options -> options.enableClassInlining = false)
+            .noMinification()
             .setMinApi(parameters.getApiLevel())
             .run(parameters.getRuntime(), TestClass.class)
             .assertSuccessWithOutput(expected)
@@ -66,16 +71,16 @@
     MethodSubject createStaticMethodSubject =
         factoryClassSubject.uniqueMethodWithName("createStatic");
     assertThat(createStaticMethodSubject, isPresent());
-    assertTrue(createStaticMethodSubject.getMethod().getReference().proto.returnType.isVoidType());
+    assertTrue(createStaticMethodSubject.getMethod().getReturnType().isVoidType());
     MethodSubject createVirtualMethodSubject =
         factoryClassSubject.uniqueMethodWithName("createVirtual");
     assertThat(createVirtualMethodSubject, isPresent());
-    assertTrue(createVirtualMethodSubject.getMethod().getReference().proto.returnType.isVoidType());
+    assertTrue(createVirtualMethodSubject.getMethod().getReturnType().isVoidType());
 
     createVirtualMethodSubject =
         inspector.clazz(SubFactory.class).uniqueMethodWithName("createVirtual");
     assertThat(createVirtualMethodSubject, isPresent());
-    assertTrue(createVirtualMethodSubject.getMethod().getReference().proto.returnType.isVoidType());
+    assertTrue(createVirtualMethodSubject.getMethod().getReturnType().isVoidType());
 
     ClassSubject subSubFactoryClassSubject = inspector.clazz(SubSubFactory.class);
     assertThat(subSubFactoryClassSubject.method("void", "createVirtual"), isPresent());
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentRemovalWithOverridingTest.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentRemovalWithOverridingTest.java
index 58b052c..44c15e6 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentRemovalWithOverridingTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentRemovalWithOverridingTest.java
@@ -83,7 +83,7 @@
 
     @NeverInline
     public String greeting(String used) {
-      return used;
+      return System.currentTimeMillis() >= 0 ? used : null;
     }
   }
 
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/b135627418/B135627418.java b/src/test/java/com/android/tools/r8/memberrebinding/b135627418/B135627418.java
index f3103f1..bf94752 100644
--- a/src/test/java/com/android/tools/r8/memberrebinding/b135627418/B135627418.java
+++ b/src/test/java/com/android/tools/r8/memberrebinding/b135627418/B135627418.java
@@ -12,10 +12,13 @@
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
 import com.android.tools.r8.memberrebinding.b135627418.library.Drawable;
 import com.android.tools.r8.memberrebinding.b135627418.library.DrawableWrapper;
 import com.android.tools.r8.memberrebinding.b135627418.library.InsetDrawable;
+import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.InstructionSubject;
@@ -75,11 +78,27 @@
                 com.android.tools.r8.memberrebinding.b135627418.runtime.InsetDrawable.class)
             .setMinApi(parameters.getRuntime())
             .addOptionsModification(
-                options ->
-                    options.desugaredLibrarySpecification =
-                        LegacyDesugaredLibrarySpecification.withOnlyRewritePrefixForTesting(
-                            ImmutableMap.of(packageName + ".runtime", packageName + ".library"),
-                            options))
+                options -> {
+                  options.desugaredLibrarySpecification =
+                      LegacyDesugaredLibrarySpecification.withOnlyRewritePrefixForTesting(
+                          ImmutableMap.of(packageName + ".runtime", packageName + ".library"),
+                          options);
+                  DexType type =
+                      options
+                          .dexItemFactory()
+                          .createType(
+                              DescriptorUtils.javaTypeToDescriptor(
+                                  packageName + ".runtime.InsetDrawable"));
+                  DexType rewrittenType =
+                      options
+                          .dexItemFactory()
+                          .createType(
+                              DescriptorUtils.javaTypeToDescriptor(
+                                  packageName + ".library.InsetDrawable"));
+                  options.machineDesugaredLibrarySpecification =
+                      MachineDesugaredLibrarySpecification.withOnlyRewriteTypeForTesting(
+                          ImmutableMap.of(type, rewrittenType));
+                })
             .compile();
 
     testForR8(parameters.getBackend())
diff --git a/src/test/java/com/android/tools/r8/naming/b124357885/B124357885Test.java b/src/test/java/com/android/tools/r8/naming/b124357885/B124357885Test.java
index a3ae2e9..7bf4064 100644
--- a/src/test/java/com/android/tools/r8/naming/b124357885/B124357885Test.java
+++ b/src/test/java/com/android/tools/r8/naming/b124357885/B124357885Test.java
@@ -11,6 +11,8 @@
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
 
+import com.android.tools.r8.KeepUnusedReturnValue;
+import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.R8TestCompileResult;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
@@ -53,40 +55,53 @@
   public void test() throws Exception {
     R8TestCompileResult compileResult =
         testForR8Compat(parameters.getBackend())
-            .addProgramClasses(Main.class, Service.class, Foo.class, FooImpl.class)
+            .addProgramClasses(Main.class, Foo.class, FooImpl.class)
             .addKeepMainRule(Main.class)
             .addKeepAttributes(
                 ProguardKeepAttributes.SIGNATURE,
                 ProguardKeepAttributes.INNER_CLASSES,
                 ProguardKeepAttributes.ENCLOSING_METHOD)
+            .applyIf(
+                minification,
+                testBuilder ->
+                    testBuilder.addKeepRules(
+                        "-keep,allowoptimization,allowshrinking class "
+                            + Main.class.getTypeName()
+                            + " { *** test(); }"))
+            .enableInliningAnnotations()
+            .enableKeepUnusedReturnValueAnnotations()
             .minification(minification)
             .setMinApi(parameters.getApiLevel())
             .compile()
             .inspect(
                 inspector -> {
-                  assertThat(inspector.clazz(Main.class), isPresentAndNotRenamed());
+                  ClassSubject mainClass = inspector.clazz(Main.class);
+                  assertThat(mainClass, isPresentAndNotRenamed());
                   assertThat(inspector.clazz(Foo.class), not(isPresent()));
                   assertThat(inspector.clazz(FooImpl.class), isPresentAndRenamed(minification));
-                  ClassSubject serviceClass = inspector.clazz(Service.class);
-                  assertThat(serviceClass, isPresentAndRenamed(minification));
-                  // TODO(124477502): Using uniqueMethodWithName("fooList") does not work.
-                  assertEquals(1, serviceClass.allMethods().size());
-                  MethodSubject fooList = serviceClass.allMethods().get(0);
-                  assertThat(fooList, isPresent());
+                  // TODO(124477502): Using uniqueMethodWithName("test") does not work.
+                  MethodSubject testMethod =
+                      mainClass.uniqueMethodThatMatches(
+                          method ->
+                              method.isStatic()
+                                  && !method.getMethod().getName().toString().equals("main"));
+                  assertThat(testMethod, isPresent());
                   checkSignature(
-                      inspector, fooList.asFoundMethodSubject().getFinalSignatureAttribute());
+                      inspector, testMethod.asFoundMethodSubject().getFinalSignatureAttribute());
                 });
 
     String fooImplFinalName = compileResult.inspector().clazz(FooImpl.class).getFinalName();
 
     compileResult
         .run(parameters.getRuntime(), Main.class)
-        .assertSuccessWithOutput(StringUtils.lines(fooImplFinalName, fooImplFinalName));
+        .assertSuccessWithOutput(
+            StringUtils.lines(fooImplFinalName, fooImplFinalName, "Hello world!"));
   }
 
   public static class Main {
     public static void main(String... args) throws Exception {
-      Method method = Service.class.getMethod("fooList");
+      String methodName = System.currentTimeMillis() >= 0 ? "test" : null;
+      Method method = Main.class.getDeclaredMethod(methodName);
 
       try {
         ParameterizedType type = (ParameterizedType) method.getGenericReturnType();
@@ -99,11 +114,17 @@
       // Convince R8 we only use subtypes to get class merging of Foo into FooImpl.
       Foo<String> foo = new FooImpl<>();
       System.out.println(foo.getClass().getCanonicalName());
-    }
-  }
 
-  interface Service {
-    Foo<String> fooList();
+      // Ensure test() remains in output.
+      test();
+    }
+
+    @NeverInline
+    @KeepUnusedReturnValue
+    static Foo<String> test() {
+      System.out.println("Hello world!");
+      return new FooImpl<>();
+    }
   }
 
   interface Foo<T> {}
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineeWithNoSuchMethodErrorTest.java b/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineeWithNoSuchMethodErrorTest.java
index 1c2f06d..f3718cd 100644
--- a/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineeWithNoSuchMethodErrorTest.java
+++ b/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineeWithNoSuchMethodErrorTest.java
@@ -7,6 +7,7 @@
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.MatcherAssert.assertThat;
 
+import com.android.tools.r8.KeepUnusedReturnValue;
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.R8FullTestBuilder;
 import com.android.tools.r8.R8TestRunResult;
@@ -78,6 +79,7 @@
             .addKeepAttributeSourceFile()
             .setMinApi(parameters.getApiLevel())
             .enableInliningAnnotations()
+            .enableKeepUnusedReturnValueAnnotations()
             .enableExperimentalMapFileVersion();
     R8TestRunResult runResult;
     if (throwReceiverNpe) {
@@ -112,6 +114,7 @@
       throw new RuntimeException("Will be removed");
     }
 
+    @KeepUnusedReturnValue
     Object inlinable() {
       return foo();
     }
diff --git a/src/test/java/com/android/tools/r8/naming/retraceproguard/VerticalClassMergingRetraceTest.java b/src/test/java/com/android/tools/r8/naming/retraceproguard/VerticalClassMergingRetraceTest.java
index bb37a8a..5403673 100644
--- a/src/test/java/com/android/tools/r8/naming/retraceproguard/VerticalClassMergingRetraceTest.java
+++ b/src/test/java/com/android/tools/r8/naming/retraceproguard/VerticalClassMergingRetraceTest.java
@@ -25,8 +25,6 @@
 import com.google.common.collect.ImmutableList;
 import java.io.IOException;
 import java.util.Collection;
-import java.util.HashSet;
-import java.util.Set;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -34,7 +32,6 @@
 
 @RunWith(Parameterized.class)
 public class VerticalClassMergingRetraceTest extends RetraceTestBase {
-  private Set<StackTraceLine> haveSeenLines = new HashSet<>();
 
   @Parameters(name = "{0}, mode: {1}, compat: {2}")
   public static Collection<Object[]> data() {
@@ -136,7 +133,6 @@
   public void testNoLineNumberTable() throws Exception {
     assumeTrue(compat);
     assumeTrue(parameters.isDexRuntime());
-    haveSeenLines.clear();
     Box<MethodSubject> syntheticMethod = new Box<>();
     runTest(
         ImmutableList.of(),
@@ -171,8 +167,11 @@
   // Will be merged down, and represented as:
   //     java.lang.String ...ResourceWrapper.foo() -> a
   @NeverInline
-  String foo() {
-    throw null;
+  String foo(boolean doThrow) {
+    if (doThrow) {
+      throw null;
+    }
+    return System.currentTimeMillis() > 0 ? "arg" : null;
   }
 }
 
@@ -181,6 +180,7 @@
 class MainApp {
   public static void main(String[] args) {
     TintResources t = new TintResources();
-    System.out.println(t.foo());
+    boolean doThrow = System.currentTimeMillis() > 0;
+    System.out.println(t.foo(doThrow));
   }
 }
diff --git a/src/test/java/com/android/tools/r8/neverreturnsnormally/NeverReturnsNormallyTest.java b/src/test/java/com/android/tools/r8/neverreturnsnormally/NeverReturnsNormallyTest.java
index aedd6a4..173990f 100644
--- a/src/test/java/com/android/tools/r8/neverreturnsnormally/NeverReturnsNormallyTest.java
+++ b/src/test/java/com/android/tools/r8/neverreturnsnormally/NeverReturnsNormallyTest.java
@@ -13,7 +13,6 @@
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.cf.code.CfStackInstruction.Opcode;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.BooleanUtils;
@@ -201,11 +200,7 @@
           insn instanceof DexInstructionSubject && ((DexInstructionSubject) insn).isConst4());
     } else {
       assertTrue(insn instanceof CfInstructionSubject);
-      assertTrue(((CfInstructionSubject) insn).isStackInstruction(Opcode.Pop));
-      assertTrue(instructions.hasNext());
-      insn = instructions.next();
-      assertTrue(insn instanceof CfInstructionSubject);
-      assertTrue(((CfInstructionSubject) insn).isConstNull());
+      assertTrue(insn.isConstNull());
     }
     assertTrue(nextInstruction(instructions).isThrow());
     assertFalse(instructions.hasNext());
diff --git a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/MonomorphicMethodWithUnusedReturnValueTest.java b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/MonomorphicMethodWithUnusedReturnValueTest.java
new file mode 100644
index 0000000..d7130ef
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/MonomorphicMethodWithUnusedReturnValueTest.java
@@ -0,0 +1,73 @@
+// 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.isAbsent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.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 MonomorphicMethodWithUnusedReturnValueTest 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()
+        // TODO(b/173398086): uniqueMethodWithName() does not work with proto changes.
+        .noMinification()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .inspect(
+            inspector -> {
+              // The test() method has been changed to have return type void.
+              MethodSubject testMethodSubject =
+                  inspector.clazz(Main.class).uniqueMethodWithName("test");
+              assertThat(testMethodSubject, isPresent());
+              assertTrue(testMethodSubject.getProgramMethod().getReturnType().isVoidType());
+
+              // The class ReturnType has been removed by tree shaking.
+              assertThat(inspector.clazz(ReturnType.class), isAbsent());
+            })
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("create()");
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      test();
+    }
+
+    @NeverInline
+    static ReturnType test() {
+      System.out.println("create()");
+      return new ReturnType();
+    }
+  }
+
+  static class ReturnType {}
+}
diff --git a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/PolymorphicMethodWithUnusedReturnValueTest.java b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/PolymorphicMethodWithUnusedReturnValueTest.java
new file mode 100644
index 0000000..e8007c3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/PolymorphicMethodWithUnusedReturnValueTest.java
@@ -0,0 +1,91 @@
+// 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.isAbsent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.NoHorizontalClassMerging;
+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.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 PolymorphicMethodWithUnusedReturnValueTest 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)
+        .enableNoHorizontalClassMergingAnnotations()
+        .enableNoVerticalClassMergingAnnotations()
+        // TODO(b/173398086): uniqueMethodWithName() does not work with proto changes.
+        .noMinification()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .inspect(
+            inspector -> {
+              // The test() methods have been changed to have return type void.
+              for (Class<?> clazz : new Class<?>[] {A.class, B.class}) {
+                MethodSubject testMethodSubject = inspector.clazz(clazz).uniqueMethodWithName("m");
+                assertThat(testMethodSubject, isPresent());
+                assertTrue(testMethodSubject.getProgramMethod().getReturnType().isVoidType());
+              }
+
+              // The class ReturnType has been removed by tree shaking.
+              assertThat(inspector.clazz(ReturnType.class), isAbsent());
+            })
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("A.m()");
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      A a = System.currentTimeMillis() >= 0 ? new A() : new B();
+      a.m();
+    }
+  }
+
+  @NoHorizontalClassMerging
+  @NoVerticalClassMerging
+  static class A {
+
+    ReturnType m() {
+      System.out.println("A.m()");
+      return new ReturnType();
+    }
+  }
+
+  static class B extends A {
+
+    @Override
+    ReturnType m() {
+      System.out.println("B.m()");
+      return new ReturnType();
+    }
+  }
+
+  @NoHorizontalClassMerging
+  static class ReturnType {}
+}
diff --git a/src/test/java/com/android/tools/r8/optimize/proto/NormalizeTest.java b/src/test/java/com/android/tools/r8/optimize/proto/NormalizeTest.java
new file mode 100644
index 0000000..c78ced4
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/optimize/proto/NormalizeTest.java
@@ -0,0 +1,106 @@
+// 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.proto;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.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 NormalizeTest 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)
+        .addOptionsModification(
+            options -> options.testing.enableExperimentalProtoNormalization = true)
+        .enableInliningAnnotations()
+        .enableNoHorizontalClassMergingAnnotations()
+        // TODO(b/173398086): uniqueMethodWithName() does not work with proto changes.
+        .noMinification()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .inspect(
+            inspector -> {
+              ClassSubject aClassSubject = inspector.clazz(A.class);
+              assertThat(aClassSubject, isPresent());
+
+              ClassSubject bClassSubject = inspector.clazz(B.class);
+              assertThat(bClassSubject, isPresent());
+
+              MethodSubject fooMethodSubject =
+                  inspector.clazz(Main.class).uniqueMethodWithName("foo");
+              assertThat(fooMethodSubject, isPresent());
+
+              String expectedMethodSignature =
+                  "void "
+                      + fooMethodSubject.getFinalName()
+                      + "("
+                      + aClassSubject.getFinalName()
+                      + ", "
+                      + bClassSubject.getFinalName()
+                      + ")";
+              assertEquals(
+                  expectedMethodSignature,
+                  fooMethodSubject.getProgramMethod().getMethodSignature().toString());
+            })
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("A", "B");
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      foo(new B(), new A());
+    }
+
+    @NeverInline
+    static void foo(B b, A a) {
+      System.out.println(a);
+      System.out.println(b);
+    }
+  }
+
+  @NoHorizontalClassMerging
+  static class A {
+
+    @Override
+    public String toString() {
+      return "A";
+    }
+  }
+
+  @NoHorizontalClassMerging
+  static class B {
+
+    @Override
+    public String toString() {
+      return "B";
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessTest.java
index 3803d1a..898cfae 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessTest.java
@@ -224,8 +224,8 @@
   static class A implements I {
     public void foo() {
       // Rewritten to invoke-special A.bar or I.bar which resolves to private method A.bar
-      // When targeting B.bar => throws NoSuchMethodError.
-      // When targeting A.bar:
+      // When targeting A.bar => throws NoSuchMethodError.
+      // When targeting I.bar:
       //   - in same nest => success.
       //   - not in nest => throws IllegalAccessError.
       bar();
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerAssertionInClinitOnlyTest.java b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerAssertionInClinitOnlyTest.java
new file mode 100644
index 0000000..314d180
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerAssertionInClinitOnlyTest.java
@@ -0,0 +1,38 @@
+// 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.rewrite.assertions;
+
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.rewrite.assertions.assertionhandler.AssertionHandlers;
+import com.android.tools.r8.rewrite.assertions.assertionhandler.AssertionsInClinit;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class AssertionConfigurationAssertionHandlerAssertionInClinitOnlyTest
+    extends AssertionConfigurationAssertionHandlerTestBase {
+
+  private static final String EXPECTED_OUTPUT = StringUtils.lines("assertionHandler: <clinit>");
+
+  @Override
+  String getExpectedOutput() {
+    return EXPECTED_OUTPUT;
+  }
+
+  @Override
+  MethodReference getAssertionHandler() throws Exception {
+    return Reference.methodFromMethod(
+        AssertionHandlers.class.getMethod("assertionHandler", Throwable.class));
+  }
+
+  @Override
+  List<Class<?>> getTestClasses() {
+    return ImmutableList.of(AssertionsInClinit.class);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerKotlinTestBase.java b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerKotlinTestBase.java
new file mode 100644
index 0000000..84bd1ef
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerKotlinTestBase.java
@@ -0,0 +1,153 @@
+// 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.rewrite.assertions;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.KotlinTestBase;
+import com.android.tools.r8.KotlinTestParameters;
+import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.TestCompilerBuilder;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.rewrite.assertions.assertionhandler.AssertionHandlers;
+import com.android.tools.r8.utils.BooleanUtils;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public abstract class AssertionConfigurationAssertionHandlerKotlinTestBase extends KotlinTestBase {
+
+  @Parameterized.Parameters(name = "{0}, {1}, kotlin-stdlib as library: {2}, -Xassertions=jvm: {3}")
+  public static Collection<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withAllRuntimesAndApiLevels().build(),
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
+        BooleanUtils.values(),
+        BooleanUtils.values());
+  }
+
+  protected final TestParameters parameters;
+  protected final boolean kotlinStdlibAsLibrary;
+  protected final boolean useJvmAssertions;
+  protected final KotlinCompileMemoizer compiledForAssertions;
+
+  public AssertionConfigurationAssertionHandlerKotlinTestBase(
+      TestParameters parameters,
+      KotlinTestParameters kotlinParameters,
+      boolean kotlinStdlibAsClasspath,
+      boolean useJvmAssertions)
+      throws IOException {
+    super(kotlinParameters);
+    this.parameters = parameters;
+    this.kotlinStdlibAsLibrary = kotlinStdlibAsClasspath;
+    this.useJvmAssertions = useJvmAssertions;
+    this.compiledForAssertions =
+        useJvmAssertions ? kotlinWithJvmAssertions() : kotlinWithoutJvmAssertions();
+  }
+
+  private KotlinCompileMemoizer kotlinWithJvmAssertions() throws IOException {
+    return getCompileMemoizer(getKotlinFiles())
+        .configure(kotlinCompilerTool -> kotlinCompilerTool.setUseJvmAssertions(true));
+  }
+
+  private KotlinCompileMemoizer kotlinWithoutJvmAssertions() throws IOException {
+    return getCompileMemoizer(getKotlinFiles())
+        .configure(kotlinCompilerTool -> kotlinCompilerTool.setUseJvmAssertions(false));
+  }
+
+  protected abstract String getExpectedOutput();
+
+  protected abstract MethodReference getAssertionHandler() throws Exception;
+
+  protected abstract List<Path> getKotlinFiles() throws IOException;
+
+  protected abstract String getTestClassName();
+
+  protected void configureR8(R8FullTestBuilder builder) {}
+
+  private Path kotlinStdlibLibraryForRuntime() throws Exception {
+    Path kotlinStdlibCf = kotlinc.getKotlinStdlibJar();
+    if (parameters.getRuntime().isCf()) {
+      return kotlinStdlibCf;
+    }
+
+    Path kotlinStdlibDex = temp.newFolder().toPath().resolve("kotlin-stdlib-dex.jar");
+    testForD8()
+        .addProgramFiles(kotlinStdlibCf)
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .writeToZip(kotlinStdlibDex);
+    return kotlinStdlibDex;
+  }
+
+  private MethodReference getAssertionHandlerIgnoreException() {
+    try {
+      return getAssertionHandler();
+    } catch (Exception e) {
+      return null;
+    }
+  }
+
+  private void configureKotlinStdlib(TestCompilerBuilder<?, ?, ?, ?, ?> builder) throws Exception {
+    if (kotlinStdlibAsLibrary) {
+      builder
+          .addClasspathFiles(kotlinc.getKotlinStdlibJar())
+          .addRunClasspathFiles(kotlinStdlibLibraryForRuntime());
+    } else {
+      builder.addProgramFiles(kotlinc.getKotlinStdlibJar());
+    }
+  }
+
+  @Test
+  public void testD8() throws Exception {
+    assumeTrue(parameters.isDexRuntime());
+    testForD8()
+        .apply(this::configureKotlinStdlib)
+        .setMinApi(parameters.getApiLevel())
+        .addProgramClasses(AssertionHandlers.class)
+        .addProgramFiles(compiledForAssertions.getForConfiguration(kotlinc, targetVersion))
+        .addAssertionsConfiguration(
+            builder ->
+                builder
+                    .setAssertionHandler(getAssertionHandlerIgnoreException())
+                    .setScopeAll()
+                    .build())
+        .run(parameters.getRuntime(), getTestClassName())
+        .assertSuccessWithOutput(getExpectedOutput());
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .apply(this::configureKotlinStdlib)
+        .setMinApi(parameters.getApiLevel())
+        .addProgramClasses(AssertionHandlers.class)
+        .addProgramFiles(compiledForAssertions.getForConfiguration(kotlinc, targetVersion))
+        .addAssertionsConfiguration(
+            builder ->
+                builder
+                    .setAssertionHandler(getAssertionHandlerIgnoreException())
+                    .setScopeAll()
+                    .build())
+        .addKeepMainRule(getTestClassName())
+        .apply(this::configureR8)
+        .allowDiagnosticWarningMessages(!kotlinStdlibAsLibrary)
+        .compile()
+        .applyIf(
+            !kotlinStdlibAsLibrary,
+            result ->
+                result.assertAllWarningMessagesMatch(
+                    equalTo("Resource 'META-INF/MANIFEST.MF' already exists.")))
+        .run(parameters.getRuntime(), getTestClassName())
+        .assertSuccessWithOutput(getExpectedOutput());
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerRethrowingTest.java b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerRethrowingTest.java
new file mode 100644
index 0000000..c1172d8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerRethrowingTest.java
@@ -0,0 +1,48 @@
+// 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.rewrite.assertions;
+
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.rewrite.assertions.assertionhandler.AssertionHandlers;
+import com.android.tools.r8.rewrite.assertions.assertionhandler.AssertionsWithExceptionHandlers;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class AssertionConfigurationAssertionHandlerRethrowingTest
+    extends AssertionConfigurationAssertionHandlerTestBase {
+
+  private static final String EXPECTED_OUTPUT =
+      StringUtils.lines(
+          "assertionHandlerRethrowing: First assertion",
+          "assertionHandlerRethrowing: Second assertion",
+          "Caught: Second assertion",
+          "assertionHandlerRethrowing: Third assertion",
+          "Caught: Third assertion",
+          "assertionHandlerRethrowing: Fourth assertion",
+          "Caught from: assertionsWithCatch3",
+          "assertionHandlerRethrowing: Fifth assertion",
+          "Caught from: simpleAssertion");
+
+  @Override
+  String getExpectedOutput() {
+    return EXPECTED_OUTPUT;
+  }
+
+  @Override
+  MethodReference getAssertionHandler() throws Exception {
+    return Reference.methodFromMethod(
+        AssertionHandlers.class.getMethod("assertionHandlerRethrowing", Throwable.class));
+  }
+
+  @Override
+  List<Class<?>> getTestClasses() {
+    return ImmutableList.of(AssertionsWithExceptionHandlers.class);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerSimpleTest.java b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerSimpleTest.java
new file mode 100644
index 0000000..43e0a7e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerSimpleTest.java
@@ -0,0 +1,42 @@
+// 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.rewrite.assertions;
+
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.rewrite.assertions.assertionhandler.AssertionHandlers;
+import com.android.tools.r8.rewrite.assertions.assertionhandler.AssertionsSimple;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class AssertionConfigurationAssertionHandlerSimpleTest
+    extends AssertionConfigurationAssertionHandlerTestBase {
+
+  private static final String EXPECTED_OUTPUT =
+      StringUtils.lines(
+          "assertionHandler: simpleAssertion",
+          "assertionHandler: multipleAssertions",
+          "assertionHandler: multipleAssertions");
+
+  @Override
+  String getExpectedOutput() {
+    return EXPECTED_OUTPUT;
+  }
+
+  @Override
+  MethodReference getAssertionHandler() throws Exception {
+    return Reference.methodFromMethod(
+        AssertionHandlers.class.getMethod("assertionHandler", Throwable.class));
+  }
+
+  @Override
+  List<Class<?>> getTestClasses() {
+    return ImmutableList.of(AssertionsSimple.class);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerTestBase.java b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerTestBase.java
new file mode 100644
index 0000000..fa4de84
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerTestBase.java
@@ -0,0 +1,81 @@
+// 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.rewrite.assertions;
+
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.rewrite.assertions.assertionhandler.AssertionHandlers;
+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 abstract class AssertionConfigurationAssertionHandlerTestBase extends TestBase {
+
+  @Parameter() public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build());
+  }
+
+  abstract String getExpectedOutput();
+
+  abstract MethodReference getAssertionHandler() throws Exception;
+
+  abstract List<Class<?>> getTestClasses();
+
+  private MethodReference getAssertionHandlerIgnoreException() {
+    try {
+      return getAssertionHandler();
+    } catch (Exception e) {
+      return null;
+    }
+  }
+
+  @Test
+  public void testD8() throws Exception {
+    assumeTrue(parameters.isDexRuntime());
+    testForD8(parameters.getBackend())
+        .addProgramClasses(AssertionHandlers.class)
+        .addProgramClasses(getTestClasses())
+        .setMinApi(parameters.getApiLevel())
+        .addOptionsModification(o -> o.testing.forceIRForCfToCfDesugar = true)
+        .addAssertionsConfiguration(
+            builder ->
+                builder
+                    .setAssertionHandler(getAssertionHandlerIgnoreException())
+                    .setScopeAll()
+                    .build())
+        .run(parameters.getRuntime(), getTestClasses().get(0))
+        .assertSuccessWithOutput(getExpectedOutput());
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClasses(AssertionHandlers.class)
+        .addProgramClasses(getTestClasses())
+        .addKeepMainRule(getTestClasses().get(0))
+        .addKeepAnnotation()
+        .addKeepRules("-keep class * { @com.android.tools.r8.Keep *; }")
+        .setMinApi(parameters.getApiLevel())
+        .addAssertionsConfiguration(
+            builder ->
+                builder
+                    .setAssertionHandler(getAssertionHandlerIgnoreException())
+                    .setScopeAll()
+                    .build())
+        .run(parameters.getRuntime(), getTestClasses().get(0))
+        .assertSuccessWithOutput(getExpectedOutput());
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerWithExceptionHandlersTest.java b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerWithExceptionHandlersTest.java
new file mode 100644
index 0000000..94f47bc
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerWithExceptionHandlersTest.java
@@ -0,0 +1,42 @@
+// 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.rewrite.assertions;
+
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.rewrite.assertions.assertionhandler.AssertionHandlers;
+import com.android.tools.r8.rewrite.assertions.assertionhandler.AssertionsWithExceptionHandlers;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class AssertionConfigurationAssertionHandlerWithExceptionHandlersTest
+    extends AssertionConfigurationAssertionHandlerTestBase {
+
+  private static final String EXPECTED_OUTPUT =
+      StringUtils.lines(
+          "assertionHandler: First assertion",
+          "assertionHandler: Second assertion",
+          "assertionHandler: Fourth assertion");
+
+  @Override
+  String getExpectedOutput() {
+    return EXPECTED_OUTPUT;
+  }
+
+  @Override
+  MethodReference getAssertionHandler() throws Exception {
+    return Reference.methodFromMethod(
+        AssertionHandlers.class.getMethod("assertionHandler", Throwable.class));
+  }
+
+  @Override
+  List<Class<?>> getTestClasses() {
+    return ImmutableList.of(AssertionsWithExceptionHandlers.class);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/assertionhandler/AssertionHandlers.java b/src/test/java/com/android/tools/r8/rewrite/assertions/assertionhandler/AssertionHandlers.java
new file mode 100644
index 0000000..bbf856d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/assertionhandler/AssertionHandlers.java
@@ -0,0 +1,28 @@
+// 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.rewrite.assertions.assertionhandler;
+
+public class AssertionHandlers {
+  static String methodWithAssertionError(Throwable assertion) {
+    return assertion.getStackTrace()[0].getMethodName();
+  }
+
+  public static void assertionHandler(Throwable assertion) {
+    System.out.println(
+        "assertionHandler: "
+            + (assertion.getMessage() != null
+                ? assertion.getMessage()
+                : methodWithAssertionError(assertion)));
+  }
+
+  public static void assertionHandlerRethrowing(Throwable assertion) throws Throwable {
+    System.out.println(
+        "assertionHandlerRethrowing: "
+            + (assertion.getMessage() != null
+                ? assertion.getMessage()
+                : methodWithAssertionError(assertion)));
+    throw assertion;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/assertionhandler/AssertionsInClinit.java b/src/test/java/com/android/tools/r8/rewrite/assertions/assertionhandler/AssertionsInClinit.java
new file mode 100644
index 0000000..d077af1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/assertionhandler/AssertionsInClinit.java
@@ -0,0 +1,14 @@
+// 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.rewrite.assertions.assertionhandler;
+
+public class AssertionsInClinit {
+
+  static {
+    assert false;
+  }
+
+  public static void main(String[] args) {}
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/assertionhandler/AssertionsSimple.java b/src/test/java/com/android/tools/r8/rewrite/assertions/assertionhandler/AssertionsSimple.java
new file mode 100644
index 0000000..cb6300d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/assertionhandler/AssertionsSimple.java
@@ -0,0 +1,26 @@
+// 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.rewrite.assertions.assertionhandler;
+
+import com.android.tools.r8.Keep;
+
+public class AssertionsSimple {
+
+  @Keep
+  public static void simpleAssertion() {
+    assert false;
+  }
+
+  @Keep
+  public static void multipleAssertions() {
+    assert false;
+    assert false;
+  }
+
+  public static void main(String[] args) {
+    simpleAssertion();
+    multipleAssertions();
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/assertionhandler/AssertionsWithExceptionHandlers.java b/src/test/java/com/android/tools/r8/rewrite/assertions/assertionhandler/AssertionsWithExceptionHandlers.java
new file mode 100644
index 0000000..e5bdad7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/assertionhandler/AssertionsWithExceptionHandlers.java
@@ -0,0 +1,63 @@
+// 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.rewrite.assertions.assertionhandler;
+
+import com.android.tools.r8.Keep;
+
+public class AssertionsWithExceptionHandlers {
+  @Keep
+  public static void assertionsWithCatch1() {
+    try {
+      assert false : "First assertion";
+    } catch (NoSuchMethodError | NoSuchFieldError e) {
+
+    } catch (NoClassDefFoundError e) {
+
+    }
+  }
+
+  @Keep
+  public static void assertionsWithCatch2() {
+    try {
+      assert false : "Second assertion";
+    } catch (AssertionError e) {
+      System.out.println("Caught: " + e.getMessage());
+      try {
+        assert false : "Third assertion";
+      } catch (AssertionError e2) {
+        System.out.println("Caught: " + e2.getMessage());
+      }
+    }
+  }
+
+  @Keep
+  private static void simpleAssertion() {
+    assert false : "Fifth assertion";
+  }
+
+  @Keep
+  public static void assertionsWithCatch3() {
+    try {
+      assert false : "Fourth assertion";
+    } catch (AssertionError e1) {
+      System.out.println("Caught from: " + AssertionHandlers.methodWithAssertionError(e1));
+      try {
+        simpleAssertion();
+      } catch (AssertionError e2) {
+        System.out.println("Caught from: " + AssertionHandlers.methodWithAssertionError(e2));
+      }
+    }
+  }
+
+  public static void main(String[] args) {
+    try {
+      assertionsWithCatch1();
+    } catch (AssertionError e) {
+      // Ignore.
+    }
+    assertionsWithCatch2();
+    assertionsWithCatch3();
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlersimple/AssertionConfigurationAssertionHandlerKotlinSimpleTest.java b/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlersimple/AssertionConfigurationAssertionHandlerKotlinSimpleTest.java
new file mode 100644
index 0000000..dc6353d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlersimple/AssertionConfigurationAssertionHandlerKotlinSimpleTest.java
@@ -0,0 +1,67 @@
+// 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.rewrite.assertions.kotlinassertionhandlersimple;
+
+import com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion;
+import com.android.tools.r8.KotlinTestParameters;
+import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.rewrite.assertions.AssertionConfigurationAssertionHandlerKotlinTestBase;
+import com.android.tools.r8.rewrite.assertions.assertionhandler.AssertionHandlers;
+import com.android.tools.r8.utils.StringUtils;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.List;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class AssertionConfigurationAssertionHandlerKotlinSimpleTest
+    extends AssertionConfigurationAssertionHandlerKotlinTestBase {
+
+  public AssertionConfigurationAssertionHandlerKotlinSimpleTest(
+      TestParameters parameters,
+      KotlinTestParameters kotlinParameters,
+      boolean kotlinStdlibAsClasspath,
+      boolean useJvmAssertions)
+      throws IOException {
+    super(parameters, kotlinParameters, kotlinStdlibAsClasspath, useJvmAssertions);
+  }
+
+  @Override
+  protected String getExpectedOutput() {
+    return StringUtils.lines(
+        "assertionHandler: simpleAssertion",
+        "assertionHandler: multipleAssertions 1",
+        "assertionHandler: multipleAssertions 2");
+  }
+
+  @Override
+  protected MethodReference getAssertionHandler() throws Exception {
+    return Reference.methodFromMethod(
+        AssertionHandlers.class.getMethod("assertionHandler", Throwable.class));
+  }
+
+  @Override
+  protected List<Path> getKotlinFiles() throws IOException {
+    return getKotlinFilesInTestPackage(getClass().getPackage());
+  }
+
+  @Override
+  protected String getTestClassName() {
+    return getClass().getPackage().getName() + ".AssertionSimpleKt";
+  }
+
+  @Override
+  protected void configureR8(R8FullTestBuilder builder) {
+    builder.applyIf(
+        !kotlinStdlibAsLibrary
+            && !useJvmAssertions
+            && !kotlinParameters.is(KotlinCompilerVersion.KOTLINC_1_3_72),
+        b -> b.addDontWarn("org.jetbrains.annotations.NotNull"));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlersimple/AssertionSimple.kt b/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlersimple/AssertionSimple.kt
new file mode 100644
index 0000000..7f6066b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlersimple/AssertionSimple.kt
@@ -0,0 +1,15 @@
+package com.android.tools.r8.rewrite.assertions.kotlinassertionhandlersimple
+
+fun simpleAssertion() {
+  assert(false) { "simpleAssertion" }
+}
+
+fun multipleAssertions() {
+  assert(false) { "multipleAssertions 1" }
+  assert(false) { "multipleAssertions 2" }
+}
+
+fun main() {
+  simpleAssertion();
+  multipleAssertions();
+}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlerwithexceptions/AssertionConfigurationAssertionHandlerKotlinRethrowingTest.java b/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlerwithexceptions/AssertionConfigurationAssertionHandlerKotlinRethrowingTest.java
new file mode 100644
index 0000000..93b3b57
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlerwithexceptions/AssertionConfigurationAssertionHandlerKotlinRethrowingTest.java
@@ -0,0 +1,71 @@
+// 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.rewrite.assertions.kotlinassertionhandlerwithexceptions;
+
+import com.android.tools.r8.KotlinTestParameters;
+import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.rewrite.assertions.AssertionConfigurationAssertionHandlerKotlinTestBase;
+import com.android.tools.r8.rewrite.assertions.assertionhandler.AssertionHandlers;
+import com.android.tools.r8.utils.StringUtils;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.List;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class AssertionConfigurationAssertionHandlerKotlinRethrowingTest
+    extends AssertionConfigurationAssertionHandlerKotlinTestBase {
+
+  public AssertionConfigurationAssertionHandlerKotlinRethrowingTest(
+      TestParameters parameters,
+      KotlinTestParameters kotlinParameters,
+      boolean kotlinStdlibAsClasspath,
+      boolean useJvmAssertions)
+      throws IOException {
+    super(parameters, kotlinParameters, kotlinStdlibAsClasspath, useJvmAssertions);
+  }
+
+  @Override
+  protected String getExpectedOutput() {
+    return StringUtils.lines(
+        "assertionHandlerRethrowing: First assertion",
+        "assertionHandlerRethrowing: Second assertion",
+        "Caught: Second assertion",
+        "assertionHandlerRethrowing: Third assertion",
+        "Caught: Third assertion",
+        "assertionHandlerRethrowing: Fourth assertion",
+        "Caught from: assertionsWithCatch3",
+        "assertionHandlerRethrowing: Fifth assertion",
+        "Caught from: simpleAssertion");
+  }
+
+  @Override
+  protected MethodReference getAssertionHandler() throws Exception {
+    return Reference.methodFromMethod(
+        AssertionHandlers.class.getMethod("assertionHandlerRethrowing", Throwable.class));
+  }
+
+  @Override
+  protected List<Path> getKotlinFiles() throws IOException {
+    return getKotlinFilesInTestPackage(getClass().getPackage());
+  }
+
+  @Override
+  protected String getTestClassName() {
+    return getClass().getPackage().getName() + ".AssertionsWithExceptionHandlersKt";
+  }
+
+  @Override
+  protected void configureR8(R8FullTestBuilder builder) {
+    builder
+        .addKeepRules("-keep class **.*Kt { assertionsWith*(); simpleAssertion(); }")
+        .addDontWarn("org.jetbrains.annotations.NotNull")
+        .applyIf(!kotlinStdlibAsLibrary, b -> b.addDontWarn("org.jetbrains.annotations.Nullable"));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlerwithexceptions/AssertionsWithExceptionHandlers.kt b/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlerwithexceptions/AssertionsWithExceptionHandlers.kt
new file mode 100644
index 0000000..dd70bc1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlerwithexceptions/AssertionsWithExceptionHandlers.kt
@@ -0,0 +1,54 @@
+package com.android.tools.r8.rewrite.assertions.kotlinassertionhandlerwithexceptions
+
+fun methodWithAssertionError(assertion : Throwable) : String {
+  return assertion.getStackTrace()[0].getMethodName();
+}
+
+fun assertionsWithCatch1() {
+  try {
+    assert(false) { "First assertion" }
+  } catch (e : NoSuchMethodError) {
+  } catch (e: NoSuchFieldError) {
+  } catch (e : NoClassDefFoundError) {
+  }
+}
+
+fun assertionsWithCatch2() {
+  try {
+    assert(false) { "Second assertion" }
+  } catch (e : AssertionError) {
+    println("Caught: " + e.message)
+    try {
+      assert(false) { "Third assertion" }
+    } catch (e : AssertionError) {
+      println("Caught: " + e.message)
+    }
+  }
+}
+
+fun simpleAssertion() {
+  assert(false) { "Fifth assertion" }
+}
+
+fun assertionsWithCatch3() {
+  try {
+    assert(false) { "Fourth assertion" }
+  } catch (e1 : AssertionError) {
+    println("Caught from: " + methodWithAssertionError(e1));
+    try {
+      simpleAssertion();
+    } catch (e2 : AssertionError) {
+      println("Caught from: " + methodWithAssertionError(e2));
+    }
+  }
+}
+
+fun main() {
+  try {
+    assertionsWithCatch1();
+  } catch (e : AssertionError) {
+    // Ignore.
+  }
+  assertionsWithCatch2();
+  assertionsWithCatch3();
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/annotations/KeepDisallowAnnotationRemovalAllowOptimizationTest.java b/src/test/java/com/android/tools/r8/shaking/annotations/KeepDisallowAnnotationRemovalAllowOptimizationTest.java
index 8fa7bc1..62bcf76 100644
--- a/src/test/java/com/android/tools/r8/shaking/annotations/KeepDisallowAnnotationRemovalAllowOptimizationTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/annotations/KeepDisallowAnnotationRemovalAllowOptimizationTest.java
@@ -11,6 +11,7 @@
 import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
 
+import com.android.tools.r8.KeepUnusedReturnValue;
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
@@ -53,6 +54,7 @@
         // In compatibility mode the rule above is a no-op.
         .allowUnusedProguardConfigurationRules(enableCompatibilityMode)
         .enableInliningAnnotations()
+        .enableKeepUnusedReturnValueAnnotations()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(
@@ -83,6 +85,7 @@
       }
     }
 
+    @KeepUnusedReturnValue
     @NeverInline
     static Object getNonNull() {
       System.out.println("getNonNull()");
diff --git a/src/test/java/com/android/tools/r8/shaking/clinit/ClassInitInlineForGetterTest.java b/src/test/java/com/android/tools/r8/shaking/clinit/ClassInitInlineForGetterTest.java
index b1e3efd..be07a10 100644
--- a/src/test/java/com/android/tools/r8/shaking/clinit/ClassInitInlineForGetterTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/clinit/ClassInitInlineForGetterTest.java
@@ -19,7 +19,6 @@
 public class ClassInitInlineForGetterTest extends TestBase {
 
   private static final String EXPECTED = "Hello World";
-  private static final String R8_RESULT = "Goodbye World";
 
   @Parameter() public TestParameters parameters;
 
@@ -46,7 +45,7 @@
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .run(parameters.getRuntime(), Main.class)
-        .assertSuccessWithOutputLines(R8_RESULT);
+        .assertSuccessWithOutputLines(EXPECTED);
   }
 
   @NeverClassInline
diff --git a/src/test/java/com/android/tools/r8/shaking/clinit/ClassInitInlineForStaticGetterInSuperTypeTest.java b/src/test/java/com/android/tools/r8/shaking/clinit/ClassInitInlineForStaticGetterInSuperTypeTest.java
new file mode 100644
index 0000000..7a4c15e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/clinit/ClassInitInlineForStaticGetterInSuperTypeTest.java
@@ -0,0 +1,87 @@
+// 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.clinit;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import 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 ClassInitInlineForStaticGetterInSuperTypeTest extends TestBase {
+
+  private static final String EXPECTED = "Hello World";
+  private static final String R8_EXPECTED = "Goodbye World";
+
+  @Parameter() public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  @Test
+  public void testRuntime() throws Exception {
+    testForRuntime(parameters)
+        .addInnerClasses(getClass())
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines(EXPECTED);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .setMinApi(parameters.getApiLevel())
+        .addKeepMainRule(Main.class)
+        .addKeepRules("-keep class " + typeName(B.class) + " { <fields>; }")
+        .enableInliningAnnotations()
+        .run(parameters.getRuntime(), Main.class)
+        // TODO(b/215477768): Should be Hello World
+        .assertSuccessWithOutputLines(R8_EXPECTED);
+  }
+
+  public static class A {
+
+    public static boolean TEST = System.currentTimeMillis() == 0;
+
+    @NeverInline
+    public static boolean getTestStatic() {
+      return TEST;
+    }
+  }
+
+  public static class B extends A {
+
+    static {
+      B.TEST = true;
+    }
+
+    @NeverInline
+    public static void triggerClassAInit(boolean test) {
+      if (test) {
+        System.out.println("Hello World");
+      } else {
+        System.out.println("Goodbye World");
+      }
+    }
+
+    public static void inlinable() {
+      triggerClassAInit(B.getTestStatic());
+    }
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) {
+      B.inlinable();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/reflection/ReflectiveNewInstanceTest.java b/src/test/java/com/android/tools/r8/shaking/reflection/ReflectiveNewInstanceTest.java
index 544a7ee..47a9fc7 100644
--- a/src/test/java/com/android/tools/r8/shaking/reflection/ReflectiveNewInstanceTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/reflection/ReflectiveNewInstanceTest.java
@@ -29,7 +29,7 @@
 
   @Parameters(name = "{0}")
   public static TestParametersCollection data() {
-    return getTestParameters().withAllRuntimes().build();
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
   }
 
   public ReflectiveNewInstanceTest(TestParameters parameters) {
@@ -57,7 +57,7 @@
             .addKeepMainRule(TestClass.class)
             .enableGraphInspector()
             .enableUnusedArgumentAnnotations()
-            .setMinApi(parameters.getRuntime())
+            .setMinApi(parameters.getApiLevel())
             .run(parameters.getRuntime(), TestClass.class)
             .assertSuccessWithOutput(expectedOutputAfterR8)
             .graphInspector();
diff --git a/third_party/android_jar/api-versions.tar.gz.sha1 b/third_party/android_jar/api-versions.tar.gz.sha1
deleted file mode 100644
index 0c98c4f..0000000
--- a/third_party/android_jar/api-versions.tar.gz.sha1
+++ /dev/null
@@ -1 +0,0 @@
-788aea6588610917a910af5d0e658fb13e8f7baf
\ No newline at end of file
diff --git a/third_party/android_jar/lib-v31.tar.gz.sha1 b/third_party/android_jar/lib-v31.tar.gz.sha1
index 0af2989..dbc61eb 100644
--- a/third_party/android_jar/lib-v31.tar.gz.sha1
+++ b/third_party/android_jar/lib-v31.tar.gz.sha1
@@ -1 +1 @@
-3f0543d825941eee32abca750ce30e83ed451f30
\ No newline at end of file
+04e0e01c814737bfed7a95e0b7218094b6c79c1f
\ No newline at end of file
diff --git a/third_party/android_jar/lib-v32.tar.gz.sha1 b/third_party/android_jar/lib-v32.tar.gz.sha1
index 1d92e7e..1084ccf 100644
--- a/third_party/android_jar/lib-v32.tar.gz.sha1
+++ b/third_party/android_jar/lib-v32.tar.gz.sha1
@@ -1 +1 @@
-9b07cb997fcedd7314bb9cf35b9ddacbe817c51c
\ No newline at end of file
+82f69bfeceab5f11796d768e165f800ee7439bd8
\ No newline at end of file
diff --git a/tools/run_benchmark.py b/tools/run_benchmark.py
index 2ad5200..d5d4fc0 100755
--- a/tools/run_benchmark.py
+++ b/tools/run_benchmark.py
@@ -21,9 +21,12 @@
 # The r8lib target is always the golem target.
 GOLEM_BUILD_TARGETS = [R8LIB_BUILD_TARGET] + R8LIB_TEST_BUILD_TARGETS
 
+def get_golem_resource_path(benchmark):
+  return os.path.join('benchmarks', benchmark + 'Group')
+
 def get_jdk_home(options, benchmark):
   if options.golem:
-    return os.path.join('benchmarks', benchmark, 'linux')
+    return os.path.join(get_golem_resource_path(benchmark), 'linux')
   return None
 
 def parse_options(argv):
diff --git a/tools/run_on_app_dump.py b/tools/run_on_app_dump.py
index 6035752..948fb7c 100755
--- a/tools/run_on_app_dump.py
+++ b/tools/run_on_app_dump.py
@@ -1151,6 +1151,7 @@
   with utils.TempDir() as temp_dir:
     if options.temp:
       temp_dir = options.temp
+      os.makedirs(temp_dir, exist_ok=True)
     if options.hash:
       # Download r8-<hash>.jar from
       # https://storage.googleapis.com/r8-releases/raw/.