Merge commit 'd9bd076076490ac1b89acf2385d94d15dc6e1b68' into dev-release
diff --git a/infra/config/global/generated/cr-buildbucket.cfg b/infra/config/global/generated/cr-buildbucket.cfg
index 577a0d0..49e39c7 100644
--- a/infra/config/global/generated/cr-buildbucket.cfg
+++ b/infra/config/global/generated/cr-buildbucket.cfg
@@ -24,7 +24,7 @@
       swarming_tags: "vpython:native-python-wrapper"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -53,7 +53,7 @@
       swarming_tags: "vpython:native-python-wrapper"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -82,7 +82,7 @@
       swarming_tags: "vpython:native-python-wrapper"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -118,7 +118,7 @@
       swarming_tags: "vpython:native-python-wrapper"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -156,7 +156,7 @@
       swarming_tags: "vpython:native-python-wrapper"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -190,7 +190,7 @@
       swarming_tags: "vpython:native-python-wrapper"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -222,7 +222,7 @@
       swarming_tags: "vpython:native-python-wrapper"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -255,7 +255,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -292,7 +292,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -329,7 +329,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -366,7 +366,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -403,7 +403,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -440,7 +440,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -477,7 +477,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -514,7 +514,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -551,7 +551,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -588,7 +588,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -625,7 +625,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -662,7 +662,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -699,7 +699,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -736,7 +736,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -773,7 +773,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -810,7 +810,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -847,7 +847,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -884,7 +884,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -921,7 +921,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -958,7 +958,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -995,7 +995,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -1032,7 +1032,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -1069,7 +1069,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -1105,7 +1105,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -1141,7 +1141,7 @@
       dimensions: "cores:2"
       dimensions: "cpu:x86-64"
       dimensions: "internal:true"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -1174,7 +1174,7 @@
       dimensions: "cores:2"
       dimensions: "cpu:x86-64"
       dimensions: "internal:true"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -1207,7 +1207,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -1243,7 +1243,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -1279,7 +1279,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -1315,7 +1315,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -1351,7 +1351,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -1387,7 +1387,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -1423,7 +1423,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -1459,7 +1459,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -1494,7 +1494,7 @@
       swarming_tags: "vpython:native-python-wrapper"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -1531,7 +1531,7 @@
       swarming_tags: "vpython:native-python-wrapper"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -1569,7 +1569,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -1605,7 +1605,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -1640,7 +1640,7 @@
       swarming_tags: "vpython:native-python-wrapper"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -1672,7 +1672,7 @@
       swarming_tags: "vpython:native-python-wrapper"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
diff --git a/infra/config/global/generated/project.cfg b/infra/config/global/generated/project.cfg
index 90b913a..11b6c85 100644
--- a/infra/config/global/generated/project.cfg
+++ b/infra/config/global/generated/project.cfg
@@ -7,7 +7,7 @@
 name: "r8"
 access: "group:all"
 lucicfg {
-  version: "1.38.1"
+  version: "1.38.2"
   package_dir: ".."
   config_dir: "generated"
   entry_point: "main.star"
diff --git a/infra/config/global/main.star b/infra/config/global/main.star
index bbbf235..9fb3733 100755
--- a/infra/config/global/main.star
+++ b/infra/config/global/main.star
@@ -178,7 +178,7 @@
   if windows:
     dimensions["os"] = "Windows-10"
   else:
-    dimensions["os"] = "Ubuntu-18.04"
+    dimensions["os"] = "Ubuntu-22.04"
   if internal:
     dimensions["internal"] = "true"
   if normal:
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
index f251ecb..285ef1b 100644
--- a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
@@ -65,6 +65,7 @@
   private final List<ArtProfileForRewriting> artProfilesForRewriting;
   private final List<StartupProfileProvider> startupProfileProviders;
   private final ClassConflictResolver classConflictResolver;
+  private final CancelCompilationChecker cancelCompilationChecker;
 
   BaseCompilerCommand(boolean printHelp, boolean printVersion) {
     super(printHelp, printVersion);
@@ -87,6 +88,7 @@
     artProfilesForRewriting = null;
     startupProfileProviders = null;
     classConflictResolver = null;
+    cancelCompilationChecker = null;
   }
 
   BaseCompilerCommand(
@@ -109,7 +111,8 @@
       boolean isAndroidPlatformBuild,
       List<ArtProfileForRewriting> artProfilesForRewriting,
       List<StartupProfileProvider> startupProfileProviders,
-      ClassConflictResolver classConflictResolver) {
+      ClassConflictResolver classConflictResolver,
+      CancelCompilationChecker cancelCompilationChecker) {
     super(app);
     assert minApiLevel > 0;
     assert mode != null;
@@ -132,6 +135,7 @@
     this.artProfilesForRewriting = artProfilesForRewriting;
     this.startupProfileProviders = startupProfileProviders;
     this.classConflictResolver = classConflictResolver;
+    this.cancelCompilationChecker = cancelCompilationChecker;
   }
 
   /**
@@ -244,6 +248,10 @@
     return classConflictResolver;
   }
 
+  public CancelCompilationChecker getCancelCompilationChecker() {
+    return cancelCompilationChecker;
+  }
+
   DumpInputFlags getDumpInputFlags() {
     return dumpInputFlags;
   }
@@ -287,6 +295,7 @@
     private List<ArtProfileForRewriting> artProfilesForRewriting = new ArrayList<>();
     private List<StartupProfileProvider> startupProfileProviders = new ArrayList<>();
     private ClassConflictResolver classConflictResolver = null;
+    private CancelCompilationChecker cancelCompilationChecker = null;
 
     abstract CompilationMode defaultCompilationMode();
 
@@ -733,6 +742,21 @@
     }
 
     /**
+     * Set a cancellation checker.
+     *
+     * <p>The cancellation checker will be periodically called to check if the compilation should be
+     * cancelled before completion.
+     */
+    public B setCancelCompilationChecker(CancelCompilationChecker checker) {
+      this.cancelCompilationChecker = checker;
+      return self();
+    }
+
+    public CancelCompilationChecker getCancelCompilationChecker() {
+      return cancelCompilationChecker;
+    }
+
+    /**
      * Allow to skip to dump into file and dump into directory instruction, this is primarily used
      * for chained compilation in L8 so there are no duplicated dumps.
      */
diff --git a/src/main/java/com/android/tools/r8/CancelCompilationChecker.java b/src/main/java/com/android/tools/r8/CancelCompilationChecker.java
new file mode 100644
index 0000000..f08cde1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/CancelCompilationChecker.java
@@ -0,0 +1,26 @@
+// Copyright (c) 2023, 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;
+
+/** Client supplied checker to allow cancelling a compilation before completion. */
+@Keep
+public interface CancelCompilationChecker {
+
+  /**
+   * Callback that is called periodically by the compiler to check if the current compilation should
+   * be cancelled before completion. The method should return true or false. The behavior of
+   * throwing an exception from this method will also abort the compilation but the compiler will
+   * treat such cases as an unexpected runtime failure and not as an expected client cancellation.
+   *
+   * <p>If this method returns true at any point before compilation has successfully completed, then
+   * the compiler will exit with a {@link CompilationFailedException}. It is *not* possible to
+   * cancel the cancellation.
+   *
+   * <p>This method may be called from multiple compiler threads. It is up to the client to ensure
+   * thread safety in determining if compilation should be cancelled.
+   *
+   * @return True if the compilation should be cancelled, otherwise false.
+   */
+  boolean cancel();
+}
diff --git a/src/main/java/com/android/tools/r8/CompilationFailedException.java b/src/main/java/com/android/tools/r8/CompilationFailedException.java
index 0078c06..94c7fde 100644
--- a/src/main/java/com/android/tools/r8/CompilationFailedException.java
+++ b/src/main/java/com/android/tools/r8/CompilationFailedException.java
@@ -10,19 +10,15 @@
 @Keep
 public class CompilationFailedException extends Exception {
 
-  public CompilationFailedException() {
-    super("Compilation failed to complete");
-  }
+  private final boolean cancelled;
 
-  public CompilationFailedException(Throwable cause) {
-    this("Compilation failed to complete", cause);
-  }
-
-  public CompilationFailedException(String message, Throwable cause) {
+  CompilationFailedException(String message, Throwable cause, boolean cancelled) {
     super(message, cause);
+    this.cancelled = cancelled;
   }
 
-  public CompilationFailedException(String message) {
-    super(message);
+  /** True if the compilation was cancelled by {@link CancelCompilationChecker} otherwise false. */
+  public boolean wasCancelled() {
+    return cancelled;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index d1495c2..49f8155 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -12,6 +12,7 @@
 import com.android.tools.r8.inspector.Inspector;
 import com.android.tools.r8.inspector.internal.InspectorImpl;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification;
+import com.android.tools.r8.naming.ProguardMapStringConsumer;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.profile.art.ArtProfileForRewriting;
 import com.android.tools.r8.shaking.ProguardConfigurationParser;
@@ -490,6 +491,7 @@
           getArtProfilesForRewriting(),
           getStartupProfileProviders(),
           getClassConflictResolver(),
+          getCancelCompilationChecker(),
           factory);
     }
   }
@@ -585,6 +587,7 @@
       List<ArtProfileForRewriting> artProfilesForRewriting,
       List<StartupProfileProvider> startupProfileProviders,
       ClassConflictResolver classConflictResolver,
+      CancelCompilationChecker cancelCompilationChecker,
       DexItemFactory factory) {
     super(
         inputApp,
@@ -606,7 +609,8 @@
         isAndroidPlatformBuild,
         artProfilesForRewriting,
         startupProfileProviders,
-        classConflictResolver);
+        classConflictResolver,
+        cancelCompilationChecker);
     this.intermediate = intermediate;
     this.globalSyntheticsConsumer = globalSyntheticsConsumer;
     this.syntheticInfoConsumer = syntheticInfoConsumer;
@@ -665,7 +669,13 @@
     internal.setSyntheticInfoConsumer(syntheticInfoConsumer);
     internal.desugarGraphConsumer = desugarGraphConsumer;
     internal.mainDexKeepRules = mainDexKeepRules;
-    internal.proguardMapConsumer = proguardMapConsumer;
+    internal.proguardMapConsumer =
+        proguardMapConsumer == null
+            ? null
+            : ProguardMapStringConsumer.builder()
+                .setStringConsumer(proguardMapConsumer)
+                .setDiagnosticsHandler(getReporter())
+                .build();
     internal.lineNumberOptimization =
         !internal.debug && proguardMapConsumer != null
             ? LineNumberOptimization.ON
@@ -742,6 +752,8 @@
         ProgramClassCollection.wrappedConflictResolver(
             getClassConflictResolver(), internal.reporter);
 
+    internal.cancelCompilationChecker = getCancelCompilationChecker();
+
     internal.tool = Tool.D8;
     internal.setDumpInputFlags(getDumpInputFlags());
     internal.dumpOptions = dumpOptions();
diff --git a/src/main/java/com/android/tools/r8/Finishable.java b/src/main/java/com/android/tools/r8/Finishable.java
new file mode 100644
index 0000000..2e114ef
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/Finishable.java
@@ -0,0 +1,19 @@
+// Copyright (c) 2023, 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;
+
+public interface Finishable {
+
+  /**
+   * Callback when no further content will be provided for the resource.
+   *
+   * <p>The consumer is expected not to throw, but instead report any errors via the diagnostics
+   * {@param handler}. If an error is reported via {@param handler} and no exceptions are thrown,
+   * then the compiler guaranties to exit with an error.
+   *
+   * @param handler Diagnostics handler for reporting.
+   */
+  default void finished(DiagnosticsHandler handler) {}
+}
diff --git a/src/main/java/com/android/tools/r8/InternalCompilationFailedExceptionUtils.java b/src/main/java/com/android/tools/r8/InternalCompilationFailedExceptionUtils.java
new file mode 100644
index 0000000..18e96ff
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/InternalCompilationFailedExceptionUtils.java
@@ -0,0 +1,31 @@
+// Copyright (c) 2023, 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;
+
+/** Non-API class to allocate compilation failed exceptions without exposing the constructor. */
+public final class InternalCompilationFailedExceptionUtils {
+
+  private InternalCompilationFailedExceptionUtils() {}
+
+  public static CompilationFailedException createForTesting() {
+    return createForTesting("Compilation failed to complete", null);
+  }
+
+  public static CompilationFailedException createForTesting(String message) {
+    return createForTesting(message, null);
+  }
+
+  public static CompilationFailedException createForTesting(Throwable cause) {
+    return createForTesting("Compilation failed to complete", cause);
+  }
+
+  public static CompilationFailedException createForTesting(String message, Throwable cause) {
+    return create(message, cause, false);
+  }
+
+  public static CompilationFailedException create(
+      String message, Throwable cause, boolean cancelled) {
+    return new CompilationFailedException(message, cause, cancelled);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java
index 6bac5df..7a28ef3 100644
--- a/src/main/java/com/android/tools/r8/L8Command.java
+++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -102,6 +102,7 @@
       DumpInputFlags dumpInputFlags,
       MapIdProvider mapIdProvider,
       ClassConflictResolver classConflictResolver,
+      CancelCompilationChecker cancelCompilationChecker,
       DexItemFactory factory) {
     super(
         inputApp,
@@ -123,7 +124,8 @@
         false,
         null,
         null,
-        classConflictResolver);
+        classConflictResolver,
+        cancelCompilationChecker);
     this.d8Command = d8Command;
     this.r8Command = r8Command;
     this.desugaredLibrarySpecification = desugaredLibrarySpecification;
@@ -228,6 +230,8 @@
         ProgramClassCollection.wrappedConflictResolver(
             getClassConflictResolver(), internal.reporter);
 
+    internal.cancelCompilationChecker = getCancelCompilationChecker();
+
     if (!DETERMINISTIC_DEBUGGING) {
       assert internal.threadCount == ThreadUtils.NOT_SPECIFIED;
       internal.threadCount = getThreadCount();
@@ -456,6 +460,7 @@
           getDumpInputFlags(),
           getMapIdProvider(),
           getClassConflictResolver(),
+          getCancelCompilationChecker(),
           factory);
     }
   }
diff --git a/src/main/java/com/android/tools/r8/ProguardMapConsumer.java b/src/main/java/com/android/tools/r8/ProguardMapConsumer.java
new file mode 100644
index 0000000..f00b428
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ProguardMapConsumer.java
@@ -0,0 +1,13 @@
+// Copyright (c) 2023, 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 com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.naming.ProguardMapMarkerInfo;
+
+public abstract class ProguardMapConsumer implements Finishable {
+
+  public abstract void accept(ProguardMapMarkerInfo makerInfo, ClassNameMapper classNameMapper);
+}
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index d96d546..2ff0966 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -67,10 +67,10 @@
 import com.android.tools.r8.optimize.MemberRebindingAnalysis;
 import com.android.tools.r8.optimize.MemberRebindingIdentityLens;
 import com.android.tools.r8.optimize.MemberRebindingIdentityLensFactory;
-import com.android.tools.r8.optimize.RedundantBridgeRemover;
 import com.android.tools.r8.optimize.bridgehoisting.BridgeHoisting;
 import com.android.tools.r8.optimize.interfaces.analysis.CfOpenClosedInterfacesAnalysis;
 import com.android.tools.r8.optimize.proto.ProtoNormalizer;
+import com.android.tools.r8.optimize.redundantbridgeremoval.RedundantBridgeRemover;
 import com.android.tools.r8.origin.CommandLineOrigin;
 import com.android.tools.r8.profile.art.ArtProfileCompletenessChecker;
 import com.android.tools.r8.profile.art.rewriting.ProfileCollectionAdditions;
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index ee35e7f..6003038 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -18,6 +18,7 @@
 import com.android.tools.r8.keepanno.asm.KeepEdgeReader;
 import com.android.tools.r8.keepanno.ast.KeepEdge;
 import com.android.tools.r8.keepanno.keeprules.KeepRuleExtractor;
+import com.android.tools.r8.naming.ProguardMapStringConsumer;
 import com.android.tools.r8.naming.SourceFileRewriter;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.origin.PathOrigin;
@@ -659,7 +660,7 @@
               getArtProfilesForRewriting(),
               getStartupProfileProviders(),
               getClassConflictResolver(),
-              fakeCompilerVersion);
+              getCancelCompilationChecker());
 
       if (inputDependencyGraphConsumer != null) {
         inputDependencyGraphConsumer.finished();
@@ -846,7 +847,6 @@
   private final FeatureSplitConfiguration featureSplitConfiguration;
   private final String synthesizedClassPrefix;
   private final boolean enableMissingLibraryApiModeling;
-  private final SemanticVersion fakeCompilerVersion;
 
   /** Get a new {@link R8Command.Builder}. */
   public static Builder builder() {
@@ -940,7 +940,7 @@
       List<ArtProfileForRewriting> artProfilesForRewriting,
       List<StartupProfileProvider> startupProfileProviders,
       ClassConflictResolver classConflictResolver,
-      SemanticVersion fakeCompilerVersion) {
+      CancelCompilationChecker cancelCompilationChecker) {
     super(
         inputApp,
         mode,
@@ -961,7 +961,8 @@
         isAndroidPlatformBuild,
         artProfilesForRewriting,
         startupProfileProviders,
-        classConflictResolver);
+        classConflictResolver,
+        cancelCompilationChecker);
     assert proguardConfiguration != null;
     assert mainDexKeepRules != null;
     this.mainDexKeepRules = mainDexKeepRules;
@@ -983,7 +984,6 @@
     this.featureSplitConfiguration = featureSplitConfiguration;
     this.synthesizedClassPrefix = synthesizedClassPrefix;
     this.enableMissingLibraryApiModeling = enableMissingLibraryApiModeling;
-    this.fakeCompilerVersion = fakeCompilerVersion;
   }
 
   private R8Command(boolean printHelp, boolean printVersion) {
@@ -1007,7 +1007,6 @@
     featureSplitConfiguration = null;
     synthesizedClassPrefix = null;
     enableMissingLibraryApiModeling = false;
-    fakeCompilerVersion = null;
   }
 
   public DexItemFactory getDexItemFactory() {
@@ -1070,11 +1069,18 @@
     }
 
     // Amend the proguard-map consumer with options from the proguard configuration.
-    internal.proguardMapConsumer =
+    StringConsumer stringConsumer =
         wrapStringConsumer(
             proguardMapConsumer,
             proguardConfiguration.isPrintMapping(),
             proguardConfiguration.getPrintMappingFile());
+    internal.proguardMapConsumer =
+        stringConsumer == null
+            ? null
+            : ProguardMapStringConsumer.builder()
+                .setStringConsumer(stringConsumer)
+                .setDiagnosticsHandler(getReporter())
+                .build();
 
     // Amend the usage information consumer with options from the proguard configuration.
     internal.usageInformationConsumer =
@@ -1173,6 +1179,8 @@
         ProgramClassCollection.wrappedConflictResolver(
             getClassConflictResolver(), internal.reporter);
 
+    internal.cancelCompilationChecker = getCancelCompilationChecker();
+
     if (!DETERMINISTIC_DEBUGGING) {
       assert internal.threadCount == ThreadUtils.NOT_SPECIFIED;
       internal.threadCount = getThreadCount();
diff --git a/src/main/java/com/android/tools/r8/StringConsumer.java b/src/main/java/com/android/tools/r8/StringConsumer.java
index d0e05cc..2583572 100644
--- a/src/main/java/com/android/tools/r8/StringConsumer.java
+++ b/src/main/java/com/android/tools/r8/StringConsumer.java
@@ -15,7 +15,7 @@
 
 /** Interface for receiving String resource. */
 @KeepForSubclassing
-public interface StringConsumer {
+public interface StringConsumer extends Finishable {
 
   /**
    * Callback to receive part of a string resource.
@@ -32,17 +32,6 @@
    */
   void accept(String string, DiagnosticsHandler handler);
 
-  /**
-   * Callback when no further content will be provided for the string resource.
-   *
-   * <p>The consumer is expected not to throw, but instead report any errors via the diagnostics
-   * {@param handler}. If an error is reported via {@param handler} and no exceptions are thrown,
-   * then the compiler guaranties to exit with an error.
-   *
-   * @param handler Diagnostics handler for reporting.
-   */
-  default void finished(DiagnosticsHandler handler) {}
-
   static EmptyConsumer emptyConsumer() {
     return EmptyConsumer.EMPTY_CONSUMER;
   }
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelCompute.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelCompute.java
index fb97359..1b732da 100644
--- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelCompute.java
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelCompute.java
@@ -47,6 +47,9 @@
     return computeApiLevelForLibraryReference(reference, ComputedApiLevel.unknown());
   }
 
+  public abstract ComputedApiLevel computeApiLevelForLibraryReferenceIgnoringDesugaredLibrary(
+      DexReference reference, ComputedApiLevel unknownValue);
+
   public ComputedApiLevel computeApiLevelForDefinition(Iterable<DexType> types) {
     return computeApiLevelForDefinition(types, ComputedApiLevel.unknown());
   }
@@ -102,6 +105,12 @@
     }
 
     @Override
+    public ComputedApiLevel computeApiLevelForLibraryReferenceIgnoringDesugaredLibrary(
+        DexReference reference, ComputedApiLevel unknownValue) {
+      return ComputedApiLevel.notSet();
+    }
+
+    @Override
     public ComputedApiLevel computeInitialMinApiLevel(InternalOptions options) {
       return ComputedApiLevel.notSet();
     }
@@ -141,6 +150,12 @@
     }
 
     @Override
+    public ComputedApiLevel computeApiLevelForLibraryReferenceIgnoringDesugaredLibrary(
+        DexReference reference, ComputedApiLevel unknownValue) {
+      return cache.lookupIgnoringDesugaredLibrary(reference, unknownValue);
+    }
+
+    @Override
     public void reportUnknownApiReferences() {
       cache
           .getUnknownReferencesToReport()
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 0e267e5..627063c 100644
--- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
@@ -67,12 +67,22 @@
   }
 
   public ComputedApiLevel lookup(DexReference reference, ComputedApiLevel unknownValue) {
+    return lookup(reference, unknownValue, false);
+  }
+
+  public ComputedApiLevel lookupIgnoringDesugaredLibrary(
+      DexReference reference, ComputedApiLevel unknownValue) {
+    return lookup(reference, unknownValue, true);
+  }
+
+  private ComputedApiLevel lookup(
+      DexReference reference, ComputedApiLevel unknownValue, boolean ignoringDesugaredLibrary) {
     DexType contextType = reference.getContextType();
     if (contextType.isArrayType()) {
       if (reference.isDexMethod() && reference.asDexMethod().match(factory.objectMembers.clone)) {
         return appView.computedMinApiLevel();
       }
-      return lookup(contextType.toBaseType(factory), unknownValue);
+      return lookup(contextType.toBaseType(factory), unknownValue, ignoringDesugaredLibrary);
     }
     if (contextType.isPrimitiveType() || contextType.isVoidType()) {
       return appView.computedMinApiLevel();
@@ -87,10 +97,11 @@
     if (reference.getContextType() == factory.objectType) {
       return appView.computedMinApiLevel();
     }
-    if (appView
-        .options()
-        .machineDesugaredLibrarySpecification
-        .isContextTypeMaintainedOrRewritten(reference)) {
+    if (!ignoringDesugaredLibrary
+        && appView
+            .options()
+            .machineDesugaredLibrarySpecification
+            .isContextTypeMaintainedOrRewritten(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/bisect/Bisect.java b/src/main/java/com/android/tools/r8/bisect/Bisect.java
index 8a196d9..caa1c27 100644
--- a/src/main/java/com/android/tools/r8/bisect/Bisect.java
+++ b/src/main/java/com/android/tools/r8/bisect/Bisect.java
@@ -5,7 +5,7 @@
 
 import com.android.tools.r8.OutputMode;
 import com.android.tools.r8.ProgramConsumer;
-import com.android.tools.r8.StringConsumer;
+import com.android.tools.r8.ProguardMapConsumer;
 import com.android.tools.r8.bisect.BisectOptions.Result;
 import com.android.tools.r8.dex.ApplicationReader;
 import com.android.tools.r8.dex.ApplicationWriter;
@@ -182,9 +182,9 @@
   private static void writeApp(DexApplication app, Path output, ExecutorService executor)
       throws IOException, ExecutionException {
     InternalOptions options = app.options;
-    // Save the original consumers so they can be unwrapped after write.
+    // Save the original consumers, so they can be unwrapped after write.
     ProgramConsumer programConsumer = options.programConsumer;
-    StringConsumer proguardMapConsumer = options.proguardMapConsumer;
+    ProguardMapConsumer proguardMapConsumer = options.proguardMapConsumer;
     AndroidAppConsumers compatSink = new AndroidAppConsumers(options);
     ApplicationWriter writer =
         ApplicationWriter.create(
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfArithmeticBinop.java b/src/main/java/com/android/tools/r8/cf/code/CfArithmeticBinop.java
index 95cdc02..398da41 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfArithmeticBinop.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfArithmeticBinop.java
@@ -120,23 +120,28 @@
   }
 
   public int getAsmOpcode() {
+    return getAsmOpcode(opcode, type);
+  }
+
+  public static int getAsmOpcode(Opcode opcode, NumericType type) {
+    int typeOffset = getAsmOpcodeTypeOffset(type);
     switch (opcode) {
       case Add:
-        return Opcodes.IADD + getAsmOpcodeTypeOffset();
+        return Opcodes.IADD + typeOffset;
       case Sub:
-        return Opcodes.ISUB + getAsmOpcodeTypeOffset();
+        return Opcodes.ISUB + typeOffset;
       case Mul:
-        return Opcodes.IMUL + getAsmOpcodeTypeOffset();
+        return Opcodes.IMUL + typeOffset;
       case Div:
-        return Opcodes.IDIV + getAsmOpcodeTypeOffset();
+        return Opcodes.IDIV + typeOffset;
       case Rem:
-        return Opcodes.IREM + getAsmOpcodeTypeOffset();
+        return Opcodes.IREM + typeOffset;
       default:
         throw new Unreachable("CfArithmeticBinop has unknown opcode " + opcode);
     }
   }
 
-  private int getAsmOpcodeTypeOffset() {
+  private static int getAsmOpcodeTypeOffset(NumericType type) {
     switch (type) {
       case LONG:
         return Opcodes.LADD - Opcodes.IADD;
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
index c741ee9..eda00f9 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
@@ -49,6 +49,8 @@
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Timing;
+import it.unimi.dsi.fastutil.ints.IntArrayList;
+import it.unimi.dsi.fastutil.ints.IntList;
 import java.io.IOException;
 import java.nio.file.Path;
 import java.util.ArrayList;
@@ -337,9 +339,17 @@
       for (ProgramResource input : dexSources) {
         DexReader dexReader = new DexReader(input);
         if (options.passthroughDexCode) {
-          computedMinApiLevel = validateOrComputeMinApiLevel(computedMinApiLevel, dexReader);
+          if (!options.testing.dexContainerExperiment) {
+            computedMinApiLevel = validateOrComputeMinApiLevel(computedMinApiLevel, dexReader);
+          } else {
+            assert dexReader.getDexVersion() == DexVersion.V41;
+          }
         }
-        dexParsers.add(new DexParser<>(dexReader, PROGRAM, options));
+        if (!options.testing.dexContainerExperiment) {
+          dexParsers.add(new DexParser<>(dexReader, PROGRAM, options));
+        } else {
+          addDexParsersForContainer(dexParsers, dexReader);
+        }
       }
 
       options.setMinApiLevel(computedMinApiLevel);
@@ -349,17 +359,56 @@
       // Read the DexCode items and DexProgramClass items in parallel.
       if (!options.skipReadingDexCode) {
         ApplicationReaderMap applicationReaderMap = ApplicationReaderMap.getInstance(options);
-        for (DexParser<DexProgramClass> dexParser : dexParsers) {
-          futures.add(
-              executorService.submit(
-                  () -> {
-                    dexParser.addClassDefsTo(
-                        classes::add, applicationReaderMap); // Depends on Methods, Code items etc.
-                  }));
+        if (!options.testing.dexContainerExperiment) {
+          for (DexParser<DexProgramClass> dexParser : dexParsers) {
+            futures.add(
+                executorService.submit(
+                    () -> {
+                      dexParser.addClassDefsTo(
+                          classes::add,
+                          applicationReaderMap); // Depends on Methods, Code items etc.
+                    }));
+          }
+        } else {
+          // All Dex parsers use the same DEX reader, so don't process in parallel.
+          for (int i = 0; i < dexParsers.size(); i++) {
+            dexParsers.get(i).addClassDefsTo(classes::add, applicationReaderMap);
+          }
         }
       }
     }
 
+    private void addDexParsersForContainer(
+        List<DexParser<DexProgramClass>> dexParsers, DexReader dexReader) {
+      // Find the start offsets of each dex section.
+      IntList offsets = new IntArrayList();
+      dexReader.setByteOrder();
+      int offset = 0;
+      while (offset < dexReader.end()) {
+        offsets.add(offset);
+        DexReader tmp = new DexReader(Origin.unknown(), dexReader.buffer.array(), offset);
+        assert tmp.getDexVersion() == DexVersion.V41;
+        assert dexReader.getUint(offset + Constants.HEADER_SIZE_OFFSET)
+            == Constants.TYPE_HEADER_ITEM_SIZE_V41;
+        assert dexReader.getUint(offset + Constants.HEADER_OFF_OFFSET) == offset;
+        int dataSize = dexReader.getUint(offset + Constants.DATA_SIZE_OFFSET);
+        int dataOffset = dexReader.getUint(offset + Constants.DATA_OFF_OFFSET);
+        int file_size = dexReader.getUint(offset + Constants.FILE_SIZE_OFFSET);
+        assert dataOffset == 0;
+        assert dataSize == 0;
+        offset += file_size;
+      }
+      assert offset == dexReader.end();
+      // Create a parser for the last section with string data.
+      DexParser<DexProgramClass> last =
+          new DexParser<>(dexReader, PROGRAM, options, offsets.getInt(offsets.size() - 1), null);
+      // Create a parsers for the remaining sections with reference to the string data.
+      for (int i = 0; i < offsets.size() - 1; i++) {
+        dexParsers.add(new DexParser<>(dexReader, PROGRAM, options, offsets.getInt(i), last));
+      }
+      dexParsers.add(last);
+    }
+
     private boolean includeAnnotationClass(DexProgramClass clazz) {
       if (!options.pruneNonVissibleAnnotationClasses) {
         return true;
diff --git a/src/main/java/com/android/tools/r8/dex/DexParser.java b/src/main/java/com/android/tools/r8/dex/DexParser.java
index 0d9fac5..263acfc 100644
--- a/src/main/java/com/android/tools/r8/dex/DexParser.java
+++ b/src/main/java/com/android/tools/r8/dex/DexParser.java
@@ -95,21 +95,23 @@
   private final int NO_INDEX = -1;
   private final Origin origin;
   private DexReader dexReader;
-  private final DexSection[] dexSections;
+  private final List<DexSection> dexSections;
+  private final int offset;
   private int[] stringIDs;
   private final ClassKind<T> classKind;
   private final InternalOptions options;
   private Object2LongMap<String> checksums;
 
-  public static DexSection[] parseMapFrom(Path file) throws IOException {
+  public static List<DexSection> parseMapFrom(Path file) throws IOException {
     return parseMapFrom(Files.newInputStream(file), new PathOrigin(file));
   }
 
-  public static DexSection[] parseMapFrom(InputStream stream, Origin origin) throws IOException {
+  public static List<DexSection> parseMapFrom(InputStream stream, Origin origin)
+      throws IOException {
     return parseMapFrom(new DexReader(origin, ByteStreams.toByteArray(stream)));
   }
 
-  private static DexSection[] parseMapFrom(DexReader dexReader) {
+  private static List<DexSection> parseMapFrom(DexReader dexReader) {
     DexParser<DexProgramClass> dexParser =
         new DexParser<>(dexReader, ClassKind.PROGRAM, new InternalOptions());
     return dexParser.dexSections;
@@ -144,13 +146,27 @@
   private final DexItemFactory dexItemFactory;
 
   public DexParser(DexReader dexReader, ClassKind<T> classKind, InternalOptions options) {
+    this(dexReader, classKind, options, 0, null);
+  }
+
+  public DexParser(
+      DexReader dexReader,
+      ClassKind<T> classKind,
+      InternalOptions options,
+      int offset,
+      DexParser<T> parserWithStringIDs) {
     assert dexReader.getOrigin() != null;
     this.origin = dexReader.getOrigin();
     this.dexReader = dexReader;
+    this.offset = offset;
     this.dexItemFactory = options.itemFactory;
     dexReader.setByteOrder();
     dexSections = parseMap();
-    parseStringIDs();
+    if (parserWithStringIDs == null) {
+      parseStringIDs();
+    } else {
+      stringIDs = parserWithStringIDs.stringIDs;
+    }
     this.classKind = classKind;
     this.options = options;
   }
@@ -919,12 +935,12 @@
     return new DexSection(type, 0, 0, 0);
   }
 
-  private DexSection[] parseMap() {
+  private List<DexSection> parseMap() {
     // Read the dexSections information from the MAP.
-    int mapOffset = dexReader.getUint(Constants.MAP_OFF_OFFSET);
+    int mapOffset = dexReader.getUint(offset + Constants.MAP_OFF_OFFSET);
     dexReader.position(mapOffset);
     int mapSize = dexReader.getUint();
-    final DexSection[] result = new DexSection[mapSize];
+    final List<DexSection> result = new ArrayList<>(mapSize);
     for (int i = 0; i < mapSize; i++) {
       int type = dexReader.getUshort();
       int unused = dexReader.getUshort();
@@ -943,12 +959,12 @@
                 + dexReader.end(),
             origin);
       }
-      result[i] = new DexSection(type, unused, size, offset);
+      result.add(new DexSection(type, unused, size, offset));
     }
     for (int i = 0; i < mapSize - 1; i++) {
-      result[i].setEnd(result[i + 1].offset);
+      result.get(i).setEnd(result.get(i + 1).offset);
     }
-    result[mapSize - 1].setEnd(dexReader.end());
+    result.get(mapSize - 1).setEnd(dexReader.end());
     return result;
   }
 
diff --git a/src/main/java/com/android/tools/r8/dex/DexReader.java b/src/main/java/com/android/tools/r8/dex/DexReader.java
index 18c0beb..87829fe 100644
--- a/src/main/java/com/android/tools/r8/dex/DexReader.java
+++ b/src/main/java/com/android/tools/r8/dex/DexReader.java
@@ -25,7 +25,7 @@
 
   public DexReader(ProgramResource resource) throws ResourceException, IOException {
     super(resource);
-    version = parseMagic(buffer);
+    version = parseMagic(buffer, 0);
   }
 
   /**
@@ -35,18 +35,23 @@
    */
   DexReader(Origin origin, byte[] bytes) {
     super(origin, bytes);
-    version = parseMagic(buffer);
+    version = parseMagic(buffer, 0);
+  }
+
+  DexReader(Origin origin, byte[] bytes, int offset) {
+    super(origin, bytes);
+    version = parseMagic(buffer, offset);
   }
 
   // Parse the magic header and determine the dex file version.
-  private DexVersion parseMagic(CompatByteBuffer buffer) {
+  private DexVersion parseMagic(CompatByteBuffer buffer, int offset) {
     try {
       buffer.get();
       buffer.rewind();
     } catch (BufferUnderflowException e) {
       throw new CompilationError("Dex file is empty", origin);
     }
-    int index = 0;
+    int index = offset;
     for (byte prefixByte : DEX_FILE_MAGIC_PREFIX) {
       byte actualByte = buffer.get(index++);
       if (actualByte != prefixByte) {
diff --git a/src/main/java/com/android/tools/r8/dex/StartupMixedSectionLayoutStrategy.java b/src/main/java/com/android/tools/r8/dex/StartupMixedSectionLayoutStrategy.java
index e848e3b..c2ae2a1 100644
--- a/src/main/java/com/android/tools/r8/dex/StartupMixedSectionLayoutStrategy.java
+++ b/src/main/java/com/android/tools/r8/dex/StartupMixedSectionLayoutStrategy.java
@@ -8,7 +8,6 @@
 import com.android.tools.r8.experimental.startup.StartupProfile;
 import com.android.tools.r8.experimental.startup.profile.StartupProfileClassRule;
 import com.android.tools.r8.experimental.startup.profile.StartupProfileMethodRule;
-import com.android.tools.r8.experimental.startup.profile.StartupProfileRule;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexAnnotation;
 import com.android.tools.r8.graph.DexAnnotationDirectory;
@@ -82,14 +81,12 @@
             virtualFile.classes().size());
     LensCodeRewriterUtils rewriter = new LensCodeRewriterUtils(appView, true);
     StartupIndexedItemCollection indexedItemCollection = new StartupIndexedItemCollection();
-    for (StartupProfileRule startupItem : startupProfileForWriting.getRules()) {
-      startupItem.accept(
-          startupClass ->
-              collectStartupItems(startupClass, indexedItemCollection, virtualFileDefinitions),
-          startupMethod ->
-              collectStartupItems(
-                  startupMethod, indexedItemCollection, virtualFileDefinitions, rewriter));
-    }
+    startupProfileForWriting.forEachRule(
+        startupClass ->
+            collectStartupItems(startupClass, indexedItemCollection, virtualFileDefinitions),
+        startupMethod ->
+            collectStartupItems(
+                startupMethod, indexedItemCollection, virtualFileDefinitions, rewriter));
   }
 
   private void collectStartupItems(
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/EmptyStartupProfile.java b/src/main/java/com/android/tools/r8/experimental/startup/EmptyStartupProfile.java
index a91c273..f629224 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/EmptyStartupProfile.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/EmptyStartupProfile.java
@@ -14,8 +14,6 @@
 import com.android.tools.r8.graph.PrunedItems;
 import com.android.tools.r8.synthesis.SyntheticItems;
 import com.android.tools.r8.utils.ThrowingConsumer;
-import java.util.Collection;
-import java.util.Collections;
 
 public class EmptyStartupProfile extends StartupProfile {
 
@@ -32,9 +30,15 @@
   }
 
   @Override
+  public <E extends Exception> void forEachRule(
+      ThrowingConsumer<? super StartupProfileRule, E> consumer) {
+    // Intentionally empty.
+  }
+
+  @Override
   public <E1 extends Exception, E2 extends Exception> void forEachRule(
-      ThrowingConsumer<StartupProfileClassRule, E1> classRuleConsumer,
-      ThrowingConsumer<StartupProfileMethodRule, E2> methodRuleConsumer) {
+      ThrowingConsumer<? super StartupProfileClassRule, E1> classRuleConsumer,
+      ThrowingConsumer<? super StartupProfileMethodRule, E2> methodRuleConsumer) {
     // Intentionally empty.
   }
 
@@ -49,11 +53,6 @@
   }
 
   @Override
-  public Collection<StartupProfileRule> getRules() {
-    return Collections.emptyList();
-  }
-
-  @Override
   public boolean isEmpty() {
     return true;
   }
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupCompleteness.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupCompleteness.java
index e3699f7..f45e4c0 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupCompleteness.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupCompleteness.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.experimental.startup;
 
-import com.android.tools.r8.experimental.startup.profile.StartupProfileRule;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexProgramClass;
@@ -74,11 +73,7 @@
 
   private Set<DexReference> computeStartupItems() {
     Set<DexReference> startupItems = Sets.newIdentityHashSet();
-    for (StartupProfileRule startupItem : startupProfile.getRules()) {
-      startupItem.accept(
-          startupClass -> startupItems.add(startupClass.getReference()),
-          startupMethod -> startupItems.add(startupMethod.getReference()));
-    }
+    startupProfile.forEachRule(rule -> startupItems.add(rule.getReference()));
     return startupItems;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupProfile.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupProfile.java
index 0689d7c..747097f 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupProfile.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupProfile.java
@@ -30,6 +30,7 @@
 import com.android.tools.r8.synthesis.SyntheticItems;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.ThrowingConsumer;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.LinkedHashMap;
@@ -87,7 +88,7 @@
   public static StartupProfile merge(Collection<StartupProfile> startupProfiles) {
     Builder builder = builder();
     for (StartupProfile startupProfile : startupProfiles) {
-      startupProfile.getRules().forEach(builder::addStartupItem);
+      startupProfile.forEachRule(builder::addStartupItem);
     }
     return builder.build();
   }
@@ -131,9 +132,10 @@
     return StartupProfile.merge(startupProfiles);
   }
 
-  public abstract boolean isStartupClass(DexType type);
+  public abstract <E extends Exception> void forEachRule(
+      ThrowingConsumer<? super StartupProfileRule, E> consumer) throws E;
 
-  public abstract Collection<StartupProfileRule> getRules();
+  public abstract boolean isStartupClass(DexType type);
 
   public abstract boolean isEmpty();
 
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupProfileProviderUtils.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupProfileProviderUtils.java
index e5b6b58..ff5b0e9 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupProfileProviderUtils.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupProfileProviderUtils.java
@@ -61,10 +61,13 @@
     startupProfileProvider.getStartupProfile(startupProfileBuilder);
     // Serialize the startup items.
     StringBuilder resultBuilder = new StringBuilder();
-    for (StartupProfileRule startupItem : startupProfileBuilder.build().getRules()) {
-      startupItem.write(resultBuilder);
-      resultBuilder.append('\n');
-    }
+    StartupProfile startupProfile = startupProfileBuilder.build();
+    startupProfile.forEachRule(rule -> writeRule(rule, resultBuilder));
     return resultBuilder.toString();
   }
+
+  private static void writeRule(StartupProfileRule rule, Appendable appendable) throws IOException {
+    rule.write(appendable);
+    appendable.append('\n');
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/profile/NonEmptyStartupProfile.java b/src/main/java/com/android/tools/r8/experimental/startup/profile/NonEmptyStartupProfile.java
index 4b35a4b..4cec4e9 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/profile/NonEmptyStartupProfile.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/profile/NonEmptyStartupProfile.java
@@ -17,7 +17,6 @@
 import com.android.tools.r8.synthesis.SyntheticItems;
 import com.android.tools.r8.utils.SetUtils;
 import com.android.tools.r8.utils.ThrowingConsumer;
-import java.util.Collection;
 import java.util.LinkedHashMap;
 import java.util.Set;
 import java.util.function.BiConsumer;
@@ -45,11 +44,17 @@
   }
 
   @Override
+  public <E extends Exception> void forEachRule(
+      ThrowingConsumer<? super StartupProfileRule, E> consumer) throws E {
+    forEachRule(consumer, consumer);
+  }
+
+  @Override
   public <E1 extends Exception, E2 extends Exception> void forEachRule(
-      ThrowingConsumer<StartupProfileClassRule, E1> classRuleConsumer,
-      ThrowingConsumer<StartupProfileMethodRule, E2> methodRuleConsumer)
+      ThrowingConsumer<? super StartupProfileClassRule, E1> classRuleConsumer,
+      ThrowingConsumer<? super StartupProfileMethodRule, E2> methodRuleConsumer)
       throws E1, E2 {
-    for (StartupProfileRule rule : getRules()) {
+    for (StartupProfileRule rule : startupRules.values()) {
       rule.accept(classRuleConsumer, methodRuleConsumer);
     }
   }
@@ -65,11 +70,6 @@
   }
 
   @Override
-  public Collection<StartupProfileRule> getRules() {
-    return startupRules.values();
-  }
-
-  @Override
   public boolean isEmpty() {
     return false;
   }
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupProfileClassRule.java b/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupProfileClassRule.java
index c474ff2..8da68cf 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupProfileClassRule.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupProfileClassRule.java
@@ -33,8 +33,8 @@
 
   @Override
   public <E1 extends Exception, E2 extends Exception> void accept(
-      ThrowingConsumer<StartupProfileClassRule, E1> classConsumer,
-      ThrowingConsumer<StartupProfileMethodRule, E2> methodConsumer)
+      ThrowingConsumer<? super StartupProfileClassRule, E1> classConsumer,
+      ThrowingConsumer<? super StartupProfileMethodRule, E2> methodConsumer)
       throws E1 {
     classConsumer.accept(this);
   }
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupProfileMethodRule.java b/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupProfileMethodRule.java
index 0a3a36a..54a7b17 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupProfileMethodRule.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupProfileMethodRule.java
@@ -33,8 +33,8 @@
 
   @Override
   public <E1 extends Exception, E2 extends Exception> void accept(
-      ThrowingConsumer<StartupProfileClassRule, E1> classConsumer,
-      ThrowingConsumer<StartupProfileMethodRule, E2> methodConsumer)
+      ThrowingConsumer<? super StartupProfileClassRule, E1> classConsumer,
+      ThrowingConsumer<? super StartupProfileMethodRule, E2> methodConsumer)
       throws E2 {
     methodConsumer.accept(this);
   }
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupProfileRule.java b/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupProfileRule.java
index 200d364..64dbb2c 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupProfileRule.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupProfileRule.java
@@ -14,8 +14,8 @@
     implements AbstractProfileRule, Comparable<StartupProfileRule> {
 
   public abstract <E1 extends Exception, E2 extends Exception> void accept(
-      ThrowingConsumer<StartupProfileClassRule, E1> classConsumer,
-      ThrowingConsumer<StartupProfileMethodRule, E2> methodConsumer)
+      ThrowingConsumer<? super StartupProfileClassRule, E1> classConsumer,
+      ThrowingConsumer<? super StartupProfileMethodRule, E2> methodConsumer)
       throws E1, E2;
 
   public abstract <T> T apply(
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 d7a47a1..f9a5279 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -475,9 +475,9 @@
     return isInstanceInitializer() || willBeInlinedIntoInstanceInitializer(dexItemFactory);
   }
 
-  public boolean isDefaultInitializer() {
+  public boolean isDefaultInstanceInitializer() {
     checkIfObsolete();
-    return isInstanceInitializer() && getReference().proto.parameters.isEmpty();
+    return isInstanceInitializer() && getParameters().isEmpty();
   }
 
   public boolean isClassInitializer() {
@@ -580,10 +580,10 @@
       return false;
     } else if (accessFlags.isPrivate()) {
       return getHolderType() == other.getHolderType();
-    } else if (accessFlags.isPublic() || accessFlags.isProtected()) {
+    } else if (accessFlags.isPublic()) {
       return true;
     } else {
-      assert accessFlags.isPackagePrivate();
+      assert accessFlags.isPackagePrivate() || accessFlags.isProtected();
       return getHolderType().getPackageName().equals(other.getHolderType().getPackageName());
     }
   }
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 9cdb7f2..5c14069 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -592,6 +592,9 @@
   public final DexType javaUtilSetType = createStaticallyKnownType("Ljava/util/Set;");
 
   public final DexType androidAppActivity = createStaticallyKnownType("Landroid/app/Activity;");
+  public final DexType androidAppFragment = createStaticallyKnownType("Landroid/app/Fragment;");
+  public final DexType androidAppZygotePreload =
+      createStaticallyKnownType("Landroid/app/ZygotePreload;");
   public final DexType androidOsBuildType = createStaticallyKnownType("Landroid/os/Build;");
   public final DexType androidOsBuildVersionType =
       createStaticallyKnownType("Landroid/os/Build$VERSION;");
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArithmeticBinop.java b/src/main/java/com/android/tools/r8/ir/code/ArithmeticBinop.java
index cf6039c..f2aa7cf 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ArithmeticBinop.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ArithmeticBinop.java
@@ -12,6 +12,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.lightir.LirBuilder;
 import java.util.function.Function;
 
 public abstract class ArithmeticBinop extends Binop {
@@ -167,4 +168,9 @@
   public void buildCf(CfBuilder builder) {
     builder.add(new CfArithmeticBinop(getCfOpcode(), type), this);
   }
+
+  @Override
+  public void buildLir(LirBuilder<Value, ?> builder) {
+    builder.addArithmeticBinop(getCfOpcode(), type, leftValue(), rightValue());
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
index acc4428..ad8a742 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
@@ -17,8 +17,8 @@
 import com.android.tools.r8.dex.code.DexIputWide;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClassAndField;
 import com.android.tools.r8.graph.DexClassAndMethod;
-import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.FieldResolutionResult;
@@ -142,15 +142,21 @@
         return true;
       }
 
-      DexEncodedField encodedField = resolutionResult.getResolvedField();
-      assert encodedField != null : "NoSuchFieldError (resolution failure) should be caught.";
+      DexClassAndField field = resolutionResult.getResolutionPair();
+      assert field != null : "NoSuchFieldError (resolution failure) should be caught.";
 
-      if (encodedField.type().isAlwaysNull(appViewWithLiveness)) {
+      if (field.getType().isAlwaysNull(appViewWithLiveness)) {
         return false;
       }
 
-      return appInfoWithLiveness.isFieldRead(encodedField)
-          || isStoringObjectWithFinalizer(appViewWithLiveness, encodedField);
+      if (appView
+          .getAssumeInfoCollection()
+          .isMaterializableInAllContexts(appViewWithLiveness, field)) {
+        return false;
+      }
+
+      return appInfoWithLiveness.isFieldRead(field.getDefinition())
+          || isStoringObjectWithFinalizer(appViewWithLiveness, field.getDefinition());
     }
 
     // In D8, we always have to assume that the field can be read, and thus have side effects.
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
index 7e18e04..98c55cc 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
@@ -16,8 +16,8 @@
 import com.android.tools.r8.dex.code.DexSputWide;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClassAndField;
 import com.android.tools.r8.graph.DexClassAndMethod;
-import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.FieldResolutionResult;
@@ -119,22 +119,28 @@
         return true;
       }
 
-      DexEncodedField encodedField = resolutionResult.getResolvedField();
-      assert encodedField != null : "NoSuchFieldError (resolution failure) should be caught.";
+      DexClassAndField field = resolutionResult.getResolutionPair();
+      assert field != null : "NoSuchFieldError (resolution failure) should be caught.";
 
       boolean isDeadProtoExtensionField =
           appView.withGeneratedExtensionRegistryShrinker(
-              shrinker -> shrinker.isDeadProtoExtensionField(encodedField.getReference()), false);
+              shrinker -> shrinker.isDeadProtoExtensionField(field.getReference()), false);
       if (isDeadProtoExtensionField) {
         return false;
       }
 
-      if (encodedField.type().isAlwaysNull(appViewWithLiveness)) {
+      if (field.getType().isAlwaysNull(appViewWithLiveness)) {
         return false;
       }
 
-      return appInfoWithLiveness.isFieldRead(encodedField)
-          || isStoringObjectWithFinalizer(appViewWithLiveness, encodedField);
+      if (appView
+          .getAssumeInfoCollection()
+          .isMaterializableInAllContexts(appViewWithLiveness, field)) {
+        return false;
+      }
+
+      return appInfoWithLiveness.isFieldRead(field.getDefinition())
+          || isStoringObjectWithFinalizer(appViewWithLiveness, field.getDefinition());
     }
 
     // In D8, we always have to assume that the field can be read, and thus have side effects.
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/callgraph/CallSiteInformation.java b/src/main/java/com/android/tools/r8/ir/conversion/callgraph/CallSiteInformation.java
index 125b53c..c2e13af 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/callgraph/CallSiteInformation.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/callgraph/CallSiteInformation.java
@@ -86,7 +86,7 @@
           continue;
         }
 
-        if (method.getDefinition().isDefaultInitializer()
+        if (method.getDefinition().isDefaultInstanceInitializer()
             && appView.hasProguardCompatibilityActions()
             && appView.getProguardCompatibilityActions().isCompatInstantiated(method.getHolder())) {
           continue;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/AbstractGenerateFiles.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/AbstractGenerateFiles.java
index 4a43131..58f8ac7 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/AbstractGenerateFiles.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/AbstractGenerateFiles.java
@@ -28,8 +28,6 @@
 
 public abstract class AbstractGenerateFiles {
 
-  private static final String ANDROID_JAR_PATTERN = "third_party/android_jar/lib-v%d/android.jar";
-
   // If we increment this api level, we need to verify everything works correctly.
   static final AndroidApiLevel MAX_TESTED_ANDROID_API_LEVEL = AndroidApiLevel.U;
 
@@ -41,26 +39,33 @@
   final Path desugaredLibrarySpecificationPath;
   final Collection<Path> desugaredLibraryImplementation;
   final Path outputDirectory;
+  final Path androidJar;
 
-  public AbstractGenerateFiles(
-      String desugarConfigurationPath, String desugarImplementationPath, String outputDirectory)
+  AbstractGenerateFiles(
+      String desugarConfigurationPath,
+      String desugarImplementationPath,
+      String outputDirectory,
+      String androidJarPath)
       throws Exception {
     this(
         Paths.get(desugarConfigurationPath),
         ImmutableList.of(Paths.get(desugarImplementationPath)),
-        Paths.get(outputDirectory));
+        Paths.get(outputDirectory),
+        Paths.get(androidJarPath));
   }
 
   AbstractGenerateFiles(
       Path desugarConfigurationPath,
       Collection<Path> desugarImplementationPath,
-      Path outputDirectory)
+      Path outputDirectory,
+      Path androidJar)
       throws Exception {
+    assert androidJar != null;
     this.desugaredLibrarySpecificationPath = desugarConfigurationPath;
     DesugaredLibrarySpecification specification =
         readDesugaredLibraryConfiguration(desugarConfigurationPath);
-    Path androidJarPath = getAndroidJarPath(specification.getRequiredCompilationApiLevel());
-    DexApplication app = createApp(androidJarPath, options);
+    this.androidJar = androidJar;
+    DexApplication app = createApp(androidJar, options);
     this.desugaredLibrarySpecification = specification.toMachineSpecification(app, Timing.empty());
     this.desugaredLibraryImplementation = desugarImplementationPath;
     this.outputDirectory = outputDirectory;
@@ -69,15 +74,6 @@
     }
   }
 
-  static Path getAndroidJarPath(AndroidApiLevel apiLevel) {
-    String jar =
-        apiLevel == AndroidApiLevel.MASTER
-            ? "third_party/android_jar/lib-master/android.jar"
-            : String.format(ANDROID_JAR_PATTERN, apiLevel.getLevel());
-    return Paths.get(jar);
-  }
-
-
   private DesugaredLibrarySpecification readDesugaredLibraryConfiguration(
       Path desugarConfigurationPath) {
     return DesugaredLibrarySpecificationParser.parseDesugaredLibrarySpecification(
@@ -88,10 +84,10 @@
         AndroidApiLevel.B.getLevel());
   }
 
-  private static DexApplication createApp(Path androidLib, InternalOptions options)
+  private static DexApplication createApp(Path androidJar, InternalOptions options)
       throws IOException {
     AndroidApp.Builder builder = AndroidApp.builder();
-    AndroidApp inputApp = builder.addLibraryFiles(androidLib).build();
+    AndroidApp inputApp = builder.addLibraryFiles(androidJar).build();
     ApplicationReader applicationReader = new ApplicationReader(inputApp, options, Timing.empty());
     ExecutorService executorService = ThreadUtils.getExecutorService(options);
     assert !options.ignoreJavaLibraryOverride;
@@ -103,19 +99,43 @@
 
   abstract AndroidApiLevel run() throws Exception;
 
-  public static void main(String[] args) throws Exception {
-    if (args.length == 3) {
-      new GenerateLintFiles(args[0], args[1], args[2]).run();
-      return;
+  private static String getFallBackAndroidJarPath(AndroidApiLevel apiLevel) {
+    String jar =
+        apiLevel == AndroidApiLevel.MASTER
+            ? "third_party/android_jar/lib-master/android.jar"
+            : String.format("third_party/android_jar/lib-v%d/android.jar", apiLevel.getLevel());
+    Path jarPath = Paths.get(jar);
+    if (!Files.exists(jarPath)) {
+      throw new RuntimeException(
+          "Generate files tools should pass a valid recent android.jar as parameter if used outside"
+              + " of the r8 repository. Missing file: "
+              + jarPath);
     }
-    if (args.length == 4 && args[0].equals("--generate-api-docs")) {
-      new GenerateHtmlDoc(args[1], args[2], args[3]).run();
+    return jar;
+  }
+
+  private static String getAndroidJarPath(String[] args, int fullLength) {
+    return args.length == fullLength
+        ? args[fullLength - 1]
+        : getFallBackAndroidJarPath(MAX_TESTED_ANDROID_API_LEVEL);
+  }
+
+  public static void main(String[] args) throws Exception {
+    if (args[0].equals("--generate-api-docs")) {
+      if (args.length == 4 || args.length == 5) {
+        new GenerateHtmlDoc(args[1], args[2], args[3], getAndroidJarPath(args, 5)).run();
+        return;
+      }
+    } else if (args.length == 3 || args.length == 4) {
+      new GenerateLintFiles(args[0], args[1], args[2], getAndroidJarPath(args, 4)).run();
       return;
     }
     throw new RuntimeException(
         StringUtils.joinLines(
             "Invalid invocation.",
-            "Usage: GenerateLineFiles [--generate-api-docs] "
-                + "<desugar configuration> <desugar implementation> <output directory>"));
+            "Usage: AbstractGenerateFiles [--generate-api-docs] <desugar configuration> <desugar"
+                + " implementation> <output directory> [<android jar path for Android "
+                + MAX_TESTED_ANDROID_API_LEVEL
+                + " or higher>]"));
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateHtmlDoc.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateHtmlDoc.java
index c62d6b1..fab7228 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateHtmlDoc.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateHtmlDoc.java
@@ -37,9 +37,12 @@
   private static final String SUP_4 = "<sup>4</sup>";
 
   public GenerateHtmlDoc(
-      String desugarConfigurationPath, String desugarImplementationPath, String outputDirectory)
+      String desugarConfigurationPath,
+      String desugarImplementationPath,
+      String outputDirectory,
+      String androidJarPath)
       throws Exception {
-    super(desugarConfigurationPath, desugarImplementationPath, outputDirectory);
+    super(desugarConfigurationPath, desugarImplementationPath, outputDirectory, androidJarPath);
   }
 
   private static class StringBuilderWithIndent {
@@ -554,7 +557,7 @@
         new PrintStream(Files.newOutputStream(outputDirectory.resolve(outputFileName)));
 
     SupportedClasses supportedClasses =
-        new SupportedClassesGenerator(options)
+        new SupportedClassesGenerator(options, androidJar)
             .run(desugaredLibraryImplementation, desugaredLibrarySpecificationPath);
 
     // Full classes added.
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateLintFiles.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateLintFiles.java
index 5bfbe46..e1e72d8 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateLintFiles.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateLintFiles.java
@@ -55,22 +55,27 @@
   private static final boolean FORMAT_WITH_FIELD = true;
 
   public static GenerateLintFiles createForTesting(
-      Path specification, Set<Path> implementation, Path outputDirectory) throws Exception {
-    return new GenerateLintFiles(specification, implementation, outputDirectory);
-  }
-
-  public GenerateLintFiles(
-      String desugarConfigurationPath, String desugarImplementationPath, String outputDirectory)
+      Path specification, Set<Path> implementation, Path outputDirectory, Path androidJar)
       throws Exception {
-    super(desugarConfigurationPath, desugarImplementationPath, outputDirectory);
+    return new GenerateLintFiles(specification, implementation, outputDirectory, androidJar);
   }
 
   public GenerateLintFiles(
+      String desugarConfigurationPath,
+      String desugarImplementationPath,
+      String outputDirectory,
+      String androidJarPath)
+      throws Exception {
+    super(desugarConfigurationPath, desugarImplementationPath, outputDirectory, androidJarPath);
+  }
+
+  private GenerateLintFiles(
       Path desugarConfigurationPath,
       Collection<Path> desugarImplementationPath,
-      Path outputDirectory)
+      Path outputDirectory,
+      Path androidJar)
       throws Exception {
-    super(desugarConfigurationPath, desugarImplementationPath, outputDirectory);
+    super(desugarConfigurationPath, desugarImplementationPath, outputDirectory, androidJar);
   }
 
   private CfCode buildEmptyThrowingCfCode(DexMethod method) {
@@ -256,11 +261,17 @@
     AndroidApiLevel compilationLevel =
         desugaredLibrarySpecification.getRequiredCompilationApiLevel();
     SupportedClasses supportedMethods =
-        new SupportedClassesGenerator(options)
+        new SupportedClassesGenerator(options, androidJar)
             .run(desugaredLibraryImplementation, desugaredLibrarySpecificationPath);
-    System.out.println("Generating lint files for compile API " + compilationLevel);
+    System.out.println(
+        "Generating lint files for "
+            + desugaredLibrarySpecification.getIdentifier()
+            + " (compile API "
+            + compilationLevel
+            + ")");
     generateLintFiles(compilationLevel, AndroidApiLevel.B, supportedMethods);
     generateLintFiles(compilationLevel, AndroidApiLevel.L, supportedMethods);
+    System.out.println();
     return compilationLevel;
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/SupportedClassesGenerator.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/SupportedClassesGenerator.java
index cb24e59..f384fd7 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/SupportedClassesGenerator.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/SupportedClassesGenerator.java
@@ -10,8 +10,9 @@
 import com.android.tools.r8.androidapi.ComputedApiLevel;
 import com.android.tools.r8.dex.ApplicationReader;
 import com.android.tools.r8.dex.Constants;
-import com.android.tools.r8.features.ClassToFeatureSplitMap;
+import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedField;
@@ -28,14 +29,12 @@
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.MethodResolutionResult;
 import com.android.tools.r8.ir.desugar.BackportedMethodRewriter;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAmender;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecificationParser;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.lint.SupportedClasses.ClassAnnotation;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.lint.SupportedClasses.FieldAnnotation;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.lint.SupportedClasses.MethodAnnotation;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
-import com.android.tools.r8.shaking.MainDexInfo;
 import com.android.tools.r8.synthesis.SyntheticItems.GlobalSyntheticsStrategy;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
@@ -46,7 +45,6 @@
 import com.google.common.collect.Sets;
 import java.io.IOException;
 import java.nio.file.Path;
-import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
@@ -55,35 +53,28 @@
 
 public class SupportedClassesGenerator {
 
-  private static final String ANDROID_JAR_PATTERN = "third_party/android_jar/lib-v%d/android.jar";
-
   private final InternalOptions options;
+  private final DirectMappedDexApplication appForMax;
+  private final SupportedClasses.Builder builder = SupportedClasses.builder();
 
-  public SupportedClassesGenerator(InternalOptions options) {
+  public SupportedClassesGenerator(InternalOptions options, Path androidJar) throws IOException {
     this.options = options;
+    this.appForMax = createAppForMax(androidJar);
   }
 
   public SupportedClasses run(Collection<Path> desugaredLibraryImplementation, Path specification)
       throws IOException {
-    SupportedClasses.Builder builder = SupportedClasses.builder();
     // First analyze everything which is supported when desugaring for api 1.
-    collectSupportedMembersInB(desugaredLibraryImplementation, specification, builder);
+    collectSupportedMembersInB(desugaredLibraryImplementation, specification);
     // Second annotate all apis which are partially and/or fully supported.
-    AndroidApp library =
-        AndroidApp.builder()
-            .addProgramFiles(getAndroidJarPath(MAX_TESTED_ANDROID_API_LEVEL))
-            .build();
-    DirectMappedDexApplication appForMax =
-        new ApplicationReader(library, options, Timing.empty()).read().toDirect();
-    annotateMethodsNotOnLatestAndroidJar(appForMax, builder);
-    annotateParallelMethods(builder);
-    annotatePartialDesugaringMembers(builder, specification);
-    annotateClasses(builder, appForMax);
+    annotateMethodsNotOnLatestAndroidJar();
+    annotateParallelMethods();
+    annotatePartialDesugaringMembers(specification);
+    annotateClasses();
     return builder.build();
   }
 
-  private void annotateClasses(
-      SupportedClasses.Builder builder, DirectMappedDexApplication appForMax) {
+  private void annotateClasses() {
     builder.forEachClassFieldsAndMethods(
         (clazz, fields, methods) -> {
           ClassAnnotation classAnnotation = builder.getClassAnnotation(clazz.type);
@@ -123,34 +114,27 @@
     return fullySupported;
   }
 
-  private void annotatePartialDesugaringMembers(
-      SupportedClasses.Builder builder, Path specification) throws IOException {
-    for (int api = AndroidApiLevel.K.getLevel();
+  private void annotatePartialDesugaringMembers(Path specification) throws IOException {
+    // The first difference should be at 18 so we're safe starting at J and not B.
+    for (int api = AndroidApiLevel.J.getLevel();
         api <= MAX_TESTED_ANDROID_API_LEVEL.getLevel();
         api++) {
-      if (api == 20) {
-        // Missing android.jar.
-        continue;
-      }
       AndroidApiLevel androidApiLevel = AndroidApiLevel.getAndroidApiLevel(api);
       MachineDesugaredLibrarySpecification machineSpecification =
           getMachineSpecification(androidApiLevel, specification);
       options.setMinApiLevel(androidApiLevel);
       options.resetDesugaredLibrarySpecificationForTesting();
       options.setDesugaredLibrarySpecification(machineSpecification);
-      AndroidApp library =
-          AndroidApp.builder().addProgramFiles(getAndroidJarPath(androidApiLevel)).build();
-      DirectMappedDexApplication dexApplication =
-          new ApplicationReader(library, options, Timing.empty()).read().toDirect();
-      AppInfoWithClassHierarchy appInfo =
-          AppInfoWithClassHierarchy.createInitialAppInfoWithClassHierarchy(
-              dexApplication,
-              ClassToFeatureSplitMap.createEmptyClassToFeatureSplitMap(),
-              MainDexInfo.none(),
-              GlobalSyntheticsStrategy.forNonSynthesizing());
 
+      AppInfo initialAppInfo =
+          AppInfo.createInitialAppInfo(appForMax, GlobalSyntheticsStrategy.forNonSynthesizing());
+      AppView<?> appView =
+          AppView.createForD8(initialAppInfo, options.getTypeRewriter(), Timing.empty());
+      AppInfoWithClassHierarchy appInfo = appView.appInfoForDesugaring();
+
+      // This should depend only on machine specification and min api.
       List<DexMethod> backports =
-          BackportedMethodRewriter.generateListOfBackportedMethods(dexApplication, options);
+          BackportedMethodRewriter.generateListOfBackportedMethods(appForMax, options);
 
       int finalApi = api;
       builder.forEachClassAndMethod(
@@ -170,13 +154,31 @@
               // interface is supported.
               return;
             }
+            // This is not supported through desugared library. We look-up to see if it is
+            // supported at the given min-api level by the library.
             MethodResolutionResult methodResolutionResult =
                 appInfo.resolveMethod(
                     dexMethod,
                     appInfo
                         .contextIndependentDefinitionFor(dexMethod.getHolderType())
                         .isInterface());
-            if (methodResolutionResult.isFailedResolution()) {
+            if (methodResolutionResult.isSingleResolution()) {
+              ComputedApiLevel computedApiLevel =
+                  appView
+                      .apiLevelCompute()
+                      .computeApiLevelForLibraryReferenceIgnoringDesugaredLibrary(
+                          methodResolutionResult.getResolvedMethod().getReference(),
+                          ComputedApiLevel.unknown());
+              if (!computedApiLevel.isKnownApiLevel()) {
+                throw new RuntimeException(
+                    "API database does not recognize the method "
+                        + encodedMethod.getReference().toSourceString());
+              }
+              if (finalApi < computedApiLevel.asKnownApiLevel().getApiLevel().getLevel()) {
+                builder.annotateMethod(dexMethod, MethodAnnotation.createMissingInMinApi(finalApi));
+              }
+            } else {
+              assert methodResolutionResult.isFailedResolution();
               builder.annotateMethod(dexMethod, MethodAnnotation.createMissingInMinApi(finalApi));
             }
           });
@@ -192,7 +194,24 @@
             }
             FieldResolutionResult fieldResolutionResult =
                 appInfo.resolveField(encodedField.getReference());
-            if (fieldResolutionResult.isFailedResolution()) {
+            if (fieldResolutionResult.isSingleFieldResolutionResult()) {
+              DexEncodedField resolvedField = fieldResolutionResult.getResolvedField();
+              ComputedApiLevel computedApiLevel =
+                  appView
+                      .apiLevelCompute()
+                      .computeApiLevelForLibraryReferenceIgnoringDesugaredLibrary(
+                          resolvedField.getReference(), ComputedApiLevel.unknown());
+              if (!computedApiLevel.isKnownApiLevel()) {
+                throw new RuntimeException(
+                    "API database does not recognize the field "
+                        + encodedField.getReference().toSourceString());
+              }
+              if (finalApi < computedApiLevel.asKnownApiLevel().getApiLevel().getLevel()) {
+                builder.annotateField(
+                    encodedField.getReference(), FieldAnnotation.createMissingInMinApi(finalApi));
+              }
+            } else {
+              assert fieldResolutionResult.isFailedResolution();
               builder.annotateField(
                   encodedField.getReference(), FieldAnnotation.createMissingInMinApi(finalApi));
             }
@@ -200,14 +219,13 @@
     }
   }
 
-  private void annotateParallelMethods(SupportedClasses.Builder builder) {
+  private void annotateParallelMethods() {
     for (DexMethod parallelMethod : getParallelMethods()) {
       builder.annotateMethodIfPresent(parallelMethod, MethodAnnotation.getParallelStreamMethod());
     }
   }
 
-  private void annotateMethodsNotOnLatestAndroidJar(
-      DirectMappedDexApplication appForMax, SupportedClasses.Builder builder) {
+  private void annotateMethodsNotOnLatestAndroidJar() {
     builder.forEachClassAndMethod(
         (clazz, method) -> {
           DexClass dexClass = appForMax.definitionFor(clazz.type);
@@ -219,19 +237,8 @@
         });
   }
 
-  static Path getAndroidJarPath(AndroidApiLevel apiLevel) {
-    String jar =
-        apiLevel == AndroidApiLevel.MASTER
-            ? "third_party/android_jar/lib-master/android.jar"
-            : String.format(ANDROID_JAR_PATTERN, apiLevel.getLevel());
-    return Paths.get(jar);
-  }
-
   private void collectSupportedMembersInB(
-      Collection<Path> desugaredLibraryImplementation,
-      Path specification,
-      SupportedClasses.Builder builder)
-      throws IOException {
+      Collection<Path> desugaredLibraryImplementation, Path specification) throws IOException {
 
     MachineDesugaredLibrarySpecification machineSpecification =
         getMachineSpecification(AndroidApiLevel.B, specification);
@@ -245,22 +252,8 @@
     DirectMappedDexApplication implementationApplication =
         new ApplicationReader(implementation, options, Timing.empty()).read().toDirect();
 
-    AndroidApp library =
-        AndroidApp.builder()
-            .addLibraryFiles(getAndroidJarPath(MAX_TESTED_ANDROID_API_LEVEL))
-            .build();
-    DirectMappedDexApplication amendedAppForMax =
-        new ApplicationReader(library, options, Timing.empty()).read().toDirect();
-
     List<DexMethod> backports =
-        BackportedMethodRewriter.generateListOfBackportedMethods(amendedAppForMax, options);
-
-    DesugaredLibraryAmender.run(
-        machineSpecification.getAmendLibraryMethods(),
-        machineSpecification.getAmendLibraryFields(),
-        amendedAppForMax,
-        options.reporter,
-        ComputedApiLevel.unknown());
+        BackportedMethodRewriter.generateListOfBackportedMethods(appForMax, options);
 
     for (DexProgramClass clazz : implementationApplication.classes()) {
       // All emulated interfaces static and default methods are supported.
@@ -285,20 +278,20 @@
           }
           builder.addSupportedMethod(clazz, method);
         }
-        addBackports(clazz, backports, builder, amendedAppForMax);
+        addBackports(clazz, backports);
         builder.annotateClass(clazz.type, ClassAnnotation.getAdditionnalMembersOnClass());
       } else {
         // All methods in maintained or rewritten classes are supported.
         if ((clazz.accessFlags.isPublic() || clazz.accessFlags.isProtected())
             && machineSpecification.isContextTypeMaintainedOrRewritten(clazz.type)
-            && amendedAppForMax.definitionFor(clazz.type) != null) {
+            && appForMax.definitionFor(clazz.type) != null) {
           for (DexEncodedMethod method : clazz.methods()) {
             if (!method.isPublic() && !method.isProtectedMethod()) {
               continue;
             }
             builder.addSupportedMethod(clazz, method);
           }
-          addBackports(clazz, backports, builder, amendedAppForMax);
+          addBackports(clazz, backports);
           for (DexEncodedField field : clazz.fields()) {
             if (!field.isPublic() && !field.isProtected()) {
               continue;
@@ -321,7 +314,7 @@
               return;
             }
           }
-          dexClass = amendedAppForMax.definitionFor(method.getHolderType());
+          dexClass = appForMax.definitionFor(method.getHolderType());
           DexEncodedMethod dexEncodedMethod = dexClass.lookupMethod(method);
           assert dexEncodedMethod != null;
           builder.addSupportedMethod(dexClass, dexEncodedMethod);
@@ -342,7 +335,7 @@
                   return;
                 }
               }
-              dexClass = amendedAppForMax.definitionFor(field.getHolderType());
+              dexClass = appForMax.definitionFor(field.getHolderType());
               DexEncodedField dexEncodedField = dexClass.lookupField(field);
               assert dexEncodedField != null;
               builder.addSupportedField(dexClass, dexEncodedField);
@@ -350,14 +343,10 @@
             });
   }
 
-  private void addBackports(
-      DexProgramClass clazz,
-      List<DexMethod> backports,
-      SupportedClasses.Builder builder,
-      DirectMappedDexApplication amendedAppForMax) {
+  private void addBackports(DexProgramClass clazz, List<DexMethod> backports) {
     for (DexMethod backport : backports) {
       if (clazz.type == backport.getHolderType()) {
-        DexClass maxClass = amendedAppForMax.definitionFor(clazz.type);
+        DexClass maxClass = appForMax.definitionFor(clazz.type);
         DexEncodedMethod dexEncodedMethod = maxClass.lookupMethod(backport);
         // Some backports are not in amendedAppForMax, such as Stream#ofNullable and recent ones
         // introduced in U.
@@ -402,22 +391,19 @@
             options.reporter,
             false,
             api.getLevel());
-    Path androidJarPath = getAndroidJarPath(librarySpecification.getRequiredCompilationApiLevel());
-    DexApplication app = createLoadingApp(androidJarPath, options);
-    return librarySpecification.toMachineSpecification(app, Timing.empty());
+    return librarySpecification.toMachineSpecification(appForMax, Timing.empty());
   }
 
-  private DexApplication createLoadingApp(Path androidLib, InternalOptions options)
-      throws IOException {
+  private DirectMappedDexApplication createAppForMax(Path androidJar) throws IOException {
     AndroidApp.Builder builder = AndroidApp.builder();
-    AndroidApp inputApp = builder.addLibraryFiles(androidLib).build();
+    AndroidApp inputApp = builder.addLibraryFiles(androidJar).build();
     ApplicationReader applicationReader = new ApplicationReader(inputApp, options, Timing.empty());
     ExecutorService executorService = ThreadUtils.getExecutorService(options);
     assert !options.ignoreJavaLibraryOverride;
     options.ignoreJavaLibraryOverride = true;
-    DexApplication loadingApp = applicationReader.read(executorService);
+    DexApplication appForMax = applicationReader.read(executorService);
     options.ignoreJavaLibraryOverride = false;
-    return loadingApp;
+    return appForMax.toDirect();
   }
 
   private Set<DexMethod> getParallelMethods() {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/membervaluepropagation/R8MemberValuePropagation.java b/src/main/java/com/android/tools/r8/ir/optimize/membervaluepropagation/R8MemberValuePropagation.java
index 587a78c..1810750 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/membervaluepropagation/R8MemberValuePropagation.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/membervaluepropagation/R8MemberValuePropagation.java
@@ -342,19 +342,23 @@
 
   private void replaceInstancePutByNullCheckIfNeverRead(
       IRCode code, InstructionListIterator iterator, InstancePut current) {
-    DexEncodedField field = appView.appInfo().resolveField(current.getField()).getResolvedField();
-    if (field == null || field.isStatic()) {
+    DexClassAndField field = appView.appInfo().resolveField(current.getField()).getResolutionPair();
+    if (field == null || field.getAccessFlags().isStatic()) {
       return;
     }
 
     // If the field is read, we can't remove the instance-put unless the value of the field is known
     // to be null (in which case the instance-put is a no-op because it assigns the field the same
     // value as its default value).
-    if (!field.type().isAlwaysNull(appView) && appView.appInfo().isFieldRead(field)) {
+    if (field.getType().isAlwaysNull(appView)
+        || !appView.appInfo().isFieldRead(field.getDefinition())) {
+      iterator.replaceCurrentInstructionByNullCheckIfPossible(appView, code.context());
       return;
     }
 
-    iterator.replaceCurrentInstructionByNullCheckIfPossible(appView, code.context());
+    if (appView.getAssumeInfoCollection().isMaterializableInAllContexts(appView, field)) {
+      iterator.replaceCurrentInstructionByNullCheckIfPossible(appView, code.context());
+    }
   }
 
   @Override
@@ -364,19 +368,24 @@
 
   private void replaceStaticPutByInitClassIfNeverRead(
       IRCode code, InstructionListIterator iterator, StaticPut current) {
-    DexEncodedField field = appView.appInfo().resolveField(current.getField()).getResolvedField();
-    if (field == null || !field.isStatic()) {
+    DexClassAndField field = appView.appInfo().resolveField(current.getField()).getResolutionPair();
+    if (field == null || !field.getAccessFlags().isStatic()) {
       return;
     }
 
     // If the field is read, we can't remove the static-put unless the value of the field is known
     // to be null (in which case the static-put is a no-op because it assigns the field the same
     // value as its default value).
-    if (!field.type().isAlwaysNull(appView) && appView.appInfo().isFieldRead(field)) {
+    if (field.getType().isAlwaysNull(appView)
+        || !appView.appInfo().isFieldRead(field.getDefinition())) {
+      iterator.removeOrReplaceCurrentInstructionByInitClassIfPossible(
+          appView, code, field.getHolderType());
       return;
     }
 
-    iterator.removeOrReplaceCurrentInstructionByInitClassIfPossible(
-        appView, code, field.getHolderType());
+    if (appView.getAssumeInfoCollection().isMaterializableInAllContexts(appView, field)) {
+      iterator.removeOrReplaceCurrentInstructionByInitClassIfPossible(
+          appView, code, field.getHolderType());
+    }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/lightir/ByteUtils.java b/src/main/java/com/android/tools/r8/lightir/ByteUtils.java
index 5d34483..dc174ff 100644
--- a/src/main/java/com/android/tools/r8/lightir/ByteUtils.java
+++ b/src/main/java/com/android/tools/r8/lightir/ByteUtils.java
@@ -17,6 +17,10 @@
     return value & 0xFF;
   }
 
+  private static int truncateToU1(long value) {
+    return (int) value & 0xFF;
+  }
+
   public static int ensureU1(int value) {
     assert isU1(value);
     return truncateToU1(value);
@@ -47,6 +51,35 @@
     return value;
   }
 
+  public static int longEncodingSize(long value) {
+    return 8;
+  }
+
+  public static void writeEncodedLong(long value, ByteWriter writer) {
+    assert 8 == longEncodingSize(value);
+    writer.put(truncateToU1(value >> 56));
+    writer.put(truncateToU1(value >> 48));
+    writer.put(truncateToU1(value >> 40));
+    writer.put(truncateToU1(value >> 32));
+    writer.put(truncateToU1(value >> 24));
+    writer.put(truncateToU1(value >> 16));
+    writer.put(truncateToU1(value >> 8));
+    writer.put(truncateToU1(value));
+  }
+
+  public static long readEncodedLong(ByteIterator iterator) {
+    assert 8 == longEncodingSize(0);
+    long value = ((long) truncateToU1(iterator.nextByte())) << 56;
+    value |= ((long) truncateToU1(iterator.nextByte())) << 48;
+    value |= ((long) truncateToU1(iterator.nextByte())) << 40;
+    value |= ((long) truncateToU1(iterator.nextByte())) << 32;
+    value |= ((long) truncateToU1(iterator.nextByte())) << 24;
+    value |= ((long) truncateToU1(iterator.nextByte())) << 16;
+    value |= ((long) truncateToU1(iterator.nextByte())) << 8;
+    value |= truncateToU1(iterator.nextByte());
+    return value;
+  }
+
   public static boolean isU2(int value) {
     return (value >= 0) && (value <= 0xFFFF);
   }
diff --git a/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java b/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java
index ebaa39c..78e84db 100644
--- a/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java
+++ b/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java
@@ -12,6 +12,7 @@
 import com.android.tools.r8.graph.ProgramMethod;
 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.Add;
 import com.android.tools.r8.ir.code.Argument;
 import com.android.tools.r8.ir.code.ArrayLength;
 import com.android.tools.r8.ir.code.BasicBlock;
@@ -36,14 +37,17 @@
 import com.android.tools.r8.ir.code.InvokeSuper;
 import com.android.tools.r8.ir.code.InvokeVirtual;
 import com.android.tools.r8.ir.code.MoveException;
+import com.android.tools.r8.ir.code.Mul;
 import com.android.tools.r8.ir.code.NewInstance;
 import com.android.tools.r8.ir.code.NumberGenerator;
 import com.android.tools.r8.ir.code.NumericType;
 import com.android.tools.r8.ir.code.Phi;
 import com.android.tools.r8.ir.code.Position;
 import com.android.tools.r8.ir.code.Position.SyntheticPosition;
+import com.android.tools.r8.ir.code.Rem;
 import com.android.tools.r8.ir.code.Return;
 import com.android.tools.r8.ir.code.StaticGet;
+import com.android.tools.r8.ir.code.Sub;
 import com.android.tools.r8.ir.code.Throw;
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
@@ -334,10 +338,33 @@
     }
 
     @Override
-    public void onDivInt(EV leftValueIndex, EV rightValueIndex) {
+    public void onAdd(NumericType type, EV leftValueIndex, EV rightValueIndex) {
       Value dest = getOutValueForNextInstruction(TypeElement.getInt());
-      addInstruction(
-          new Div(NumericType.INT, dest, getValue(leftValueIndex), getValue(rightValueIndex)));
+      addInstruction(new Add(type, dest, getValue(leftValueIndex), getValue(rightValueIndex)));
+    }
+
+    @Override
+    public void onSub(NumericType type, EV leftValueIndex, EV rightValueIndex) {
+      Value dest = getOutValueForNextInstruction(TypeElement.getInt());
+      addInstruction(new Sub(type, dest, getValue(leftValueIndex), getValue(rightValueIndex)));
+    }
+
+    @Override
+    public void onMul(NumericType type, EV leftValueIndex, EV rightValueIndex) {
+      Value dest = getOutValueForNextInstruction(TypeElement.getInt());
+      addInstruction(new Mul(type, dest, getValue(leftValueIndex), getValue(rightValueIndex)));
+    }
+
+    @Override
+    public void onDiv(NumericType type, EV leftValueIndex, EV rightValueIndex) {
+      Value dest = getOutValueForNextInstruction(TypeElement.getInt());
+      addInstruction(new Div(type, dest, getValue(leftValueIndex), getValue(rightValueIndex)));
+    }
+
+    @Override
+    public void onRem(NumericType type, EV leftValueIndex, EV rightValueIndex) {
+      Value dest = getOutValueForNextInstruction(TypeElement.getInt());
+      addInstruction(new Rem(type, dest, getValue(leftValueIndex), getValue(rightValueIndex)));
     }
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/lightir/LirBuilder.java b/src/main/java/com/android/tools/r8/lightir/LirBuilder.java
index 37806dd..024d6d7 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirBuilder.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirBuilder.java
@@ -3,6 +3,8 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.lightir;
 
+import com.android.tools.r8.cf.code.CfArithmeticBinop;
+import com.android.tools.r8.cf.code.CfArithmeticBinop.Opcode;
 import com.android.tools.r8.errors.Unimplemented;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DebugLocalInfo;
@@ -42,6 +44,11 @@
 
 /** Builder for constructing LIR code from IR. */
 public class LirBuilder<V, EV> {
+  private static final int FLOAT_0 = Float.floatToRawIntBits(0);
+  private static final int FLOAT_1 = Float.floatToRawIntBits(1);
+  private static final int FLOAT_2 = Float.floatToRawIntBits(2);
+  private static final long DOUBLE_0 = Double.doubleToRawLongBits(0);
+  private static final long DOUBLE_1 = Double.doubleToRawLongBits(1);
 
   private final DexItemFactory factory;
   private final ByteArrayWriter byteWriter = new ByteArrayWriter();
@@ -254,6 +261,48 @@
     return this;
   }
 
+  public LirBuilder<V, EV> addConstFloat(int value) {
+    if (value == FLOAT_0) {
+      return addNoOperandInstruction(LirOpcodes.FCONST_0);
+    }
+    if (value == FLOAT_1) {
+      return addNoOperandInstruction(LirOpcodes.FCONST_1);
+    }
+    if (value == FLOAT_2) {
+      return addNoOperandInstruction(LirOpcodes.FCONST_2);
+    }
+    advanceInstructionState();
+    writer.writeInstruction(LirOpcodes.FCONST, ByteUtils.intEncodingSize(value));
+    ByteUtils.writeEncodedInt(value, writer::writeOperand);
+    return this;
+  }
+
+  public LirBuilder<V, EV> addConstLong(long value) {
+    if (value == 0) {
+      return addNoOperandInstruction(LirOpcodes.LCONST_0);
+    }
+    if (value == 1) {
+      return addNoOperandInstruction(LirOpcodes.LCONST_1);
+    }
+    advanceInstructionState();
+    writer.writeInstruction(LirOpcodes.LCONST, ByteUtils.longEncodingSize(value));
+    ByteUtils.writeEncodedLong(value, writer::writeOperand);
+    return this;
+  }
+
+  public LirBuilder<V, EV> addConstDouble(long value) {
+    if (value == DOUBLE_0) {
+      return addNoOperandInstruction(LirOpcodes.DCONST_0);
+    }
+    if (value == DOUBLE_1) {
+      return addNoOperandInstruction(LirOpcodes.DCONST_1);
+    }
+    advanceInstructionState();
+    writer.writeInstruction(LirOpcodes.DCONST, ByteUtils.longEncodingSize(value));
+    ByteUtils.writeEncodedLong(value, writer::writeOperand);
+    return this;
+  }
+
   public LirBuilder<V, EV> addConstNumber(ValueType type, long value) {
     switch (type) {
       case OBJECT:
@@ -261,8 +310,11 @@
       case INT:
         return addConstInt((int) value);
       case FLOAT:
+        return addConstFloat((int) value);
       case LONG:
+        return addConstLong(value);
       case DOUBLE:
+        return addConstDouble(value);
       default:
         throw new Unimplemented();
     }
@@ -495,4 +547,13 @@
   public LirBuilder<V, EV> addCmp(NumericType type, Bias bias, V leftValue, V rightValue) {
     return addTwoValueInstruction(getCmpOpcode(type, bias), leftValue, rightValue);
   }
+
+  public LirBuilder<V, EV> addArithmeticBinop(
+      Opcode binop, NumericType type, V leftValue, V rightValue) {
+    // The LIR and CF opcodes are the same values, check that the two endpoints match.
+    assert LirOpcodes.IADD == CfArithmeticBinop.getAsmOpcode(Opcode.Add, NumericType.INT);
+    assert LirOpcodes.DREM == CfArithmeticBinop.getAsmOpcode(Opcode.Rem, NumericType.DOUBLE);
+    int opcode = CfArithmeticBinop.getAsmOpcode(binop, type);
+    return addTwoValueInstruction(opcode, leftValue, rightValue);
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/lightir/LirInstructionView.java b/src/main/java/com/android/tools/r8/lightir/LirInstructionView.java
index 3eef40d..546da90 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirInstructionView.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirInstructionView.java
@@ -30,6 +30,9 @@
   /** Get the next operand as an encoded integer */
   int getNextIntegerOperand();
 
+  /** Get the next operand as an encoded long */
+  long getNextLongOperand();
+
   /** Get the next operand as a constant-pool index. */
   int getNextConstantOperand();
 
diff --git a/src/main/java/com/android/tools/r8/lightir/LirIterator.java b/src/main/java/com/android/tools/r8/lightir/LirIterator.java
index dd29c93..84eaee6 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirIterator.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirIterator.java
@@ -88,6 +88,12 @@
   }
 
   @Override
+  public long getNextLongOperand() {
+    assert hasMoreOperands();
+    return u8();
+  }
+
+  @Override
   public int getNextConstantOperand() {
     return getNextIntegerOperand();
   }
@@ -116,4 +122,9 @@
     currentByteIndex += 4;
     return ByteUtils.readEncodedInt(iterator);
   }
+
+  private long u8() {
+    currentByteIndex += 8;
+    return ByteUtils.readEncodedLong(iterator);
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/lightir/LirParsedInstructionCallback.java b/src/main/java/com/android/tools/r8/lightir/LirParsedInstructionCallback.java
index 96cb7ee..925e8af 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirParsedInstructionCallback.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirParsedInstructionCallback.java
@@ -62,10 +62,82 @@
     onConstNumber(NumericType.INT, value);
   }
 
+  public void onConstFloat(int value) {
+    onConstNumber(NumericType.FLOAT, value);
+  }
+
+  public void onConstLong(long value) {
+    onConstNumber(NumericType.LONG, value);
+  }
+
+  public void onConstDouble(long value) {
+    onConstNumber(NumericType.DOUBLE, value);
+  }
+
   public void onConstString(DexString string) {
     onInstruction();
   }
 
+  public void onAdd(NumericType type, EV leftValueIndex, EV rightValueIndex) {
+    onInstruction();
+  }
+
+  public void onAddInt(EV leftValueIndex, EV rightValueIndex) {
+    onAdd(NumericType.INT, leftValueIndex, rightValueIndex);
+  }
+
+  public void onAddLong(EV leftValueIndex, EV rightValueIndex) {
+    onAdd(NumericType.LONG, leftValueIndex, rightValueIndex);
+  }
+
+  public void onAddFloat(EV leftValueIndex, EV rightValueIndex) {
+    onAdd(NumericType.FLOAT, leftValueIndex, rightValueIndex);
+  }
+
+  public void onAddDouble(EV leftValueIndex, EV rightValueIndex) {
+    onAdd(NumericType.DOUBLE, leftValueIndex, rightValueIndex);
+  }
+
+  public void onSub(NumericType type, EV leftValueIndex, EV rightValueIndex) {
+    onInstruction();
+  }
+
+  public void onSubInt(EV leftValueIndex, EV rightValueIndex) {
+    onSub(NumericType.INT, leftValueIndex, rightValueIndex);
+  }
+
+  public void onSubLong(EV leftValueIndex, EV rightValueIndex) {
+    onSub(NumericType.LONG, leftValueIndex, rightValueIndex);
+  }
+
+  public void onSubFloat(EV leftValueIndex, EV rightValueIndex) {
+    onSub(NumericType.FLOAT, leftValueIndex, rightValueIndex);
+  }
+
+  public void onSubDouble(EV leftValueIndex, EV rightValueIndex) {
+    onSub(NumericType.DOUBLE, leftValueIndex, rightValueIndex);
+  }
+
+  public void onMul(NumericType type, EV leftValueIndex, EV rightValueIndex) {
+    onInstruction();
+  }
+
+  public void onMulInt(EV leftValueIndex, EV rightValueIndex) {
+    onMul(NumericType.INT, leftValueIndex, rightValueIndex);
+  }
+
+  public void onMulLong(EV leftValueIndex, EV rightValueIndex) {
+    onMul(NumericType.LONG, leftValueIndex, rightValueIndex);
+  }
+
+  public void onMulFloat(EV leftValueIndex, EV rightValueIndex) {
+    onMul(NumericType.FLOAT, leftValueIndex, rightValueIndex);
+  }
+
+  public void onMulDouble(EV leftValueIndex, EV rightValueIndex) {
+    onMul(NumericType.DOUBLE, leftValueIndex, rightValueIndex);
+  }
+
   public void onDiv(NumericType type, EV leftValueIndex, EV rightValueIndex) {
     onInstruction();
   }
@@ -74,6 +146,38 @@
     onDiv(NumericType.INT, leftValueIndex, rightValueIndex);
   }
 
+  public void onDivLong(EV leftValueIndex, EV rightValueIndex) {
+    onDiv(NumericType.LONG, leftValueIndex, rightValueIndex);
+  }
+
+  public void onDivFloat(EV leftValueIndex, EV rightValueIndex) {
+    onDiv(NumericType.FLOAT, leftValueIndex, rightValueIndex);
+  }
+
+  public void onDivDouble(EV leftValueIndex, EV rightValueIndex) {
+    onDiv(NumericType.DOUBLE, leftValueIndex, rightValueIndex);
+  }
+
+  public void onRem(NumericType type, EV leftValueIndex, EV rightValueIndex) {
+    onInstruction();
+  }
+
+  public void onRemInt(EV leftValueIndex, EV rightValueIndex) {
+    onRem(NumericType.INT, leftValueIndex, rightValueIndex);
+  }
+
+  public void onRemLong(EV leftValueIndex, EV rightValueIndex) {
+    onRem(NumericType.LONG, leftValueIndex, rightValueIndex);
+  }
+
+  public void onRemFloat(EV leftValueIndex, EV rightValueIndex) {
+    onRem(NumericType.FLOAT, leftValueIndex, rightValueIndex);
+  }
+
+  public void onRemDouble(EV leftValueIndex, EV rightValueIndex) {
+    onRem(NumericType.DOUBLE, leftValueIndex, rightValueIndex);
+  }
+
   public void onIf(IfType ifKind, int blockIndex, EV valueIndex) {
     onInstruction();
   }
@@ -197,6 +301,136 @@
           onConstInt(value);
           return;
         }
+      case LirOpcodes.FCONST_0:
+      case LirOpcodes.FCONST_1:
+      case LirOpcodes.FCONST_2:
+        {
+          float value = opcode - LirOpcodes.FCONST_0;
+          onConstFloat(Float.floatToRawIntBits(value));
+          return;
+        }
+      case LirOpcodes.FCONST:
+        {
+          int value = view.getNextIntegerOperand();
+          onConstFloat(value);
+          return;
+        }
+      case LirOpcodes.LCONST_0:
+        {
+          onConstLong(0);
+          return;
+        }
+      case LirOpcodes.LCONST_1:
+        {
+          onConstLong(1);
+          return;
+        }
+      case LirOpcodes.LCONST:
+        {
+          long value = view.getNextLongOperand();
+          onConstLong(value);
+          return;
+        }
+      case LirOpcodes.DCONST_0:
+        {
+          onConstDouble(Double.doubleToRawLongBits(0));
+          return;
+        }
+      case LirOpcodes.DCONST_1:
+        {
+          onConstDouble(Double.doubleToRawLongBits(1));
+          return;
+        }
+      case LirOpcodes.DCONST:
+        {
+          long value = view.getNextLongOperand();
+          onConstDouble(value);
+          return;
+        }
+      case LirOpcodes.IADD:
+        {
+          EV leftValueIndex = getNextValueOperand(view);
+          EV rightValueIndex = getNextValueOperand(view);
+          onAddInt(leftValueIndex, rightValueIndex);
+          return;
+        }
+      case LirOpcodes.LADD:
+        {
+          EV leftValueIndex = getNextValueOperand(view);
+          EV rightValueIndex = getNextValueOperand(view);
+          onAddLong(leftValueIndex, rightValueIndex);
+          return;
+        }
+      case LirOpcodes.FADD:
+        {
+          EV leftValueIndex = getNextValueOperand(view);
+          EV rightValueIndex = getNextValueOperand(view);
+          onAddFloat(leftValueIndex, rightValueIndex);
+          return;
+        }
+      case LirOpcodes.DADD:
+        {
+          EV leftValueIndex = getNextValueOperand(view);
+          EV rightValueIndex = getNextValueOperand(view);
+          onAddDouble(leftValueIndex, rightValueIndex);
+          return;
+        }
+      case LirOpcodes.ISUB:
+        {
+          EV leftValueIndex = getNextValueOperand(view);
+          EV rightValueIndex = getNextValueOperand(view);
+          onSubInt(leftValueIndex, rightValueIndex);
+          return;
+        }
+      case LirOpcodes.LSUB:
+        {
+          EV leftValueIndex = getNextValueOperand(view);
+          EV rightValueIndex = getNextValueOperand(view);
+          onSubLong(leftValueIndex, rightValueIndex);
+          return;
+        }
+      case LirOpcodes.FSUB:
+        {
+          EV leftValueIndex = getNextValueOperand(view);
+          EV rightValueIndex = getNextValueOperand(view);
+          onSubFloat(leftValueIndex, rightValueIndex);
+          return;
+        }
+      case LirOpcodes.DSUB:
+        {
+          EV leftValueIndex = getNextValueOperand(view);
+          EV rightValueIndex = getNextValueOperand(view);
+          onSubDouble(leftValueIndex, rightValueIndex);
+          return;
+        }
+      case LirOpcodes.IMUL:
+        {
+          EV leftValueIndex = getNextValueOperand(view);
+          EV rightValueIndex = getNextValueOperand(view);
+          onMulInt(leftValueIndex, rightValueIndex);
+          return;
+        }
+      case LirOpcodes.LMUL:
+        {
+          EV leftValueIndex = getNextValueOperand(view);
+          EV rightValueIndex = getNextValueOperand(view);
+          onMulLong(leftValueIndex, rightValueIndex);
+          return;
+        }
+      case LirOpcodes.FMUL:
+        {
+          EV leftValueIndex = getNextValueOperand(view);
+          EV rightValueIndex = getNextValueOperand(view);
+          onMulFloat(leftValueIndex, rightValueIndex);
+          return;
+        }
+      case LirOpcodes.DMUL:
+        {
+          EV leftValueIndex = getNextValueOperand(view);
+          EV rightValueIndex = getNextValueOperand(view);
+          onMulDouble(leftValueIndex, rightValueIndex);
+          return;
+        }
       case LirOpcodes.IDIV:
         {
           EV leftValueIndex = getNextValueOperand(view);
@@ -204,6 +438,55 @@
           onDivInt(leftValueIndex, rightValueIndex);
           return;
         }
+      case LirOpcodes.LDIV:
+        {
+          EV leftValueIndex = getNextValueOperand(view);
+          EV rightValueIndex = getNextValueOperand(view);
+          onDivLong(leftValueIndex, rightValueIndex);
+          return;
+        }
+      case LirOpcodes.FDIV:
+        {
+          EV leftValueIndex = getNextValueOperand(view);
+          EV rightValueIndex = getNextValueOperand(view);
+          onDivFloat(leftValueIndex, rightValueIndex);
+          return;
+        }
+      case LirOpcodes.DDIV:
+        {
+          EV leftValueIndex = getNextValueOperand(view);
+          EV rightValueIndex = getNextValueOperand(view);
+          onDivDouble(leftValueIndex, rightValueIndex);
+          return;
+        }
+      case LirOpcodes.IREM:
+        {
+          EV leftValueIndex = getNextValueOperand(view);
+          EV rightValueIndex = getNextValueOperand(view);
+          onRemInt(leftValueIndex, rightValueIndex);
+          return;
+        }
+      case LirOpcodes.LREM:
+        {
+          EV leftValueIndex = getNextValueOperand(view);
+          EV rightValueIndex = getNextValueOperand(view);
+          onRemLong(leftValueIndex, rightValueIndex);
+          return;
+        }
+      case LirOpcodes.FREM:
+        {
+          EV leftValueIndex = getNextValueOperand(view);
+          EV rightValueIndex = getNextValueOperand(view);
+          onRemFloat(leftValueIndex, rightValueIndex);
+          return;
+        }
+      case LirOpcodes.DREM:
+        {
+          EV leftValueIndex = getNextValueOperand(view);
+          EV rightValueIndex = getNextValueOperand(view);
+          onRemDouble(leftValueIndex, rightValueIndex);
+          return;
+        }
       case LirOpcodes.IFNE:
         {
           int blockIndex = view.getNextBlockOperand();
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNaming.java b/src/main/java/com/android/tools/r8/naming/ClassNaming.java
index 5899d87..ed80ba9 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNaming.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNaming.java
@@ -45,6 +45,12 @@
   <T extends Throwable> void forAllFieldNaming(
       ThrowingConsumer<MemberNaming, T> consumer) throws T;
 
+  <T extends Throwable> void forAllFieldNamingSorted(ThrowingConsumer<MemberNaming, T> consumer)
+      throws T;
+
   <T extends Throwable> void forAllMethodNaming(
       ThrowingConsumer<MemberNaming, T> consumer) throws T;
+
+  <T extends Throwable> void forAllMethodNamingSorted(ThrowingConsumer<MemberNaming, T> consumer)
+      throws T;
 }
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNamingForMapApplier.java b/src/main/java/com/android/tools/r8/naming/ClassNamingForMapApplier.java
index afc7132..a63f263 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNamingForMapApplier.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNamingForMapApplier.java
@@ -13,6 +13,7 @@
 import com.android.tools.r8.naming.MemberNaming.Signature.SignatureKind;
 import com.android.tools.r8.naming.mappinginformation.MappingInformation;
 import com.android.tools.r8.position.Position;
+import com.android.tools.r8.utils.CollectionUtils;
 import com.android.tools.r8.utils.Reporter;
 import com.android.tools.r8.utils.ThrowingConsumer;
 import com.google.common.base.Objects;
@@ -165,6 +166,14 @@
   }
 
   @Override
+  public <T extends Throwable> void forAllFieldNamingSorted(
+      ThrowingConsumer<MemberNaming, T> consumer) throws T {
+    for (MemberNaming naming : CollectionUtils.sort(fieldMembers.values())) {
+      consumer.accept(naming);
+    }
+  }
+
+  @Override
   public <T extends Throwable> void forAllMethodNaming(
       ThrowingConsumer<MemberNaming, T> consumer) throws T {
     for (MemberNaming naming : methodMembers.values()) {
@@ -173,6 +182,14 @@
   }
 
   @Override
+  public <T extends Throwable> void forAllMethodNamingSorted(
+      ThrowingConsumer<MemberNaming, T> consumer) throws T {
+    for (MemberNaming naming : CollectionUtils.sort(methodMembers.values())) {
+      consumer.accept(naming);
+    }
+  }
+
+  @Override
   public MemberNaming lookup(Signature renamedSignature) {
     // As the key is inverted, this looks a lot like
     //   {@link ClassNamingForNameMapper#lookupByOriginalSignature}.
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java b/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
index 2a1408a..5a71db9 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
@@ -13,6 +13,7 @@
 import com.android.tools.r8.naming.mappinginformation.OutlineCallsiteMappingInformation;
 import com.android.tools.r8.naming.mappinginformation.RewriteFrameMappingInformation;
 import com.android.tools.r8.utils.ChainableStringConsumer;
+import com.android.tools.r8.utils.CollectionUtils;
 import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.ThrowingConsumer;
 import com.google.common.collect.ImmutableList;
@@ -424,6 +425,14 @@
     }
   }
 
+  @Override
+  public <T extends Throwable> void forAllFieldNamingSorted(
+      ThrowingConsumer<MemberNaming, T> consumer) throws T {
+    for (MemberNaming naming : CollectionUtils.sort(fieldMembers.values())) {
+      consumer.accept(naming);
+    }
+  }
+
   public Collection<MemberNaming> allFieldNamings() {
     return fieldMembers.values();
   }
@@ -460,6 +469,14 @@
     }
   }
 
+  @Override
+  public <T extends Throwable> void forAllMethodNamingSorted(
+      ThrowingConsumer<MemberNaming, T> consumer) throws T {
+    for (MemberNaming naming : CollectionUtils.sort(methodMembers.values())) {
+      consumer.accept(naming);
+    }
+  }
+
   public Collection<MemberNaming> allMethodNamings() {
     return methodMembers.values();
   }
@@ -471,7 +488,7 @@
     additionalMappingInfo.forEach(info -> consumer.accept("# " + info.serialize()).accept("\n"));
 
     // Print field member namings.
-    forAllFieldNaming(m -> consumer.accept("    ").accept(m.toString()).accept("\n"));
+    forAllFieldNamingSorted(m -> consumer.accept("    ").accept(m.toString()).accept("\n"));
 
     // Sort MappedRanges by sequence number to restore construction order (original Proguard-map
     // input).
diff --git a/src/main/java/com/android/tools/r8/naming/MemberNaming.java b/src/main/java/com/android/tools/r8/naming/MemberNaming.java
index a26d531..d1bfb78 100644
--- a/src/main/java/com/android/tools/r8/naming/MemberNaming.java
+++ b/src/main/java/com/android/tools/r8/naming/MemberNaming.java
@@ -32,6 +32,7 @@
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.List;
 import java.util.function.Consumer;
 import java.util.function.Function;
@@ -42,7 +43,7 @@
  *
  * <p>This includes the signature and the original name.
  */
-public class MemberNaming implements MappingWithResidualInfo {
+public class MemberNaming implements MappingWithResidualInfo, Comparable<MemberNaming> {
 
   private static final List<ReferentialMappingInformation> EMPTY_MAPPING_INFORMATION =
       Collections.emptyList();
@@ -172,6 +173,15 @@
     return additionalMappingInformation;
   }
 
+  @Override
+  public int compareTo(MemberNaming other) {
+    return Comparator.comparing(MemberNaming::isFieldNaming)
+        .thenComparing(MemberNaming::getRenamedName)
+        .thenComparing(MemberNaming::getOriginalName)
+        .thenComparing(memberNaming -> getOriginalSignature().toString())
+        .compare(this, other);
+  }
+
   public abstract static class Signature {
 
     public final String name;
diff --git a/src/main/java/com/android/tools/r8/naming/MultiProguardMapConsumer.java b/src/main/java/com/android/tools/r8/naming/MultiProguardMapConsumer.java
new file mode 100644
index 0000000..5901fd3
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/MultiProguardMapConsumer.java
@@ -0,0 +1,47 @@
+// Copyright (c) 2023, 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.naming;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.ProguardMapConsumer;
+import java.util.ArrayList;
+import java.util.List;
+
+public class MultiProguardMapConsumer extends ProguardMapConsumer {
+
+  private final List<ProguardMapConsumer> proguardMapConsumers;
+
+  public MultiProguardMapConsumer(List<ProguardMapConsumer> proguardMapConsumers) {
+    this.proguardMapConsumers = proguardMapConsumers;
+  }
+
+  @Override
+  public void finished(DiagnosticsHandler handler) {
+    proguardMapConsumers.forEach(consumer -> consumer.finished(handler));
+  }
+
+  @Override
+  public void accept(ProguardMapMarkerInfo markerInfo, ClassNameMapper classNameMapper) {
+    proguardMapConsumers.forEach(consumer -> consumer.accept(markerInfo, classNameMapper));
+  }
+
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  public static class Builder {
+
+    private final List<ProguardMapConsumer> proguardMapConsumers = new ArrayList<>();
+
+    public Builder addProguardMapConsumer(ProguardMapConsumer consumer) {
+      proguardMapConsumers.add(consumer);
+      return this;
+    }
+
+    public MultiProguardMapConsumer build() {
+      return new MultiProguardMapConsumer(proguardMapConsumers);
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapChecker.java b/src/main/java/com/android/tools/r8/naming/ProguardMapChecker.java
new file mode 100644
index 0000000..be80509
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapChecker.java
@@ -0,0 +1,129 @@
+// Copyright (c) 2023, 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.naming;
+
+import static com.android.tools.r8.naming.ProguardMapMarkerInfo.MARKER_KEY_PG_MAP_HASH;
+import static com.android.tools.r8.naming.ProguardMapMarkerInfo.SHA_256_KEY;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.StringConsumer;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.hash.Hasher;
+import com.google.common.hash.Hashing;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+public class ProguardMapChecker implements StringConsumer {
+
+  private final StringConsumer inner;
+  private final StringBuilder contents = new StringBuilder();
+
+  ProguardMapChecker(StringConsumer inner) {
+    if (!InternalOptions.assertionsEnabled()) {
+      // Make sure we never get here without assertions enabled.
+      throw new Unreachable();
+    }
+    this.inner = inner;
+  }
+
+  @Override
+  public void accept(String string, DiagnosticsHandler handler) {
+    inner.accept(string, handler);
+    contents.append(string);
+  }
+
+  @Override
+  public void finished(DiagnosticsHandler handler) {
+    inner.finished(handler);
+    String stringContent = contents.toString();
+    assert validateProguardMapParses(stringContent);
+    assert validateProguardMapHash(stringContent).isOk();
+  }
+
+  private static boolean validateProguardMapParses(String content) {
+    try {
+      ClassNameMapper.mapperFromString(content);
+    } catch (IOException e) {
+      e.printStackTrace();
+      return false;
+    }
+    return true;
+  }
+
+  public static class VerifyMappingFileHashResult {
+
+    private final boolean error;
+    private final String message;
+
+    public static VerifyMappingFileHashResult createOk() {
+      return new VerifyMappingFileHashResult(false, null);
+    }
+
+    public static VerifyMappingFileHashResult createInfo(String message) {
+      return new VerifyMappingFileHashResult(false, message);
+    }
+
+    public static VerifyMappingFileHashResult createError(String message) {
+      return new VerifyMappingFileHashResult(true, message);
+    }
+
+    private VerifyMappingFileHashResult(boolean error, String message) {
+      this.error = error;
+      this.message = message;
+    }
+
+    public boolean isOk() {
+      return !error && message == null;
+    }
+
+    public boolean isError() {
+      return error;
+    }
+
+    public String getMessage() {
+      assert message != null;
+      return message;
+    }
+  }
+
+  public static VerifyMappingFileHashResult validateProguardMapHash(String content) {
+    int lineEnd = -1;
+    while (true) {
+      int lineStart = lineEnd + 1;
+      lineEnd = content.indexOf('\n', lineStart);
+      if (lineEnd < 0) {
+        return VerifyMappingFileHashResult.createInfo("Failure to find map hash");
+      }
+      String line = content.substring(lineStart, lineEnd).trim();
+      if (line.isEmpty()) {
+        // Ignore empty lines in the header.
+        continue;
+      }
+      if (line.charAt(0) != '#') {
+        // At the first non-empty non-comment line we assume that the file has no hash marker.
+        return VerifyMappingFileHashResult.createInfo("Failure to find map hash in header");
+      }
+      String headerLine = line.substring(1).trim();
+      if (headerLine.startsWith(MARKER_KEY_PG_MAP_HASH)) {
+        int shaIndex = headerLine.indexOf(SHA_256_KEY + " ", MARKER_KEY_PG_MAP_HASH.length());
+        if (shaIndex < 0) {
+          return VerifyMappingFileHashResult.createError(
+              "Unknown map hash function: '" + headerLine + "'");
+        }
+        String headerHash = headerLine.substring(shaIndex + SHA_256_KEY.length()).trim();
+        // We are on the hash line. Everything past this line is the hashed content.
+        Hasher hasher = Hashing.sha256().newHasher();
+        String hashedContent = content.substring(lineEnd + 1);
+        hasher.putString(hashedContent, StandardCharsets.UTF_8);
+        String computedHash = hasher.hash().toString();
+        return headerHash.equals(computedHash)
+            ? VerifyMappingFileHashResult.createOk()
+            : VerifyMappingFileHashResult.createError(
+                "Mismatching map hash: '" + headerHash + "' != '" + computedHash + "'");
+      }
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapMarkerInfo.java b/src/main/java/com/android/tools/r8/naming/ProguardMapMarkerInfo.java
new file mode 100644
index 0000000..8394ecf
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapMarkerInfo.java
@@ -0,0 +1,112 @@
+// Copyright (c) 2023, 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.naming;
+
+import com.android.tools.r8.Version;
+import com.android.tools.r8.naming.ProguardMapSupplier.ProguardMapId;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.VersionProperties;
+import java.util.ArrayList;
+import java.util.List;
+
+public class ProguardMapMarkerInfo {
+
+  private static final String MARKER_KEY_COMPILER = "compiler";
+  private static final String MARKER_KEY_COMPILER_VERSION = "compiler_version";
+  private static final String MARKER_KEY_COMPILER_HASH = "compiler_hash";
+  private static final String MARKER_KEY_MIN_API = "min_api";
+  private static final String MARKER_KEY_PG_MAP_ID = "pg_map_id";
+  public static final String MARKER_KEY_PG_MAP_HASH = "pg_map_hash";
+  public static final String SHA_256_KEY = "SHA-256";
+
+  private final String compilerName;
+  private final boolean isGeneratingDex;
+  private final AndroidApiLevel apiLevel;
+  private final MapVersion mapVersion;
+  private final ProguardMapId proguardMapId;
+
+  private ProguardMapMarkerInfo(
+      String compilerName,
+      boolean isGeneratingDex,
+      AndroidApiLevel apiLevel,
+      MapVersion mapVersion,
+      ProguardMapId proguardMapId) {
+    this.compilerName = compilerName;
+    this.isGeneratingDex = isGeneratingDex;
+    this.apiLevel = apiLevel;
+    this.mapVersion = mapVersion;
+    this.proguardMapId = proguardMapId;
+  }
+
+  public List<String> toPreamble() {
+    List<String> preamble = new ArrayList<>();
+    preamble.add("# " + MARKER_KEY_COMPILER + ": " + compilerName);
+    preamble.add("# " + MARKER_KEY_COMPILER_VERSION + ": " + Version.LABEL);
+    if (isGeneratingDex) {
+      preamble.add("# " + MARKER_KEY_MIN_API + ": " + apiLevel.getLevel());
+    }
+    if (Version.isDevelopmentVersion()) {
+      preamble.add("# " + MARKER_KEY_COMPILER_HASH + ": " + VersionProperties.INSTANCE.getSha());
+    }
+    // Turn off linting of the mapping file in some build systems.
+    preamble.add("# common_typos_disable");
+    // Emit the R8 specific map-file version.
+    if (mapVersion.isGreaterThan(MapVersion.MAP_VERSION_NONE)) {
+      preamble.add("# " + mapVersion.toMapVersionMappingInformation().serialize());
+    }
+    preamble.add("# " + MARKER_KEY_PG_MAP_ID + ": " + proguardMapId.getId());
+    preamble.add(
+        "# " + MARKER_KEY_PG_MAP_HASH + ": " + SHA_256_KEY + " " + proguardMapId.getHash());
+    return preamble;
+  }
+
+  public String serializeToString() {
+    return StringUtils.unixLines(toPreamble());
+  }
+
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  public static class Builder {
+
+    private String compilerName;
+    private boolean isGeneratingDex;
+    private AndroidApiLevel apiLevel;
+    private MapVersion mapVersion;
+    private ProguardMapId proguardMapId;
+
+    public Builder setCompilerName(String compilerName) {
+      this.compilerName = compilerName;
+      return this;
+    }
+
+    public Builder setApiLevel(AndroidApiLevel apiLevel) {
+      this.apiLevel = apiLevel;
+      return this;
+    }
+
+    public Builder setGeneratingDex(boolean generatingDex) {
+      isGeneratingDex = generatingDex;
+      return this;
+    }
+
+    public Builder setMapVersion(MapVersion mapVersion) {
+      this.mapVersion = mapVersion;
+      return this;
+    }
+
+    public Builder setProguardMapId(ProguardMapId proguardMapId) {
+      this.proguardMapId = proguardMapId;
+      return this;
+    }
+
+    public ProguardMapMarkerInfo build() {
+      return new ProguardMapMarkerInfo(
+          compilerName, isGeneratingDex, apiLevel, mapVersion, proguardMapId);
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapPartitionConsumer.java b/src/main/java/com/android/tools/r8/naming/ProguardMapPartitionConsumer.java
new file mode 100644
index 0000000..451fd6c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapPartitionConsumer.java
@@ -0,0 +1,96 @@
+// Copyright (c) 2023, 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.naming;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.ProguardMapConsumer;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.retrace.MappingPartition;
+import com.android.tools.r8.retrace.MappingPartitionMetadata;
+import com.android.tools.r8.retrace.ProguardMapPartitioner;
+import com.android.tools.r8.retrace.internal.ProguardMapProducerInternal;
+import java.io.IOException;
+import java.util.function.Consumer;
+
+public class ProguardMapPartitionConsumer extends ProguardMapConsumer {
+
+  private final Consumer<MappingPartition> mappingPartitionConsumer;
+  private final Consumer<MappingPartitionMetadata> metadataConsumer;
+  private final Runnable finishedConsumer;
+  private final DiagnosticsHandler diagnosticsHandler;
+
+  private ProguardMapPartitionConsumer(
+      Consumer<MappingPartition> mappingPartitionConsumer,
+      Consumer<MappingPartitionMetadata> metadataConsumer,
+      Runnable finishedConsumer,
+      DiagnosticsHandler diagnosticsHandler) {
+    this.mappingPartitionConsumer = mappingPartitionConsumer;
+    this.metadataConsumer = metadataConsumer;
+    this.finishedConsumer = finishedConsumer;
+    this.diagnosticsHandler = diagnosticsHandler;
+  }
+
+  @Override
+  public void accept(ProguardMapMarkerInfo makerInfo, ClassNameMapper classNameMapper) {
+    try {
+      // TODO(b/274735214): Ensure we get markerInfo consumed as well.
+      MappingPartitionMetadata run =
+          ProguardMapPartitioner.builder(diagnosticsHandler)
+              .setProguardMapProducer(new ProguardMapProducerInternal(classNameMapper))
+              .setPartitionConsumer(mappingPartitionConsumer)
+              // Setting these do not actually do anything currently since there is no parsing.
+              .setAllowEmptyMappedRanges(false)
+              .setAllowExperimentalMapping(false)
+              .build()
+              .run();
+      metadataConsumer.accept(run);
+    } catch (IOException exception) {
+      throw new Unreachable("IOExceptions should only occur when parsing");
+    }
+  }
+
+  @Override
+  public void finished(DiagnosticsHandler handler) {
+    finishedConsumer.run();
+  }
+
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  public static class Builder {
+
+    private Consumer<MappingPartition> mappingPartitionConsumer;
+    private Consumer<MappingPartitionMetadata> metadataConsumer;
+    private Runnable finishedConsumer;
+    private DiagnosticsHandler diagnosticsHandler;
+
+    public Builder setMappingPartitionConsumer(
+        Consumer<MappingPartition> mappingPartitionConsumer) {
+      this.mappingPartitionConsumer = mappingPartitionConsumer;
+      return this;
+    }
+
+    public Builder setMetadataConsumer(Consumer<MappingPartitionMetadata> metadataConsumer) {
+      this.metadataConsumer = metadataConsumer;
+      return this;
+    }
+
+    public Builder setFinishedConsumer(Runnable finishedConsumer) {
+      this.finishedConsumer = finishedConsumer;
+      return this;
+    }
+
+    public Builder setDiagnosticsHandler(DiagnosticsHandler diagnosticsHandler) {
+      this.diagnosticsHandler = diagnosticsHandler;
+      return this;
+    }
+
+    public ProguardMapPartitionConsumer build() {
+      return new ProguardMapPartitionConsumer(
+          mappingPartitionConsumer, metadataConsumer, finishedConsumer, diagnosticsHandler);
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapStringConsumer.java b/src/main/java/com/android/tools/r8/naming/ProguardMapStringConsumer.java
new file mode 100644
index 0000000..95bbd38
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapStringConsumer.java
@@ -0,0 +1,74 @@
+// Copyright (c) 2023, 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.naming;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.ProguardMapConsumer;
+import com.android.tools.r8.StringConsumer;
+import com.android.tools.r8.utils.ChainableStringConsumer;
+
+/***
+ * Default implementation of a ProguardMapConsumer that wraps around a string consumer for streamed
+ * string output.
+ */
+public class ProguardMapStringConsumer extends ProguardMapConsumer
+    implements ChainableStringConsumer {
+
+  private final StringConsumer stringConsumer;
+  private final DiagnosticsHandler diagnosticsHandler;
+
+  private ProguardMapStringConsumer(
+      StringConsumer stringConsumer, DiagnosticsHandler diagnosticsHandler) {
+    assert stringConsumer != null;
+    assert diagnosticsHandler != null;
+    this.stringConsumer = stringConsumer;
+    this.diagnosticsHandler = diagnosticsHandler;
+  }
+
+  @Override
+  public void accept(ProguardMapMarkerInfo markerInfo, ClassNameMapper classNameMapper) {
+    accept(markerInfo.serializeToString());
+    classNameMapper.write(this);
+  }
+
+  @Override
+  public ChainableStringConsumer accept(String string) {
+    stringConsumer.accept(string, diagnosticsHandler);
+    return this;
+  }
+
+  public StringConsumer getStringConsumer() {
+    return stringConsumer;
+  }
+
+  @Override
+  public void finished(DiagnosticsHandler handler) {
+    stringConsumer.finished(handler);
+  }
+
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  public static class Builder {
+
+    private StringConsumer stringConsumer;
+    private DiagnosticsHandler diagnosticsHandler;
+
+    public Builder setStringConsumer(StringConsumer stringConsumer) {
+      this.stringConsumer = stringConsumer;
+      return this;
+    }
+
+    public Builder setDiagnosticsHandler(DiagnosticsHandler diagnosticsHandler) {
+      this.diagnosticsHandler = diagnosticsHandler;
+      return this;
+    }
+
+    public ProguardMapStringConsumer build() {
+      return new ProguardMapStringConsumer(stringConsumer, diagnosticsHandler);
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java b/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java
index a7f62dd..c3fc071 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java
@@ -3,33 +3,20 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.naming;
 
-import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.MapIdEnvironment;
 import com.android.tools.r8.MapIdProvider;
-import com.android.tools.r8.StringConsumer;
-import com.android.tools.r8.Version;
+import com.android.tools.r8.ProguardMapConsumer;
 import com.android.tools.r8.dex.Marker.Tool;
-import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.utils.ChainableStringConsumer;
 import com.android.tools.r8.utils.ExceptionUtils;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.Reporter;
-import com.android.tools.r8.utils.VersionProperties;
 import com.google.common.hash.Hasher;
 import com.google.common.hash.Hashing;
-import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 
 public class ProguardMapSupplier {
 
-  public static final String MARKER_KEY_COMPILER = "compiler";
-  public static final String MARKER_KEY_COMPILER_VERSION = "compiler_version";
-  public static final String MARKER_KEY_COMPILER_HASH = "compiler_hash";
-  public static final String MARKER_KEY_MIN_API = "min_api";
-  public static final String MARKER_KEY_PG_MAP_ID = "pg_map_id";
-  public static final String MARKER_KEY_PG_MAP_HASH = "pg_map_hash";
-  public static final String SHA_256_KEY = "SHA-256";
-
   public static int PG_MAP_ID_LENGTH = 7;
 
   // Hash of the Proguard map (excluding the header up to and including the hash marker).
@@ -56,8 +43,8 @@
   }
 
   private final ClassNameMapper classNameMapper;
-  private final StringConsumer consumer;
   private final InternalOptions options;
+  private final ProguardMapConsumer consumer;
   private final Reporter reporter;
   private final Tool compiler;
 
@@ -79,11 +66,18 @@
   }
 
   public ProguardMapId writeProguardMap() {
-    ProguardMapId id = computeProguardMapId();
-    writeMarker(id);
-    writeBody();
-    ExceptionUtils.withFinishedResourceHandler(reporter, consumer);
-    return id;
+    ProguardMapId proguardMapId = computeProguardMapId();
+    consumer.accept(
+        ProguardMapMarkerInfo.builder()
+            .setCompilerName(compiler.name())
+            .setProguardMapId(proguardMapId)
+            .setGeneratingDex(options.isGeneratingDex())
+            .setApiLevel(options.getMinApiLevel())
+            .setMapVersion(options.getMapFileVersion())
+            .build(),
+        classNameMapper);
+    ExceptionUtils.withConsumeResourceHandler(reporter, this.consumer::finished);
+    return proguardMapId;
   }
 
   private ProguardMapId computeProguardMapId() {
@@ -92,52 +86,6 @@
     return builder.build(options.mapIdProvider);
   }
 
-  private void writeBody() {
-    classNameMapper.write(new ProguardMapWriter());
-  }
-
-  private void writeMarker(ProguardMapId id) {
-    StringBuilder builder = new StringBuilder();
-    builder.append(
-        "# "
-            + MARKER_KEY_COMPILER
-            + ": "
-            + compiler.name()
-            + "\n"
-            + "# "
-            + MARKER_KEY_COMPILER_VERSION
-            + ": "
-            + Version.LABEL
-            + "\n");
-    if (options.isGeneratingDex()) {
-      builder.append("# " + MARKER_KEY_MIN_API + ": " + options.getMinApiLevel().getLevel() + "\n");
-    }
-    if (Version.isDevelopmentVersion()) {
-      builder.append(
-          "# " + MARKER_KEY_COMPILER_HASH + ": " + VersionProperties.INSTANCE.getSha() + "\n");
-    }
-    // Turn off linting of the mapping file in some build systems.
-    builder.append("# common_typos_disable" + "\n");
-    // Emit the R8 specific map-file version.
-    MapVersion mapVersion = options.getMapFileVersion();
-    if (mapVersion.isGreaterThan(MapVersion.MAP_VERSION_NONE)) {
-      builder
-          .append("# ")
-          .append(mapVersion.toMapVersionMappingInformation().serialize())
-          .append("\n");
-    }
-    builder.append("# " + MARKER_KEY_PG_MAP_ID + ": " + id.getId() + "\n");
-    // Place the map hash as the last header item. Everything past this line is the hashed content.
-    builder
-        .append("# ")
-        .append(MARKER_KEY_PG_MAP_HASH)
-        .append(": ")
-        .append(SHA_256_KEY)
-        .append(" ")
-        .append(id.getHash())
-        .append("\n");
-    consumer.accept(builder.toString(), reporter);
-  }
 
   static class ProguardMapIdBuilder implements ChainableStringConsumer {
 
@@ -171,123 +119,4 @@
     }
   }
 
-  class ProguardMapWriter implements ChainableStringConsumer {
-
-    @Override
-    public ProguardMapWriter accept(String string) {
-      consumer.accept(string, reporter);
-      return this;
-    }
-  }
-
-  public static class ProguardMapChecker implements StringConsumer {
-
-    private final StringConsumer inner;
-    private final StringBuilder contents = new StringBuilder();
-
-    ProguardMapChecker(StringConsumer inner) {
-      if (!InternalOptions.assertionsEnabled()) {
-        // Make sure we never get here without assertions enabled.
-        throw new Unreachable();
-      }
-      this.inner = inner;
-    }
-
-    @Override
-    public void accept(String string, DiagnosticsHandler handler) {
-      inner.accept(string, handler);
-      contents.append(string);
-    }
-
-    @Override
-    public void finished(DiagnosticsHandler handler) {
-      inner.finished(handler);
-      String stringContent = contents.toString();
-      assert validateProguardMapParses(stringContent);
-      assert validateProguardMapHash(stringContent).isOk();
-    }
-
-    private static boolean validateProguardMapParses(String content) {
-      try {
-        ClassNameMapper.mapperFromString(content);
-      } catch (IOException e) {
-        e.printStackTrace();
-        return false;
-      }
-      return true;
-    }
-
-    public static class VerifyMappingFileHashResult {
-      private final boolean error;
-      private final String message;
-
-      public static VerifyMappingFileHashResult createOk() {
-        return new VerifyMappingFileHashResult(false, null);
-      }
-
-      public static VerifyMappingFileHashResult createInfo(String message) {
-        return new VerifyMappingFileHashResult(false, message);
-      }
-
-      public static VerifyMappingFileHashResult createError(String message) {
-        return new VerifyMappingFileHashResult(true, message);
-      }
-
-      private VerifyMappingFileHashResult(boolean error, String message) {
-        this.error = error;
-        this.message = message;
-      }
-
-      public boolean isOk() {
-        return !error && message == null;
-      }
-
-      public boolean isError() {
-        return error;
-      }
-
-      public String getMessage() {
-        assert message != null;
-        return message;
-      }
-    }
-
-    public static VerifyMappingFileHashResult validateProguardMapHash(String content) {
-      int lineEnd = -1;
-      while (true) {
-        int lineStart = lineEnd + 1;
-        lineEnd = content.indexOf('\n', lineStart);
-        if (lineEnd < 0) {
-          return VerifyMappingFileHashResult.createInfo("Failure to find map hash");
-        }
-        String line = content.substring(lineStart, lineEnd).trim();
-        if (line.isEmpty()) {
-          // Ignore empty lines in the header.
-          continue;
-        }
-        if (line.charAt(0) != '#') {
-          // At the first non-empty non-comment line we assume that the file has no hash marker.
-          return VerifyMappingFileHashResult.createInfo("Failure to find map hash in header");
-        }
-        String headerLine = line.substring(1).trim();
-        if (headerLine.startsWith(MARKER_KEY_PG_MAP_HASH)) {
-          int shaIndex = headerLine.indexOf(SHA_256_KEY + " ", MARKER_KEY_PG_MAP_HASH.length());
-          if (shaIndex < 0) {
-            return VerifyMappingFileHashResult.createError(
-                "Unknown map hash function: '" + headerLine + "'");
-          }
-          String headerHash = headerLine.substring(shaIndex + SHA_256_KEY.length()).trim();
-          // We are on the hash line. Everything past this line is the hashed content.
-          Hasher hasher = Hashing.sha256().newHasher();
-          String hashedContent = content.substring(lineEnd + 1);
-          hasher.putString(hashedContent, StandardCharsets.UTF_8);
-          String computedHash = hasher.hash().toString();
-          return headerHash.equals(computedHash)
-              ? VerifyMappingFileHashResult.createOk()
-              : VerifyMappingFileHashResult.createError(
-                  "Mismatching map hash: '" + headerHash + "' != '" + computedHash + "'");
-        }
-      }
-    }
-  }
 }
diff --git a/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemovalOptions.java b/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemovalOptions.java
new file mode 100644
index 0000000..a902764
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemovalOptions.java
@@ -0,0 +1,42 @@
+// Copyright (c) 2023, 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.redundantbridgeremoval;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexLibraryClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.SetUtils;
+import java.util.Collections;
+import java.util.Set;
+
+public class RedundantBridgeRemovalOptions {
+
+  private final InternalOptions options;
+
+  private Set<DexType> noConstructorShrinkingHierarchies;
+
+  public RedundantBridgeRemovalOptions(InternalOptions options) {
+    this.options = options;
+  }
+
+  public void clearNoConstructorShrinkingHierarchiesForTesting() {
+    noConstructorShrinkingHierarchies = Collections.emptySet();
+  }
+
+  public RedundantBridgeRemovalOptions ensureInitialized() {
+    if (noConstructorShrinkingHierarchies == null) {
+      DexItemFactory dexItemFactory = options.dexItemFactory();
+      noConstructorShrinkingHierarchies =
+          SetUtils.newIdentityHashSet(
+              dexItemFactory.androidAppFragment, dexItemFactory.androidAppZygotePreload);
+    }
+    return this;
+  }
+
+  public boolean isPlatformReflectingOnDefaultConstructorInSubclasses(DexLibraryClass clazz) {
+    return noConstructorShrinkingHierarchies.contains(clazz.getType());
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/RedundantBridgeRemover.java b/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemover.java
similarity index 72%
rename from src/main/java/com/android/tools/r8/optimize/RedundantBridgeRemover.java
rename to src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemover.java
index 763c40f..c050859 100644
--- a/src/main/java/com/android/tools/r8/optimize/RedundantBridgeRemover.java
+++ b/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemover.java
@@ -1,9 +1,10 @@
 // Copyright (c) 2017, 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;
+package com.android.tools.r8.optimize.redundantbridgeremoval;
 
 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.DexEncodedMethod;
 import com.android.tools.r8.graph.DexMethod;
@@ -15,23 +16,35 @@
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.PrunedItems;
 import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
+import com.android.tools.r8.optimize.InvokeSingleTargetExtractor;
 import com.android.tools.r8.optimize.InvokeSingleTargetExtractor.InvokeKind;
-import com.android.tools.r8.optimize.redundantbridgeremoval.RedundantBridgeRemovalLens;
+import com.android.tools.r8.optimize.MemberRebindingIdentityLens;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.KeepMethodInfo;
+import com.android.tools.r8.utils.IterableUtils;
 import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.WorkList;
 import com.android.tools.r8.utils.collections.ProgramMethodSet;
+import com.google.common.collect.Iterables;
+import java.util.Collections;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
+import java.util.function.Consumer;
 
 public class RedundantBridgeRemover {
 
   private final AppView<AppInfoWithLiveness> appView;
+  private final RedundantBridgeRemovalOptions redundantBridgeRemovalOptions;
+
+  private final InvokedReflectivelyFromPlatformAnalysis invokedReflectivelyFromPlatformAnalysis =
+      new InvokedReflectivelyFromPlatformAnalysis();
 
   public RedundantBridgeRemover(AppView<AppInfoWithLiveness> appView) {
     this.appView = appView;
+    this.redundantBridgeRemovalOptions =
+        appView.options().getRedundantBridgeRemovalOptions().ensureInitialized();
   }
 
   private DexClassAndMethod getTargetForRedundantBridge(ProgramMethod method) {
@@ -52,6 +65,9 @@
     if (!isTargetingSuperMethod(method, targetExtractor.getKind(), target)) {
       return null;
     }
+    if (invokedReflectivelyFromPlatformAnalysis.isMaybeInvokedReflectivelyFromPlatform(method)) {
+      return null;
+    }
     // This is a visibility forward, so check for the direct target.
     DexClassAndMethod targetMethod =
         appView.appInfo().unsafeResolveMethodDueToDexFormatLegacy(target).getResolutionPair();
@@ -236,4 +252,88 @@
         });
     appView.pruneItems(prunedItemsBuilder.build(), executorService);
   }
+
+  class InvokedReflectivelyFromPlatformAnalysis {
+
+    // Maps each class to a boolean indicating if the class inherits from android.app.Fragment or
+    // android.app.ZygotePreload.
+    private final Map<DexClass, Boolean> cache = new ConcurrentHashMap<>();
+
+    boolean isMaybeInvokedReflectivelyFromPlatform(ProgramMethod method) {
+      return method.getDefinition().isDefaultInstanceInitializer()
+          && !method.getHolder().isAbstract()
+          && computeIsPlatformReflectingOnDefaultConstructor(method.getHolder());
+    }
+
+    private boolean computeIsPlatformReflectingOnDefaultConstructor(DexProgramClass clazz) {
+      Boolean cacheResult = cache.get(clazz);
+      if (cacheResult != null) {
+        return cacheResult;
+      }
+      WorkList.<WorklistItem>newIdentityWorkList(new NotProcessedWorklistItem(clazz))
+          .process(WorklistItem::accept);
+      assert cache.containsKey(clazz);
+      return cache.get(clazz);
+    }
+
+    abstract class WorklistItem implements Consumer<WorkList<WorklistItem>> {
+
+      protected final DexClass clazz;
+
+      WorklistItem(DexClass clazz) {
+        this.clazz = clazz;
+      }
+
+      Iterable<DexClass> getImmediateSupertypes() {
+        return IterableUtils.flatMap(
+            clazz.allImmediateSupertypes(),
+            supertype -> {
+              DexClass definition = appView.definitionFor(supertype);
+              return definition != null
+                  ? Collections.singletonList(definition)
+                  : Collections.emptyList();
+            });
+      }
+    }
+
+    class NotProcessedWorklistItem extends WorklistItem {
+
+      NotProcessedWorklistItem(DexClass clazz) {
+        super(clazz);
+      }
+
+      @Override
+      public void accept(WorkList<WorklistItem> worklist) {
+        // Enqueue a worklist item to process the current class after processing its super classes.
+        worklist.addFirstIgnoringSeenSet(new ProcessedWorklistItem(clazz));
+        // Enqueue all superclasses for processing.
+        for (DexClass supertype : getImmediateSupertypes()) {
+          if (!cache.containsKey(supertype)) {
+            worklist.addFirstIgnoringSeenSet(new NotProcessedWorklistItem(supertype));
+          }
+        }
+      }
+    }
+
+    class ProcessedWorklistItem extends WorklistItem {
+
+      ProcessedWorklistItem(DexClass clazz) {
+        super(clazz);
+      }
+
+      @Override
+      public void accept(WorkList<WorklistItem> worklist) {
+        cache.put(
+            clazz,
+            Iterables.any(
+                getImmediateSupertypes(),
+                supertype ->
+                    cache.get(supertype)
+                        || (supertype.isLibraryClass()
+                            && redundantBridgeRemovalOptions
+                                .isPlatformReflectingOnDefaultConstructorInSubclasses(
+                                    supertype.asLibraryClass()))));
+      }
+    }
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/profile/AbstractProfile.java b/src/main/java/com/android/tools/r8/profile/AbstractProfile.java
index 87b5656..ff8c494 100644
--- a/src/main/java/com/android/tools/r8/profile/AbstractProfile.java
+++ b/src/main/java/com/android/tools/r8/profile/AbstractProfile.java
@@ -16,8 +16,8 @@
   boolean containsMethodRule(DexMethod method);
 
   <E1 extends Exception, E2 extends Exception> void forEachRule(
-      ThrowingConsumer<ClassRule, E1> classRuleConsumer,
-      ThrowingConsumer<MethodRule, E2> methodRuleConsumer)
+      ThrowingConsumer<? super ClassRule, E1> classRuleConsumer,
+      ThrowingConsumer<? super MethodRule, E2> methodRuleConsumer)
       throws E1, E2;
 
   ClassRule getClassRule(DexType type);
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfile.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfile.java
index 7ab727d..e42e8ba 100644
--- a/src/main/java/com/android/tools/r8/profile/art/ArtProfile.java
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfile.java
@@ -71,8 +71,8 @@
 
   @Override
   public <E1 extends Exception, E2 extends Exception> void forEachRule(
-      ThrowingConsumer<ArtProfileClassRule, E1> classRuleConsumer,
-      ThrowingConsumer<ArtProfileMethodRule, E2> methodRuleConsumer)
+      ThrowingConsumer<? super ArtProfileClassRule, E1> classRuleConsumer,
+      ThrowingConsumer<? super ArtProfileMethodRule, E2> methodRuleConsumer)
       throws E1, E2 {
     for (ArtProfileRule rule : rules.values()) {
       rule.accept(classRuleConsumer, methodRuleConsumer);
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfileClassRule.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfileClassRule.java
index 625d92f..55590a0 100644
--- a/src/main/java/com/android/tools/r8/profile/art/ArtProfileClassRule.java
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileClassRule.java
@@ -32,8 +32,8 @@
 
   @Override
   public <E1 extends Exception, E2 extends Exception> void accept(
-      ThrowingConsumer<ArtProfileClassRule, E1> classRuleConsumer,
-      ThrowingConsumer<ArtProfileMethodRule, E2> methodRuleConsumer)
+      ThrowingConsumer<? super ArtProfileClassRule, E1> classRuleConsumer,
+      ThrowingConsumer<? super ArtProfileMethodRule, E2> methodRuleConsumer)
       throws E1 {
     classRuleConsumer.accept(this);
   }
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfileMethodRule.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfileMethodRule.java
index 1440b8b..4b417c9 100644
--- a/src/main/java/com/android/tools/r8/profile/art/ArtProfileMethodRule.java
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileMethodRule.java
@@ -35,8 +35,8 @@
 
   @Override
   public <E1 extends Exception, E2 extends Exception> void accept(
-      ThrowingConsumer<ArtProfileClassRule, E1> classRuleConsumer,
-      ThrowingConsumer<ArtProfileMethodRule, E2> methodRuleConsumer)
+      ThrowingConsumer<? super ArtProfileClassRule, E1> classRuleConsumer,
+      ThrowingConsumer<? super ArtProfileMethodRule, E2> methodRuleConsumer)
       throws E2 {
     methodRuleConsumer.accept(this);
   }
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfileRule.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfileRule.java
index bb0bcc1..39f541f 100644
--- a/src/main/java/com/android/tools/r8/profile/art/ArtProfileRule.java
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileRule.java
@@ -14,8 +14,8 @@
 public abstract class ArtProfileRule implements Comparable<ArtProfileRule>, AbstractProfileRule {
 
   public abstract <E1 extends Exception, E2 extends Exception> void accept(
-      ThrowingConsumer<ArtProfileClassRule, E1> classRuleConsumer,
-      ThrowingConsumer<ArtProfileMethodRule, E2> methodRuleConsumer)
+      ThrowingConsumer<? super ArtProfileClassRule, E1> classRuleConsumer,
+      ThrowingConsumer<? super ArtProfileMethodRule, E2> methodRuleConsumer)
       throws E1, E2;
 
   public abstract <T, E1 extends Exception, E2 extends Exception> T apply(
diff --git a/src/main/java/com/android/tools/r8/retrace/PartitionedToProguardMappingConverter.java b/src/main/java/com/android/tools/r8/retrace/PartitionedToProguardMappingConverter.java
new file mode 100644
index 0000000..b6181dc
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/PartitionedToProguardMappingConverter.java
@@ -0,0 +1,117 @@
+// Copyright (c) 2023, 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.retrace;
+
+import static com.google.common.base.Predicates.alwaysTrue;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.StringConsumer;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.naming.LineReader;
+import com.android.tools.r8.naming.MapVersion;
+import com.android.tools.r8.retrace.internal.MappingPartitionMetadataInternal;
+import com.android.tools.r8.retrace.internal.ProguardMapReaderWithFiltering.ProguardMapReaderWithFilteringInputBuffer;
+import com.android.tools.r8.retrace.internal.RetracePartitionException;
+import com.android.tools.r8.utils.ChainableStringConsumer;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+public class PartitionedToProguardMappingConverter {
+
+  private final StringConsumer consumer;
+  private final MappingPartitionFromKeySupplier partitionSupplier;
+  private final byte[] metadata;
+  private final DiagnosticsHandler diagnosticsHandler;
+
+  private PartitionedToProguardMappingConverter(
+      StringConsumer consumer,
+      MappingPartitionFromKeySupplier partitionSupplier,
+      byte[] metadata,
+      DiagnosticsHandler diagnosticsHandler) {
+    this.consumer = consumer;
+    this.partitionSupplier = partitionSupplier;
+    this.metadata = metadata;
+    this.diagnosticsHandler = diagnosticsHandler;
+  }
+
+  public void run() throws RetracePartitionException {
+    MappingPartitionMetadataInternal metadataInternal =
+        MappingPartitionMetadataInternal.deserialize(
+            metadata, MapVersion.MAP_VERSION_UNKNOWN, diagnosticsHandler);
+    if (!metadataInternal.canGetPartitionKeys()) {
+      throw new RetracePartitionException("Cannot obtain all partition keys from metadata");
+    }
+    // TODO(b/274893426): Account for preamble.
+    ClassNameMapper classNameMapper = ClassNameMapper.builder().build();
+    for (String partitionKey : metadataInternal.getPartitionKeys()) {
+      LineReader reader =
+          new ProguardMapReaderWithFilteringInputBuffer(
+              new ByteArrayInputStream(partitionSupplier.get(partitionKey)), alwaysTrue(), true);
+      try {
+        classNameMapper =
+            ClassNameMapper.mapperFromLineReaderWithFiltering(
+                    reader, metadataInternal.getMapVersion(), diagnosticsHandler, true, true)
+                .combine(classNameMapper);
+      } catch (IOException e) {
+        throw new RetracePartitionException(e);
+      }
+    }
+    classNameMapper.sorted().write(new ProguardMapWriter(consumer, diagnosticsHandler));
+  }
+
+  private static class ProguardMapWriter implements ChainableStringConsumer {
+
+    private final StringConsumer consumer;
+    private final DiagnosticsHandler diagnosticsHandler;
+
+    private ProguardMapWriter(StringConsumer consumer, DiagnosticsHandler diagnosticsHandler) {
+      this.consumer = consumer;
+      this.diagnosticsHandler = diagnosticsHandler;
+    }
+
+    @Override
+    public ProguardMapWriter accept(String string) {
+      consumer.accept(string, diagnosticsHandler);
+      return this;
+    }
+  }
+
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  public static class Builder {
+
+    private StringConsumer consumer;
+    private MappingPartitionFromKeySupplier partitionSupplier;
+    private byte[] metadata;
+    private DiagnosticsHandler diagnosticsHandler;
+
+    public Builder setConsumer(StringConsumer consumer) {
+      this.consumer = consumer;
+      return this;
+    }
+
+    public Builder setPartitionSupplier(MappingPartitionFromKeySupplier partitionSupplier) {
+      this.partitionSupplier = partitionSupplier;
+      return this;
+    }
+
+    public Builder setMetadata(byte[] metadata) {
+      this.metadata = metadata;
+      return this;
+    }
+
+    public Builder setDiagnosticsHandler(DiagnosticsHandler diagnosticsHandler) {
+      this.diagnosticsHandler = diagnosticsHandler;
+      return this;
+    }
+
+    public PartitionedToProguardMappingConverter build() {
+      return new PartitionedToProguardMappingConverter(
+          consumer, partitionSupplier, metadata, diagnosticsHandler);
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/Retrace.java b/src/main/java/com/android/tools/r8/retrace/Retrace.java
index 8f619ac..3d2740a 100644
--- a/src/main/java/com/android/tools/r8/retrace/Retrace.java
+++ b/src/main/java/com/android/tools/r8/retrace/Retrace.java
@@ -425,7 +425,10 @@
       run(mappedArgs, retraceDiagnosticsHandler);
     } catch (Throwable t) {
       throw failWithFakeEntry(
-          retraceDiagnosticsHandler, t, RetraceFailedException::new, RetraceAbortException.class);
+          retraceDiagnosticsHandler,
+          t,
+          (message, cause, ignore) -> new RetraceFailedException(message, cause),
+          RetraceAbortException.class);
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/MappingPartitionKeyStrategy.java b/src/main/java/com/android/tools/r8/retrace/internal/MappingPartitionKeyStrategy.java
index ad53968..b31d984 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/MappingPartitionKeyStrategy.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/MappingPartitionKeyStrategy.java
@@ -5,11 +5,22 @@
 package com.android.tools.r8.retrace.internal;
 
 public enum MappingPartitionKeyStrategy {
-  OBFUSCATED_TYPE_NAME_AS_KEY(0);
+  OBFUSCATED_TYPE_NAME_AS_KEY(0),
+  OBFUSCATED_TYPE_NAME_AS_KEY_WITH_PARTITIONS(1);
 
-  final int serializedKey;
+  private static final MappingPartitionKeyStrategy DEFAULT_STRATEGY = OBFUSCATED_TYPE_NAME_AS_KEY;
+
+  private final int serializedKey;
 
   MappingPartitionKeyStrategy(int serializedKey) {
     this.serializedKey = serializedKey;
   }
+
+  public int getSerializedKey() {
+    return serializedKey;
+  }
+
+  public static MappingPartitionKeyStrategy getDefaultStrategy() {
+    return DEFAULT_STRATEGY;
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/MappingPartitionMetadataInternal.java b/src/main/java/com/android/tools/r8/retrace/internal/MappingPartitionMetadataInternal.java
index 6c2e3ca..0cee013 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/MappingPartitionMetadataInternal.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/MappingPartitionMetadataInternal.java
@@ -5,16 +5,19 @@
 package com.android.tools.r8.retrace.internal;
 
 import static com.android.tools.r8.retrace.internal.MappingPartitionKeyStrategy.OBFUSCATED_TYPE_NAME_AS_KEY;
+import static com.android.tools.r8.retrace.internal.MappingPartitionKeyStrategy.OBFUSCATED_TYPE_NAME_AS_KEY_WITH_PARTITIONS;
 
 import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.naming.MapVersion;
 import com.android.tools.r8.references.ClassReference;
 import com.android.tools.r8.retrace.MappingPartitionMetadata;
+import com.android.tools.r8.retrace.internal.MetadataPartitionCollection.LazyMetadataPartitionCollection;
 import com.android.tools.r8.utils.ExceptionDiagnostic;
 import com.google.common.primitives.Ints;
 import java.io.ByteArrayOutputStream;
 import java.io.DataOutputStream;
 import java.io.IOException;
+import java.util.Collection;
 
 public interface MappingPartitionMetadataInternal extends MappingPartitionMetadata {
 
@@ -22,26 +25,56 @@
 
   MapVersion getMapVersion();
 
-  byte ZERO_BYTE = (byte) 0;
+  default boolean canGetPartitionKeys() {
+    return false;
+  }
 
-  static MappingPartitionMetadataInternal createFromBytes(
+  default Collection<String> getPartitionKeys() {
+    return null;
+  }
+
+  byte ZERO_BYTE = (byte) 0;
+  // Magic byte put into the metadata
+  byte[] MAGIC = new byte[] {(byte) 0xAA, (byte) 0xA8};
+
+  static int magicOffset() {
+    return MAGIC.length;
+  }
+
+  static MappingPartitionMetadataInternal deserialize(
       byte[] bytes, MapVersion fallBackMapVersion, DiagnosticsHandler diagnosticsHandler) {
     if (bytes == null) {
-      return obfuscatedTypeNameAsKey(fallBackMapVersion);
-    } else if (bytes.length > 2) {
-      int serializedStrategyId = Ints.fromBytes(ZERO_BYTE, ZERO_BYTE, bytes[0], bytes[1]);
-      MapVersion mapVersion = MapVersion.fromName(new String(bytes, 2, bytes.length - 2));
-      if (serializedStrategyId == OBFUSCATED_TYPE_NAME_AS_KEY.serializedKey) {
-        return obfuscatedTypeNameAsKey(mapVersion);
+      return ObfuscatedTypeNameAsKeyMetadata.create(fallBackMapVersion);
+    }
+    if (bytes.length > 2) {
+      if (startsWithMagic(bytes)) {
+        int serializedKey =
+            Ints.fromBytes(ZERO_BYTE, ZERO_BYTE, bytes[magicOffset()], bytes[magicOffset() + 1]);
+        if (serializedKey == OBFUSCATED_TYPE_NAME_AS_KEY_WITH_PARTITIONS.getSerializedKey()) {
+          return ObfuscatedTypeNameAsKeyMetadataWithPartitionNames.deserialize(bytes);
+        }
+      } else if (OBFUSCATED_TYPE_NAME_AS_KEY.getSerializedKey()
+          == Ints.fromBytes(ZERO_BYTE, ZERO_BYTE, bytes[0], bytes[1])) {
+        return ObfuscatedTypeNameAsKeyMetadata.deserialize(bytes);
       }
     }
-    RuntimeException exception = new RuntimeException("Unable to build key strategy from metadata");
+    // If we arrived here then we could not deserialize the metadata.
+    RetracePartitionException exception =
+        new RetracePartitionException("Unknown map partition strategy for metadata");
     diagnosticsHandler.error(new ExceptionDiagnostic(exception));
     throw exception;
   }
 
-  static ObfuscatedTypeNameAsKeyMetadata obfuscatedTypeNameAsKey(MapVersion mapVersion) {
-    return new ObfuscatedTypeNameAsKeyMetadata(mapVersion);
+  private static boolean startsWithMagic(byte[] bytes) {
+    if (bytes.length < MAGIC.length) {
+      return false;
+    }
+    for (int i = 0; i < MAGIC.length; i++) {
+      if (bytes[i] != MAGIC[i]) {
+        return false;
+      }
+    }
+    return true;
   }
 
   class ObfuscatedTypeNameAsKeyMetadata implements MappingPartitionMetadataInternal {
@@ -62,12 +95,14 @@
       return mapVersion;
     }
 
+    // The format is:
+    // <type:short><map-version>
     @Override
     public byte[] getBytes() {
       try {
         ByteArrayOutputStream temp = new ByteArrayOutputStream();
         DataOutputStream dataOutputStream = new DataOutputStream(temp);
-        dataOutputStream.writeShort(OBFUSCATED_TYPE_NAME_AS_KEY.serializedKey);
+        dataOutputStream.writeShort(OBFUSCATED_TYPE_NAME_AS_KEY.getSerializedKey());
         dataOutputStream.writeBytes(mapVersion.getName());
         dataOutputStream.close();
         return temp.toByteArray();
@@ -75,5 +110,83 @@
         throw new RuntimeException(e);
       }
     }
+
+    public static ObfuscatedTypeNameAsKeyMetadata deserialize(byte[] bytes) {
+      MapVersion mapVersion = MapVersion.fromName(new String(bytes, 2, bytes.length - 2));
+      return create(mapVersion);
+    }
+
+    public static ObfuscatedTypeNameAsKeyMetadata create(MapVersion mapVersion) {
+      return new ObfuscatedTypeNameAsKeyMetadata(mapVersion);
+    }
+  }
+
+  class ObfuscatedTypeNameAsKeyMetadataWithPartitionNames
+      implements MappingPartitionMetadataInternal {
+
+    private final MapVersion mapVersion;
+    private final MetadataPartitionCollection metadataPartitionCollection;
+
+    private ObfuscatedTypeNameAsKeyMetadataWithPartitionNames(
+        MapVersion mapVersion, MetadataPartitionCollection metadataPartitionCollection) {
+      this.mapVersion = mapVersion;
+      this.metadataPartitionCollection = metadataPartitionCollection;
+    }
+
+    public static ObfuscatedTypeNameAsKeyMetadataWithPartitionNames create(
+        MapVersion mapVersion, MetadataPartitionCollection metadataPartitionCollection) {
+      return new ObfuscatedTypeNameAsKeyMetadataWithPartitionNames(
+          mapVersion, metadataPartitionCollection);
+    }
+
+    @Override
+    public String getKey(ClassReference classReference) {
+      return classReference.getTypeName();
+    }
+
+    @Override
+    public MapVersion getMapVersion() {
+      return mapVersion;
+    }
+
+    @Override
+    public boolean canGetPartitionKeys() {
+      return true;
+    }
+
+    @Override
+    public Collection<String> getPartitionKeys() {
+      return metadataPartitionCollection.getPartitionKeys();
+    }
+
+    // The format is:
+    // <type:short><map-version-length:short><map-version>[<partition_key>]
+    @Override
+    public byte[] getBytes() {
+      try {
+        ByteArrayOutputStream temp = new ByteArrayOutputStream();
+        DataOutputStream dataOutputStream = new DataOutputStream(temp);
+        dataOutputStream.write(MAGIC);
+        dataOutputStream.writeShort(OBFUSCATED_TYPE_NAME_AS_KEY_WITH_PARTITIONS.getSerializedKey());
+        String name = mapVersion.getName();
+        dataOutputStream.writeShort(name.length());
+        dataOutputStream.writeBytes(name);
+        dataOutputStream.write(metadataPartitionCollection.serialize());
+        dataOutputStream.close();
+        return temp.toByteArray();
+      } catch (IOException e) {
+        throw new RuntimeException(e);
+      }
+    }
+
+    public static ObfuscatedTypeNameAsKeyMetadataWithPartitionNames deserialize(byte[] bytes) {
+      int start = magicOffset();
+      int length = Ints.fromBytes(ZERO_BYTE, ZERO_BYTE, bytes[start + 2], bytes[start + 3]);
+      MapVersion mapVersion = MapVersion.fromName(new String(bytes, start + 4, length));
+      int partitionCollectionStartIndex = start + 4 + length;
+      return ObfuscatedTypeNameAsKeyMetadataWithPartitionNames.create(
+          mapVersion,
+          new LazyMetadataPartitionCollection(bytes, partitionCollectionStartIndex, bytes.length));
+    }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/MetadataPartitionCollection.java b/src/main/java/com/android/tools/r8/retrace/internal/MetadataPartitionCollection.java
new file mode 100644
index 0000000..aef50f0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/internal/MetadataPartitionCollection.java
@@ -0,0 +1,72 @@
+// Copyright (c) 2023, 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.retrace.internal;
+
+import com.android.tools.r8.utils.StringUtils;
+import java.nio.charset.StandardCharsets;
+import java.util.Collection;
+import java.util.Collections;
+
+public class MetadataPartitionCollection {
+
+  private static final char SEPARATOR = ';';
+
+  private final Collection<String> partitionKeys;
+
+  private MetadataPartitionCollection(Collection<String> partitionKeys) {
+    this.partitionKeys = partitionKeys;
+  }
+
+  public Collection<String> getPartitionKeys() {
+    return partitionKeys;
+  }
+
+  public byte[] serialize() {
+    return StringUtils.join(SEPARATOR + "", partitionKeys).getBytes(StandardCharsets.UTF_8);
+  }
+
+  public static MetadataPartitionCollection create(Collection<String> partitionKeys) {
+    return new MetadataPartitionCollection(partitionKeys);
+  }
+
+  public static MetadataPartitionCollection createLazy(
+      byte[] bytes, int partitionCollectionStartIndex, int partitionCollectionEndIndex) {
+    return new LazyMetadataPartitionCollection(
+        bytes, partitionCollectionStartIndex, partitionCollectionEndIndex);
+  }
+
+  public static class LazyMetadataPartitionCollection extends MetadataPartitionCollection {
+
+    private final byte[] bytes;
+    private final int partitionCollectionStartIndex;
+    private final int partitionCollectionEndIndex;
+    private MetadataPartitionCollection metadataPartitionCollection = null;
+
+    public LazyMetadataPartitionCollection(
+        byte[] bytes, int partitionCollectionStartIndex, int partitionCollectionEndIndex) {
+      super(Collections.emptyList());
+      this.bytes = bytes;
+      this.partitionCollectionStartIndex = partitionCollectionStartIndex;
+      this.partitionCollectionEndIndex = partitionCollectionEndIndex;
+    }
+
+    @Override
+    public Collection<String> getPartitionKeys() {
+      if (metadataPartitionCollection == null) {
+        metadataPartitionCollection = deserialize();
+      }
+      return metadataPartitionCollection.getPartitionKeys();
+    }
+
+    private MetadataPartitionCollection deserialize() {
+      String allKeys =
+          new String(
+              bytes,
+              partitionCollectionStartIndex,
+              partitionCollectionEndIndex - partitionCollectionStartIndex);
+      return create(StringUtils.split(allKeys, SEPARATOR));
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/PartitionMappingSupplierImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/PartitionMappingSupplierImpl.java
index d37d45c..7d663fb 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/PartitionMappingSupplierImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/PartitionMappingSupplierImpl.java
@@ -65,7 +65,7 @@
       return mappingPartitionMetadataCache;
     }
     return mappingPartitionMetadataCache =
-        MappingPartitionMetadataInternal.createFromBytes(
+        MappingPartitionMetadataInternal.deserialize(
             metadata, fallbackMapVersion, diagnosticsHandler);
   }
 
@@ -77,6 +77,7 @@
   }
 
   private void registerKeyUse(String key) {
+    // TODO(b/274735214): only call the register partition if we have a partition for it.
     if (!builtKeys.contains(key) && pendingKeys.add(key)) {
       registerPartitionCallback.register(key);
     }
@@ -103,9 +104,14 @@
     }
     for (String pendingKey : pendingKeys) {
       try {
+        byte[] suppliedPartition = partitionSupplier.get(pendingKey);
+        // TODO(b/274735214): only expect a partition if have generated one for the key.
+        if (suppliedPartition == null) {
+          continue;
+        }
         LineReader reader =
             new ProguardMapReaderWithFilteringInputBuffer(
-                new ByteArrayInputStream(partitionSupplier.get(pendingKey)), alwaysTrue(), true);
+                new ByteArrayInputStream(suppliedPartition), alwaysTrue(), true);
         classNameMapper =
             ClassNameMapper.mapperFromLineReaderWithFiltering(
                     reader, metadata.getMapVersion(), diagnosticsHandler, true, allowExperimental)
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/ProguardMapPartitionerOnClassNameToText.java b/src/main/java/com/android/tools/r8/retrace/internal/ProguardMapPartitionerOnClassNameToText.java
index 5fd87ef..06ede47 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/ProguardMapPartitionerOnClassNameToText.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/ProguardMapPartitionerOnClassNameToText.java
@@ -18,19 +18,22 @@
 import com.android.tools.r8.retrace.ProguardMapPartitioner;
 import com.android.tools.r8.retrace.ProguardMapPartitionerBuilder;
 import com.android.tools.r8.retrace.ProguardMapProducer;
+import com.android.tools.r8.retrace.internal.MappingPartitionMetadataInternal.ObfuscatedTypeNameAsKeyMetadata;
+import com.android.tools.r8.retrace.internal.MappingPartitionMetadataInternal.ObfuscatedTypeNameAsKeyMetadataWithPartitionNames;
 import com.android.tools.r8.retrace.internal.ProguardMapReaderWithFiltering.ProguardMapReaderWithFilteringInputBuffer;
 import com.android.tools.r8.retrace.internal.ProguardMapReaderWithFiltering.ProguardMapReaderWithFilteringMappedBuffer;
 import com.android.tools.r8.utils.ExceptionDiagnostic;
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.TriConsumer;
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
 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;
@@ -42,22 +45,46 @@
   private final DiagnosticsHandler diagnosticsHandler;
   private final boolean allowEmptyMappedRanges;
   private final boolean allowExperimentalMapping;
+  private final MappingPartitionKeyStrategy mappingPartitionKeyStrategy;
 
   private ProguardMapPartitionerOnClassNameToText(
       ProguardMapProducer proguardMapProducer,
       Consumer<MappingPartition> mappingPartitionConsumer,
       DiagnosticsHandler diagnosticsHandler,
       boolean allowEmptyMappedRanges,
-      boolean allowExperimentalMapping) {
+      boolean allowExperimentalMapping,
+      MappingPartitionKeyStrategy mappingPartitionKeyStrategy) {
     this.proguardMapProducer = proguardMapProducer;
     this.mappingPartitionConsumer = mappingPartitionConsumer;
     this.diagnosticsHandler = diagnosticsHandler;
     this.allowEmptyMappedRanges = allowEmptyMappedRanges;
     this.allowExperimentalMapping = allowExperimentalMapping;
+    this.mappingPartitionKeyStrategy = mappingPartitionKeyStrategy;
   }
 
-  @Override
-  public MappingPartitionMetadata run() throws IOException {
+  private ClassNameMapper getPartitionsFromProguardMapProducer(
+      TriConsumer<ClassNameMapper, ClassNamingForNameMapper, String> consumer) throws IOException {
+    if (proguardMapProducer instanceof ProguardMapProducerInternal) {
+      return getPartitionsFromInternalProguardMapProducer(consumer);
+    } else {
+      return getPartitionsFromStringBackedProguardMapProducer(consumer);
+    }
+  }
+
+  private ClassNameMapper getPartitionsFromInternalProguardMapProducer(
+      TriConsumer<ClassNameMapper, ClassNamingForNameMapper, String> consumer) {
+    ClassNameMapper classNameMapper =
+        ((ProguardMapProducerInternal) proguardMapProducer).getClassNameMapper();
+    classNameMapper
+        .getClassNameMappings()
+        .forEach(
+            (key, mappingForClass) ->
+                consumer.accept(classNameMapper, mappingForClass, mappingForClass.toString()));
+    return classNameMapper;
+  }
+
+  private ClassNameMapper getPartitionsFromStringBackedProguardMapProducer(
+      TriConsumer<ClassNameMapper, ClassNamingForNameMapper, String> consumer) throws IOException {
     PartitionLineReader reader =
         new PartitionLineReader(
             proguardMapProducer.isFileBacked()
@@ -69,8 +96,11 @@
     // mappings.
     ClassNameMapper classMapper =
         ClassNameMapper.mapperFromLineReaderWithFiltering(
-            reader, MapVersion.MAP_VERSION_UNKNOWN, diagnosticsHandler, true, true);
-    // We can then iterate over all sections.
+            reader,
+            MapVersion.MAP_VERSION_UNKNOWN,
+            diagnosticsHandler,
+            allowEmptyMappedRanges,
+            allowExperimentalMapping);
     reader.forEachClassMapping(
         (classMapping, entries) -> {
           try {
@@ -78,47 +108,76 @@
             ClassNameMapper classNameMapper =
                 ClassNameMapper.mapperFromString(
                     payload, null, allowEmptyMappedRanges, allowExperimentalMapping, false);
-            if (classNameMapper.getClassNameMappings().size() != 1) {
+            Map<String, ClassNamingForNameMapper> classNameMappings =
+                classNameMapper.getClassNameMappings();
+            if (classNameMappings.size() != 1) {
               diagnosticsHandler.error(
                   new StringDiagnostic("Multiple class names in payload\n: " + payload));
               return;
             }
-            Entry<String, ClassNamingForNameMapper> currentClassMapping =
-                classNameMapper.getClassNameMappings().entrySet().iterator().next();
-            ClassNamingForNameMapper value = currentClassMapping.getValue();
-            Set<String> seenMappings = new HashSet<>();
-            StringBuilder payloadWithClassReferences = new StringBuilder();
-            value.visitAllFullyQualifiedReferences(
-                holder -> {
-                  if (seenMappings.add(holder)) {
-                    payloadWithClassReferences.append(
-                        getSourceFileMapping(holder, classMapper.getSourceFile(holder)));
-                  }
-                });
-            payloadWithClassReferences.append(payload);
-            mappingPartitionConsumer.accept(
-                new MappingPartitionImpl(
-                    currentClassMapping.getKey(),
-                    payloadWithClassReferences.toString().getBytes(StandardCharsets.UTF_8)));
+            consumer.accept(classMapper, classNameMappings.values().iterator().next(), payload);
           } catch (IOException e) {
             diagnosticsHandler.error(new ExceptionDiagnostic(e));
           }
         });
+    return classMapper;
+  }
+
+  @Override
+  public MappingPartitionMetadata run() throws IOException {
+    HashSet<String> keys = new LinkedHashSet<>();
+    // We can then iterate over all sections.
+    ClassNameMapper classMapper =
+        getPartitionsFromProguardMapProducer(
+            (classNameMapper, classNamingForNameMapper, payload) -> {
+              Set<String> seenMappings = new HashSet<>();
+              StringBuilder payloadWithClassReferences = new StringBuilder();
+              Map<String, String> lookupMap =
+                  classNameMapper.getObfuscatedToOriginalMapping().inverse;
+              classNamingForNameMapper.visitAllFullyQualifiedReferences(
+                  holder -> {
+                    if (seenMappings.add(holder)) {
+                      payloadWithClassReferences.append(
+                          getSourceFileMapping(
+                              holder,
+                              lookupMap.get(holder),
+                              classNameMapper.getSourceFile(holder)));
+                    }
+                  });
+              payloadWithClassReferences.append(payload);
+              mappingPartitionConsumer.accept(
+                  new MappingPartitionImpl(
+                      classNamingForNameMapper.renamedName,
+                      payloadWithClassReferences.toString().getBytes(StandardCharsets.UTF_8)));
+              keys.add(classNamingForNameMapper.renamedName);
+            });
     MapVersion mapVersion = MapVersion.MAP_VERSION_UNKNOWN;
     MapVersionMappingInformation mapVersionInfo = classMapper.getFirstMapVersionInformation();
     if (mapVersionInfo != null) {
       mapVersion = mapVersionInfo.getMapVersion();
     }
-    return MappingPartitionMetadataInternal.obfuscatedTypeNameAsKey(mapVersion);
+    if (mappingPartitionKeyStrategy == MappingPartitionKeyStrategy.OBFUSCATED_TYPE_NAME_AS_KEY) {
+      return ObfuscatedTypeNameAsKeyMetadata.create(mapVersion);
+    } else if (mappingPartitionKeyStrategy
+        == MappingPartitionKeyStrategy.OBFUSCATED_TYPE_NAME_AS_KEY_WITH_PARTITIONS) {
+      return ObfuscatedTypeNameAsKeyMetadataWithPartitionNames.create(
+          mapVersion, MetadataPartitionCollection.create(keys));
+    } else {
+      RetracePartitionException retraceError =
+          new RetracePartitionException("Unknown mapping partitioning strategy");
+      diagnosticsHandler.error(new ExceptionDiagnostic(retraceError));
+      throw retraceError;
+    }
   }
 
-  private String getSourceFileMapping(String className, String sourceFile) {
+  private String getSourceFileMapping(
+      String className, String obfuscatedClassName, String sourceFile) {
     if (sourceFile == null) {
       return "";
     }
     return className
         + " -> "
-        + className
+        + (obfuscatedClassName == null ? className : obfuscatedClassName)
         + ":"
         + "\n  # "
         + FileNameInformation.build(sourceFile).serialize()
@@ -129,13 +188,11 @@
 
     private final ProguardMapReaderWithFiltering lineReader;
     private final Map<String, List<String>> readSections = new LinkedHashMap<>();
-    private final List<String> preamble;
     private List<String> currentList;
 
     public PartitionLineReader(ProguardMapReaderWithFiltering lineReader) {
       this.lineReader = lineReader;
       currentList = new ArrayList<>();
-      preamble = currentList;
     }
 
     @Override
@@ -166,11 +223,11 @@
       implements ProguardMapPartitionerBuilder<
           ProguardMapPartitionerBuilderImpl, ProguardMapPartitionerOnClassNameToText> {
 
-    private ProguardMapProducer proguardMapProducer;
-    private Consumer<MappingPartition> mappingPartitionConsumer;
-    private final DiagnosticsHandler diagnosticsHandler;
-    private boolean allowEmptyMappedRanges = false;
-    private boolean allowExperimentalMapping = false;
+    protected ProguardMapProducer proguardMapProducer;
+    protected Consumer<MappingPartition> mappingPartitionConsumer;
+    protected final DiagnosticsHandler diagnosticsHandler;
+    protected boolean allowEmptyMappedRanges = false;
+    protected boolean allowExperimentalMapping = false;
 
     public ProguardMapPartitionerBuilderImpl(DiagnosticsHandler diagnosticsHandler) {
       this.diagnosticsHandler = diagnosticsHandler;
@@ -211,7 +268,38 @@
           mappingPartitionConsumer,
           diagnosticsHandler,
           allowEmptyMappedRanges,
-          allowExperimentalMapping);
+          allowExperimentalMapping,
+          MappingPartitionKeyStrategy.getDefaultStrategy());
+    }
+  }
+
+  // This class should not be exposed to clients and is only used from tests to control the
+  // partitioning strategy.
+  public static class ProguardMapPartitionerBuilderImplInternal
+      extends ProguardMapPartitionerBuilderImpl {
+
+    private MappingPartitionKeyStrategy mappingPartitionKeyStrategy =
+        MappingPartitionKeyStrategy.OBFUSCATED_TYPE_NAME_AS_KEY_WITH_PARTITIONS;
+
+    public ProguardMapPartitionerBuilderImplInternal(DiagnosticsHandler diagnosticsHandler) {
+      super(diagnosticsHandler);
+    }
+
+    public ProguardMapPartitionerBuilderImplInternal setMappingPartitionKeyStrategy(
+        MappingPartitionKeyStrategy mappingPartitionKeyStrategy) {
+      this.mappingPartitionKeyStrategy = mappingPartitionKeyStrategy;
+      return this;
+    }
+
+    @Override
+    public ProguardMapPartitionerOnClassNameToText build() {
+      return new ProguardMapPartitionerOnClassNameToText(
+          proguardMapProducer,
+          mappingPartitionConsumer,
+          diagnosticsHandler,
+          allowEmptyMappedRanges,
+          allowExperimentalMapping,
+          mappingPartitionKeyStrategy);
     }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/ProguardMapProducerInternal.java b/src/main/java/com/android/tools/r8/retrace/internal/ProguardMapProducerInternal.java
new file mode 100644
index 0000000..6650458
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/internal/ProguardMapProducerInternal.java
@@ -0,0 +1,29 @@
+// Copyright (c) 2023, 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.retrace.internal;
+
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.retrace.ProguardMapProducer;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ProguardMapProducerInternal implements ProguardMapProducer {
+
+  private final ClassNameMapper classNameMapper;
+
+  public ProguardMapProducerInternal(ClassNameMapper classNameMapper) {
+    this.classNameMapper = classNameMapper;
+  }
+
+  public ClassNameMapper getClassNameMapper() {
+    return classNameMapper;
+  }
+
+  @Override
+  public InputStream get() throws IOException {
+    throw new Unreachable("Should never get on ProguardMapProducerInternal");
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/ProguardMappingSupplierImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/ProguardMappingSupplierImpl.java
index 14f41ed..d791a5c 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/ProguardMappingSupplierImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/ProguardMappingSupplierImpl.java
@@ -8,8 +8,8 @@
 import com.android.tools.r8.naming.ClassNameMapper;
 import com.android.tools.r8.naming.LineReader;
 import com.android.tools.r8.naming.MapVersion;
-import com.android.tools.r8.naming.ProguardMapSupplier.ProguardMapChecker;
-import com.android.tools.r8.naming.ProguardMapSupplier.ProguardMapChecker.VerifyMappingFileHashResult;
+import com.android.tools.r8.naming.ProguardMapChecker;
+import com.android.tools.r8.naming.ProguardMapChecker.VerifyMappingFileHashResult;
 import com.android.tools.r8.naming.mappinginformation.MapVersionMappingInformation;
 import com.android.tools.r8.references.ClassReference;
 import com.android.tools.r8.retrace.InvalidMappingFileException;
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetracePartitionException.java b/src/main/java/com/android/tools/r8/retrace/internal/RetracePartitionException.java
new file mode 100644
index 0000000..7e6e924
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetracePartitionException.java
@@ -0,0 +1,19 @@
+// Copyright (c) 2023, 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.retrace.internal;
+
+import com.android.tools.r8.Keep;
+
+@Keep
+public class RetracePartitionException extends RuntimeException {
+
+  public RetracePartitionException(String message) {
+    super(message);
+  }
+
+  public RetracePartitionException(Exception e) {
+    super(e);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/AssumeInfoCollection.java b/src/main/java/com/android/tools/r8/shaking/AssumeInfoCollection.java
index efff258..f9cda2f 100644
--- a/src/main/java/com/android/tools/r8/shaking/AssumeInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/shaking/AssumeInfoCollection.java
@@ -45,6 +45,13 @@
     return get(member.getReference());
   }
 
+  public boolean isMaterializableInAllContexts(
+      AppView<AppInfoWithLiveness> appView, DexClassAndMember<?, ?> member) {
+    AbstractValue assumeValue = get(member).getAssumeValue();
+    return assumeValue.isSingleValue()
+        && assumeValue.asSingleValue().isMaterializableInAllContexts(appView);
+  }
+
   public boolean isSideEffectFree(DexMember<?, ?> member) {
     return get(member).isSideEffectFree();
   }
diff --git a/src/main/java/com/android/tools/r8/tracereferences/Tracer.java b/src/main/java/com/android/tools/r8/tracereferences/Tracer.java
index b430854..8715d7d 100644
--- a/src/main/java/com/android/tools/r8/tracereferences/Tracer.java
+++ b/src/main/java/com/android/tools/r8/tracereferences/Tracer.java
@@ -278,19 +278,26 @@
         MethodLookupResult lookupResult = graphLens().lookupInvokeDirect(method, getContext());
         assert lookupResult.getType().isDirect();
         DexMethod rewrittenMethod = lookupResult.getReference();
-        BooleanBox seenMethod = new BooleanBox();
-        appView
-            .contextIndependentDefinitionForWithResolutionResult(rewrittenMethod.getHolderType())
-            .forEachClassResolutionResult(
-                holder -> {
-                  DexClassAndMethod target = rewrittenMethod.lookupMemberOnClass(holder);
-                  if (target != null) {
-                    handleRewrittenMethodReference(rewrittenMethod, target);
-                    seenMethod.set();
-                  }
-                });
-        if (seenMethod.isFalse()) {
-          handleRewrittenMethodReference(rewrittenMethod, (DexClassAndMethod) null);
+        if (getContext().getHolder().originatesFromDexResource()) {
+          handleRewrittenMethodResolution(
+              rewrittenMethod,
+              appInfo().unsafeResolveMethodDueToDexFormat(rewrittenMethod),
+              SingleResolutionResult::getResolutionPair);
+        } else {
+          BooleanBox seenMethod = new BooleanBox();
+          appView
+              .contextIndependentDefinitionForWithResolutionResult(rewrittenMethod.getHolderType())
+              .forEachClassResolutionResult(
+                  holder -> {
+                    DexClassAndMethod target = rewrittenMethod.lookupMemberOnClass(holder);
+                    if (target != null) {
+                      handleRewrittenMethodReference(rewrittenMethod, target);
+                      seenMethod.set();
+                    }
+                  });
+          if (seenMethod.isFalse()) {
+            handleRewrittenMethodReference(rewrittenMethod, (DexClassAndMethod) null);
+          }
         }
       }
 
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidAppConsumers.java b/src/main/java/com/android/tools/r8/utils/AndroidAppConsumers.java
index 7a77e25..653e567 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidAppConsumers.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidAppConsumers.java
@@ -14,8 +14,11 @@
 import com.android.tools.r8.DexIndexedConsumer.ForwardingConsumer;
 import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.ProgramConsumer;
+import com.android.tools.r8.ProguardMapConsumer;
 import com.android.tools.r8.ResourceException;
 import com.android.tools.r8.StringConsumer;
+import com.android.tools.r8.naming.MultiProguardMapConsumer;
+import com.android.tools.r8.naming.ProguardMapStringConsumer;
 import com.android.tools.r8.origin.Origin;
 import com.google.common.io.ByteStreams;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceAVLTreeMap;
@@ -33,7 +36,7 @@
   private boolean closed = false;
 
   private ProgramConsumer programConsumer = null;
-  private StringConsumer proguardMapConsumer = null;
+  private ProguardMapConsumer proguardMapConsumer = null;
 
   public AndroidAppConsumers() {
     // Nothing to do.
@@ -45,7 +48,8 @@
 
   public AndroidAppConsumers(InternalOptions options) {
     options.programConsumer = wrapProgramConsumer(options.programConsumer);
-    options.proguardMapConsumer = wrapProguardMapConsumer(options.proguardMapConsumer);
+    options.proguardMapConsumer =
+        wrapProguardMapConsumer(options.proguardMapConsumer, options.reporter);
   }
 
   public ProgramConsumer wrapProgramConsumer(ProgramConsumer consumer) {
@@ -65,30 +69,37 @@
     return programConsumer;
   }
 
-  public StringConsumer wrapProguardMapConsumer(StringConsumer consumer) {
+  public ProguardMapConsumer wrapProguardMapConsumer(
+      ProguardMapConsumer consumer, DiagnosticsHandler diagnosticsHandler) {
     assert proguardMapConsumer == null;
     if (consumer != null) {
       proguardMapConsumer =
-          new StringConsumer.ForwardingConsumer(consumer) {
-            StringBuilder stringBuilder = null;
+          MultiProguardMapConsumer.builder()
+              .addProguardMapConsumer(consumer)
+              .addProguardMapConsumer(
+                  ProguardMapStringConsumer.builder()
+                      .setStringConsumer(
+                          new StringConsumer() {
+                            StringBuilder stringBuilder = null;
 
-            @Override
-            public void accept(String string, DiagnosticsHandler handler) {
-              super.accept(string, handler);
-              if (stringBuilder == null) {
-                stringBuilder = new StringBuilder();
-              }
-              stringBuilder.append(string);
-            }
+                            @Override
+                            public void accept(String string, DiagnosticsHandler handler) {
+                              if (stringBuilder == null) {
+                                stringBuilder = new StringBuilder();
+                              }
+                              stringBuilder.append(string);
+                            }
 
-            @Override
-            public void finished(DiagnosticsHandler handler) {
-              super.finished(handler);
-              if (stringBuilder != null) {
-                builder.setProguardMapOutputData(stringBuilder.toString());
-              }
-            }
-          };
+                            @Override
+                            public void finished(DiagnosticsHandler handler) {
+                              if (stringBuilder != null) {
+                                builder.setProguardMapOutputData(stringBuilder.toString());
+                              }
+                            }
+                          })
+                      .setDiagnosticsHandler(diagnosticsHandler)
+                      .build())
+              .build();
     }
     return proguardMapConsumer;
   }
diff --git a/src/main/java/com/android/tools/r8/utils/CancelCompilationException.java b/src/main/java/com/android/tools/r8/utils/CancelCompilationException.java
new file mode 100644
index 0000000..0070efe
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/CancelCompilationException.java
@@ -0,0 +1,6 @@
+// Copyright (c) 2023, 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.utils;
+
+public class CancelCompilationException extends RuntimeException {}
diff --git a/src/main/java/com/android/tools/r8/utils/CollectionUtils.java b/src/main/java/com/android/tools/r8/utils/CollectionUtils.java
index 934bb8d..f5eea66 100644
--- a/src/main/java/com/android/tools/r8/utils/CollectionUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/CollectionUtils.java
@@ -7,6 +7,7 @@
 import com.google.common.collect.ImmutableSet;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Comparator;
 import java.util.Set;
 import java.util.function.Consumer;
@@ -33,6 +34,12 @@
     }
   }
 
+  public static <T extends Comparable<T>> Collection<T> sort(Collection<T> items) {
+    ArrayList<T> ts = new ArrayList<>(items);
+    Collections.sort(ts);
+    return ts;
+  }
+
   public static <T> Collection<T> sort(Collection<T> items, Comparator<T> comparator) {
     ArrayList<T> ts = new ArrayList<>(items);
     ts.sort(comparator);
diff --git a/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java b/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java
index ef5c3ca..959a25e 100644
--- a/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java
@@ -5,6 +5,7 @@
 
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.InternalCompilationFailedExceptionUtils;
 import com.android.tools.r8.ResourceException;
 import com.android.tools.r8.StringConsumer;
 import com.android.tools.r8.Version;
@@ -19,7 +20,6 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.ExecutionException;
-import java.util.function.BiFunction;
 import java.util.function.Consumer;
 import java.util.function.Supplier;
 import java.util.stream.Collectors;
@@ -87,13 +87,16 @@
   private static CompilationFailedException failCompilation(
       Reporter reporter, Throwable topMostException) {
     return failWithFakeEntry(
-        reporter, topMostException, CompilationFailedException::new, AbortException.class);
+        reporter,
+        topMostException,
+        InternalCompilationFailedExceptionUtils::create,
+        AbortException.class);
   }
 
   public static <T extends Exception, A extends Exception> T failWithFakeEntry(
       DiagnosticsHandler diagnosticsHandler,
       Throwable topMostException,
-      BiFunction<String, Throwable, T> newException,
+      TriFunction<String, Throwable, Boolean, T> newException,
       Class<A> abortException) {
     // Find inner-most cause of the failure and compute origin, position and reported for the path.
     boolean hasBeenReported = false;
@@ -122,8 +125,13 @@
       innerMostCause.addSuppressed(topMostException);
     }
 
+    boolean cancelled =
+        topMostException instanceof CancelCompilationException
+            || innerMostCause instanceof CancelCompilationException;
+    assert !cancelled || topMostException == innerMostCause;
+
     // If no abort is seen, the exception is not reported, so report it now.
-    if (!hasBeenReported) {
+    if (!cancelled && !hasBeenReported) {
       diagnosticsHandler.error(new ExceptionDiagnostic(innerMostCause, origin, position));
     }
 
@@ -136,7 +144,7 @@
       message.append(", origin: ").append(origin);
     }
     // Create the final exception object.
-    T rethrow = newException.apply(message.toString(), innerMostCause);
+    T rethrow = newException.apply(message.toString(), innerMostCause, cancelled);
     // Replace its stack by the cause stack and insert version info at the top.
     String filename = "Version_" + Version.LABEL + ".java";
     StackTraceElement versionElement =
@@ -244,6 +252,8 @@
       Origin origin, Position position, Supplier<T> action) {
     try {
       return action.get();
+    } catch (CancelCompilationException e) {
+      throw e;
     } catch (RuntimeException e) {
       throw OriginAttachmentException.wrap(e, origin, position);
     }
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 f9bd041..8ce6323 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -6,6 +6,7 @@
 import static com.android.tools.r8.utils.AndroidApiLevel.B;
 import static com.android.tools.r8.utils.SystemPropertyUtils.parseSystemPropertyForDevelopmentOrDefault;
 
+import com.android.tools.r8.CancelCompilationChecker;
 import com.android.tools.r8.ClassFileConsumer;
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.DataResourceConsumer;
@@ -16,6 +17,7 @@
 import com.android.tools.r8.GlobalSyntheticsConsumer;
 import com.android.tools.r8.MapIdProvider;
 import com.android.tools.r8.ProgramConsumer;
+import com.android.tools.r8.ProguardMapConsumer;
 import com.android.tools.r8.SourceFileProvider;
 import com.android.tools.r8.StringConsumer;
 import com.android.tools.r8.SyntheticInfoConsumer;
@@ -74,6 +76,7 @@
 import com.android.tools.r8.naming.ClassNameMapper;
 import com.android.tools.r8.naming.MapVersion;
 import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagatorEventConsumer;
+import com.android.tools.r8.optimize.redundantbridgeremoval.RedundantBridgeRemovalOptions;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.position.Position;
 import com.android.tools.r8.profile.art.ArtProfileOptions;
@@ -165,6 +168,27 @@
     return itemFactory;
   }
 
+  // Internal state signifying that the compilation is cancelled.
+  // The state can only ever transition from false to true.
+  private final AtomicBoolean cancelled = new AtomicBoolean(false);
+  public CancelCompilationChecker cancelCompilationChecker = null;
+
+  public boolean checkIfCancelled() {
+    if (cancelCompilationChecker == null) {
+      assert !cancelled.get();
+      return false;
+    }
+    if (cancelled.get()) {
+      return true;
+    }
+    if (cancelCompilationChecker.cancel()) {
+      cancelled.set(true);
+      return true;
+    }
+    // Return the cancelled value in case another thread has cancelled.
+    return cancelled.get();
+  }
+
   public boolean hasProguardConfiguration() {
     return proguardConfiguration != null;
   }
@@ -862,6 +886,8 @@
   private final OpenClosedInterfacesOptions openClosedInterfacesOptions =
       new OpenClosedInterfacesOptions();
   private final ProtoShrinkingOptions protoShrinking = new ProtoShrinkingOptions();
+  private final RedundantBridgeRemovalOptions redundantBridgeRemovalOptions =
+      new RedundantBridgeRemovalOptions(this);
   private final KotlinOptimizationOptions kotlinOptimizationOptions =
       new KotlinOptimizationOptions();
   private final ApiModelTestingOptions apiModelTestingOptions = new ApiModelTestingOptions();
@@ -930,6 +956,10 @@
     return cfCodeAnalysisOptions;
   }
 
+  public RedundantBridgeRemovalOptions getRedundantBridgeRemovalOptions() {
+    return redundantBridgeRemovalOptions;
+  }
+
   public DumpInputFlags getDumpInputFlags() {
     return dumpInputFlags;
   }
@@ -1018,7 +1048,7 @@
 
   // If null, no proguard map needs to be computed.
   // If non null it must be and passed to the consumer.
-  public StringConsumer proguardMapConsumer = null;
+  public ProguardMapConsumer proguardMapConsumer = null;
 
   // If null, no usage information needs to be computed.
   // If non-null, it must be and is passed to the consumer.
@@ -2884,8 +2914,7 @@
   }
 
   public boolean canHaveNonReboundConstructorInvoke() {
-    // TODO(b/246679983): Turned off while diagnosing b/246679983.
-    return false && isGeneratingDex() && minApiLevel.isGreaterThanOrEqualTo(AndroidApiLevel.L);
+    return isGeneratingDex() && minApiLevel.isGreaterThanOrEqualTo(AndroidApiLevel.L);
   }
 
   // b/238399429 Some art 6 vms have issues with multiple monitors in the same method
diff --git a/src/main/java/com/android/tools/r8/utils/StringUtils.java b/src/main/java/com/android/tools/r8/utils/StringUtils.java
index a561718..9885ab6 100644
--- a/src/main/java/com/android/tools/r8/utils/StringUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/StringUtils.java
@@ -248,7 +248,11 @@
   }
 
   public static String unixLines(String... lines) {
-    return lines(Arrays.asList(lines), "\n");
+    return unixLines(Arrays.asList(lines));
+  }
+
+  public static String unixLines(List<String> lines) {
+    return lines(lines, "\n");
   }
 
   public static String withNativeLineSeparator(String s) {
diff --git a/src/main/java/com/android/tools/r8/utils/Timing.java b/src/main/java/com/android/tools/r8/utils/Timing.java
index 3947cae..dc7db26 100644
--- a/src/main/java/com/android/tools/r8/utils/Timing.java
+++ b/src/main/java/com/android/tools/r8/utils/Timing.java
@@ -62,11 +62,60 @@
     return Timing.EMPTY;
   }
 
+  private static class TimingWithCancellation extends Timing {
+    private final InternalOptions options;
+    private final Timing timing;
+
+    TimingWithCancellation(InternalOptions options, Timing timing) {
+      super("<cancel>", false);
+      this.options = options;
+      this.timing = timing;
+    }
+
+    @Override
+    public TimingMerger beginMerger(String title, int numberOfThreads) {
+      return timing.beginMerger(title, numberOfThreads);
+    }
+
+    @Override
+    public void begin(String title) {
+      if (options.checkIfCancelled()) {
+        throw new CancelCompilationException();
+      }
+      timing.begin(title);
+    }
+
+    @Override
+    public <E extends Exception> void time(String title, ThrowingAction<E> action) throws E {
+      timing.time(title, action);
+    }
+
+    @Override
+    public <T, E extends Exception> T time(String title, ThrowingSupplier<T, E> supplier) throws E {
+      return timing.time(title, supplier);
+    }
+
+    @Override
+    public void end() {
+      timing.end();
+    }
+
+    @Override
+    public void report() {
+      timing.report();
+    }
+  }
+
   public static Timing create(String title, InternalOptions options) {
     // We also create a timer when running assertions to validate wellformedness of the node stack.
-    return options.printTimes || InternalOptions.assertionsEnabled()
-        ? new Timing(title, options.printMemory)
-        : Timing.empty();
+    Timing timing =
+        options.printTimes || InternalOptions.assertionsEnabled()
+            ? new Timing(title, options.printMemory)
+            : Timing.empty();
+    if (options.cancelCompilationChecker != null) {
+      return new TimingWithCancellation(options, timing);
+    }
+    return timing;
   }
 
   public static Timing create(String title, boolean printMemory) {
diff --git a/src/test/java/com/android/tools/r8/CommandTestBase.java b/src/test/java/com/android/tools/r8/CommandTestBase.java
index 6c72b39..1ea2212 100644
--- a/src/test/java/com/android/tools/r8/CommandTestBase.java
+++ b/src/test/java/com/android/tools/r8/CommandTestBase.java
@@ -96,7 +96,7 @@
             try {
               command.getReporter().failIfPendingErrors();
             } catch (RuntimeException e) {
-              throw new CompilationFailedException();
+              throw InternalCompilationFailedExceptionUtils.createForTesting();
             }
           });
       fail("Failure expected");
@@ -144,7 +144,7 @@
             try {
               command.getReporter().failIfPendingErrors();
             } catch (RuntimeException e) {
-              throw new CompilationFailedException();
+              throw InternalCompilationFailedExceptionUtils.createForTesting();
             }
           });
       fail("Failure expected");
@@ -190,7 +190,7 @@
             try {
               command.getReporter().failIfPendingErrors();
             } catch (RuntimeException e) {
-              throw new CompilationFailedException();
+              throw InternalCompilationFailedExceptionUtils.createForTesting();
             }
           });
       fail("Expected failure");
@@ -209,7 +209,7 @@
             try {
               command.getReporter().failIfPendingErrors();
             } catch (RuntimeException e) {
-              throw new CompilationFailedException();
+              throw InternalCompilationFailedExceptionUtils.createForTesting();
             }
           });
       fail("Expected failure");
diff --git a/src/test/java/com/android/tools/r8/D8CommandTest.java b/src/test/java/com/android/tools/r8/D8CommandTest.java
index f22ce21..87b5338 100644
--- a/src/test/java/com/android/tools/r8/D8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/D8CommandTest.java
@@ -33,6 +33,7 @@
 import com.android.tools.r8.utils.ExtractMarkerUtils;
 import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.ZipUtils;
 import com.google.common.collect.ImmutableList;
@@ -812,7 +813,8 @@
 
     // Verify we found the same rule.
     StartupProfile startupProfile = startupProfileBuilder.build();
-    Collection<StartupProfileRule> startupItems = startupProfile.getRules();
+    Collection<StartupProfileRule> startupItems =
+        ListUtils.newArrayList(consumer -> startupProfile.forEachRule(consumer::accept));
     assertEquals(1, startupItems.size());
     StartupProfileRule startupItem = startupItems.iterator().next();
     startupItem.accept(
diff --git a/src/test/java/com/android/tools/r8/ExternalR8TestBuilder.java b/src/test/java/com/android/tools/r8/ExternalR8TestBuilder.java
index 2808f05..444d14b 100644
--- a/src/test/java/com/android/tools/r8/ExternalR8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/ExternalR8TestBuilder.java
@@ -197,7 +197,7 @@
       return new ExternalR8TestCompileResult(
           getState(), outputJar, processResult, proguardMap, getMinApiLevel(), getOutputMode());
     } catch (IOException e) {
-      throw new CompilationFailedException(e);
+      throw InternalCompilationFailedExceptionUtils.createForTesting(e);
     }
   }
 
diff --git a/src/test/java/com/android/tools/r8/L8CommandTest.java b/src/test/java/com/android/tools/r8/L8CommandTest.java
index e2d8b20..4b42d31 100644
--- a/src/test/java/com/android/tools/r8/L8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/L8CommandTest.java
@@ -18,6 +18,7 @@
 import com.android.tools.r8.StringConsumer.FileConsumer;
 import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
 import com.android.tools.r8.dex.Marker.Tool;
+import com.android.tools.r8.naming.ProguardMapStringConsumer;
 import com.android.tools.r8.origin.EmbeddedOrigin;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.references.Reference;
@@ -186,8 +187,10 @@
     assertNotNull(parsedCommand.getR8Command());
     InternalOptions internalOptions = parsedCommand.getR8Command().getInternalOptions();
     assertNotNull(internalOptions);
-    assertTrue(internalOptions.proguardMapConsumer instanceof StringConsumer.FileConsumer);
-    FileConsumer proguardMapConsumer = (FileConsumer) internalOptions.proguardMapConsumer;
+    assertTrue(internalOptions.proguardMapConsumer instanceof ProguardMapStringConsumer);
+    ProguardMapStringConsumer mapStringConsumer =
+        (ProguardMapStringConsumer) internalOptions.proguardMapConsumer;
+    FileConsumer proguardMapConsumer = (FileConsumer) mapStringConsumer.getStringConsumer();
     assertEquals(pgMap, proguardMapConsumer.getOutputPath());
   }
 
diff --git a/src/test/java/com/android/tools/r8/ProguardMapMarkerTest.java b/src/test/java/com/android/tools/r8/ProguardMapMarkerTest.java
index f827134..e38ecf7 100644
--- a/src/test/java/com/android/tools/r8/ProguardMapMarkerTest.java
+++ b/src/test/java/com/android/tools/r8/ProguardMapMarkerTest.java
@@ -25,6 +25,7 @@
 
 @RunWith(Parameterized.class)
 public class ProguardMapMarkerTest extends TestBase {
+
   private static final int EXPECTED_NUMBER_OF_KEYS_DEX = 6;
   private static final int EXPECTED_NUMBER_OF_KEYS_CF = 5;
   private static final String CLASS_FILE =
@@ -85,11 +86,10 @@
             .setMinApiLevel(minApiLevel.getLevel())
             .setProguardMapConsumer(
                 ToolHelper.consumeString(
-                    proguardMap -> {
-                      proguardMapIds.fromMap =
-                          verifyMarkersGetPgMapId(
-                              proguardMap, minApiLevel.getLevel(), EXPECTED_NUMBER_OF_KEYS_DEX);
-                    }))
+                    proguardMap ->
+                        proguardMapIds.fromMap =
+                            verifyMarkersGetPgMapId(
+                                proguardMap, minApiLevel.getLevel(), EXPECTED_NUMBER_OF_KEYS_DEX)))
             .build());
     verifyProguardMapIds(proguardMapIds);
   }
@@ -154,21 +154,28 @@
       }
       String key = comment.substring(0, colonIndex).trim();
       String value = comment.substring(colonIndex + 1).trim();
-      if (key.equals(ProguardMapSupplier.MARKER_KEY_COMPILER)) {
-        assertEquals("R8", value);
-      } else if (key.equals(ProguardMapSupplier.MARKER_KEY_COMPILER_VERSION)) {
-        assertEquals(Version.LABEL, value);
-      } else if (key.equals(ProguardMapSupplier.MARKER_KEY_MIN_API)) {
-        assertNotNull(minApiLevel);
-        assertEquals(minApiLevel.intValue(), Integer.parseInt(value));
-      } else if (key.equals(ProguardMapSupplier.MARKER_KEY_COMPILER_HASH)) {
-        assertEquals(VersionProperties.INSTANCE.getSha(), value);
-      } else if (key.equals(ProguardMapSupplier.MARKER_KEY_PG_MAP_ID)) {
-        proguardMapId = value;
-      } else if (key.equals(ProguardMapSupplier.MARKER_KEY_PG_MAP_HASH)) {
-        proguardMapHash = value;
-      } else {
-        continue;
+      switch (key) {
+        case "compiler":
+          assertEquals("R8", value);
+          break;
+        case "compiler_version":
+          assertEquals(Version.LABEL, value);
+          break;
+        case "min_api":
+          assertNotNull(minApiLevel);
+          assertEquals(minApiLevel.intValue(), Integer.parseInt(value));
+          break;
+        case "compiler_hash":
+          assertEquals(VersionProperties.INSTANCE.getSha(), value);
+          break;
+        case "pg_map_id":
+          proguardMapId = value;
+          break;
+        case "pg_map_hash":
+          proguardMapHash = value;
+          break;
+        default:
+          continue;
       }
       assertFalse(keysFound.contains(key));
       keysFound.add(key);
diff --git a/src/test/java/com/android/tools/r8/ProguardTestBuilder.java b/src/test/java/com/android/tools/r8/ProguardTestBuilder.java
index d91646a..ac6e335 100644
--- a/src/test/java/com/android/tools/r8/ProguardTestBuilder.java
+++ b/src/test/java/com/android/tools/r8/ProguardTestBuilder.java
@@ -114,14 +114,14 @@
       ProcessBuilder pbuilder = new ProcessBuilder(command);
       ProcessResult result = ToolHelper.runProcess(pbuilder, getStdoutForTesting());
       if (result.exitCode != 0) {
-        throw new CompilationFailedException(result.toString());
+        throw InternalCompilationFailedExceptionUtils.createForTesting(result.toString());
       }
       String proguardMap =
           Files.exists(mapFile) ? FileUtils.readTextFile(mapFile, Charsets.UTF_8) : "";
       return new ProguardTestCompileResult(
           result, getState(), outJar, getMinApiLevel(), proguardMap);
     } catch (IOException e) {
-      throw new CompilationFailedException(e);
+      throw InternalCompilationFailedExceptionUtils.createForTesting(e);
     }
   }
 
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index 45322ff..74d850c 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -2026,4 +2026,15 @@
     }
     return byteRead1 == byteRead2;
   }
+
+  protected void assertListsAreEqual(List<String> expected, List<String> actual) {
+    int minLines = Math.min(expected.size(), actual.size());
+    for (int i = 0; i < minLines; i++) {
+      assertEquals(expected.get(i), actual.get(i));
+    }
+    if (expected.size() != actual.size()) {
+      assertEquals(
+          "", expected.size() > actual.size() ? expected.get(minLines) : actual.get(minLines));
+    }
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/TestParameters.java b/src/test/java/com/android/tools/r8/TestParameters.java
index c22f43e..206a8ab 100644
--- a/src/test/java/com/android/tools/r8/TestParameters.java
+++ b/src/test/java/com/android/tools/r8/TestParameters.java
@@ -58,8 +58,7 @@
    * runtime error.
    */
   public boolean canHaveNonReboundConstructorInvoke() {
-    // TODO(b/246679983): Turned off while diagnosing b/246679983.
-    return false && isDexRuntime() && getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.L);
+    return isDexRuntime() && getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.L);
   }
 
   public boolean canUseDefaultAndStaticInterfaceMethods() {
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/ProtectedBridgeRemovalTest.java b/src/test/java/com/android/tools/r8/bridgeremoval/ProtectedBridgeRemovalTest.java
new file mode 100644
index 0000000..6747779
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/ProtectedBridgeRemovalTest.java
@@ -0,0 +1,110 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.bridgeremoval;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.BeforeClass;
+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 ProtectedBridgeRemovalTest extends TestBase {
+
+  private static final String A_DESCRIPTOR = "LA;";
+  private static final String EXPECTED_OUTPUT = StringUtils.lines("IllegalAccessError", "A.foo()");
+
+  private static List<byte[]> programClassFileData;
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  @BeforeClass
+  public static void setup() throws Exception {
+    programClassFileData =
+        ImmutableList.of(
+            transformer(Main.class)
+                .replaceClassDescriptorInMethodInstructions(descriptor(A.class), A_DESCRIPTOR)
+                .transform(),
+            transformer(A.class).setClassDescriptor(A_DESCRIPTOR).transform(),
+            transformer(B.class)
+                .replaceClassDescriptorInMethodInstructions(descriptor(A.class), A_DESCRIPTOR)
+                .setBridge(B.class.getDeclaredMethod("foo"))
+                .setSuper(A_DESCRIPTOR)
+                .transform());
+  }
+
+  @Test
+  public void testJvm() throws Exception {
+    parameters.assumeJvmTestParameters();
+    testForJvm(parameters)
+        .addProgramClassFileData(programClassFileData)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClassFileData(programClassFileData)
+        .addKeepMainRule(Main.class)
+        .enableInliningAnnotations()
+        .enableNoVerticalClassMergingAnnotations()
+        .setMinApi(parameters)
+        .compile()
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) {
+      for (A a : new A[] {new A(), new B()}) {
+        if (a instanceof B) {
+          B b = (B) a;
+          b.foo();
+        } else {
+          try {
+            a.foo();
+          } catch (IllegalAccessError e) {
+            System.out.println("IllegalAccessError");
+          }
+        }
+      }
+    }
+  }
+
+  @NoVerticalClassMerging
+  public static class /*other package.*/ A {
+
+    @NeverInline
+    protected void foo() {
+      System.out.println("A.foo()");
+    }
+  }
+
+  public static class B extends A {
+
+    @NeverInline
+    @Override
+    protected /*bridge*/ void foo() {
+      super.foo();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java b/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java
index 1bbaf31..71c9dc4 100644
--- a/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java
+++ b/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java
@@ -11,6 +11,7 @@
 import com.android.tools.r8.compilerapi.androidplatformbuild.AndroidPlatformBuildApiTest;
 import com.android.tools.r8.compilerapi.artprofiles.ArtProfilesForRewritingApiTest;
 import com.android.tools.r8.compilerapi.assertionconfiguration.AssertionConfigurationTest;
+import com.android.tools.r8.compilerapi.cancelcompilationchecker.CancelCompilationCheckerTest;
 import com.android.tools.r8.compilerapi.classconflictresolver.ClassConflictResolverTest;
 import com.android.tools.r8.compilerapi.desugardependencies.DesugarDependenciesTest;
 import com.android.tools.r8.compilerapi.diagnostics.ProguardKeepRuleDiagnosticsApiTest;
@@ -62,7 +63,7 @@
           ExtractMarkerApiTest.ApiTest.class);
 
   private static final List<Class<? extends CompilerApiTest>> CLASSES_PENDING_BINARY_COMPATIBILITY =
-      ImmutableList.of();
+      ImmutableList.of(CancelCompilationCheckerTest.ApiTest.class);
 
   private final TemporaryFolder temp;
 
diff --git a/src/test/java/com/android/tools/r8/compilerapi/cancelcompilationchecker/CancelCompilationCheckerTest.java b/src/test/java/com/android/tools/r8/compilerapi/cancelcompilationchecker/CancelCompilationCheckerTest.java
new file mode 100644
index 0000000..8afacb4
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/compilerapi/cancelcompilationchecker/CancelCompilationCheckerTest.java
@@ -0,0 +1,123 @@
+// Copyright (c) 2023, 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.compilerapi.cancelcompilationchecker;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.android.tools.r8.CancelCompilationChecker;
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.D8;
+import com.android.tools.r8.D8Command;
+import com.android.tools.r8.DexIndexedConsumer;
+import com.android.tools.r8.R8;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.compilerapi.CompilerApiTest;
+import com.android.tools.r8.compilerapi.CompilerApiTestRunner;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.IntBox;
+import java.util.function.BooleanSupplier;
+import org.junit.Test;
+
+public class CancelCompilationCheckerTest extends CompilerApiTestRunner {
+
+  public CancelCompilationCheckerTest(TestParameters parameters) {
+    super(parameters);
+  }
+
+  @Override
+  public Class<? extends CompilerApiTest> binaryTestClass() {
+    return ApiTest.class;
+  }
+
+  @Test
+  public void testD8() throws Exception {
+    // Use an integer box to delay the cancel until some time internal in the compiler.
+    IntBox i = new IntBox();
+    try {
+      new ApiTest(ApiTest.PARAMETERS).runD8(() -> i.incrementAndGet() > 10);
+      fail("excepted cancelled");
+    } catch (CompilationFailedException e) {
+      assertTrue(e.wasCancelled());
+    }
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    // Use an integer box to delay the cancel until some time internal in the compiler.
+    IntBox i = new IntBox();
+    try {
+      new ApiTest(ApiTest.PARAMETERS).runR8(() -> i.incrementAndGet() > 20);
+      fail("excepted cancelled");
+    } catch (CompilationFailedException e) {
+      assertTrue(e.wasCancelled());
+    }
+  }
+
+  public static class ApiTest extends CompilerApiTest {
+
+    public ApiTest(Object parameters) {
+      super(parameters);
+    }
+
+    public void runD8(BooleanSupplier supplier) throws Exception {
+      D8.run(
+          D8Command.builder()
+              .addClassProgramData(getBytesForClass(getMockClass()), Origin.unknown())
+              .addLibraryFiles(getJava8RuntimeJar())
+              .setProgramConsumer(DexIndexedConsumer.emptyConsumer())
+              .setCancelCompilationChecker(
+                  new CancelCompilationChecker() {
+                    @Override
+                    public boolean cancel() {
+                      return supplier.getAsBoolean();
+                    }
+                  })
+              .build());
+    }
+
+    public void runR8(BooleanSupplier supplier) throws Exception {
+      R8.run(
+          R8Command.builder()
+              .addClassProgramData(getBytesForClass(getMockClass()), Origin.unknown())
+              .addLibraryFiles(getJava8RuntimeJar())
+              .setDisableTreeShaking(true)
+              .setDisableMinification(true)
+              .setProgramConsumer(DexIndexedConsumer.emptyConsumer())
+              .setCancelCompilationChecker(
+                  new CancelCompilationChecker() {
+                    @Override
+                    public boolean cancel() {
+                      return supplier.getAsBoolean();
+                    }
+                  })
+              .build());
+    }
+
+    @Test
+    public void testD8() throws Exception {
+      try {
+        runD8(() -> true);
+      } catch (CompilationFailedException e) {
+        if (e.wasCancelled()) {
+          return;
+        }
+      }
+      throw new AssertionError("expected cancelled");
+    }
+
+    @Test
+    public void testR8() throws Exception {
+      try {
+        runR8(() -> true);
+      } catch (CompilationFailedException e) {
+        if (e.wasCancelled()) {
+          return;
+        }
+      }
+      throw new AssertionError("expected cancelled");
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/CanonicalizeWithInline.java b/src/test/java/com/android/tools/r8/debuginfo/CanonicalizeWithInline.java
index 455bfe4..0373c01 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/CanonicalizeWithInline.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/CanonicalizeWithInline.java
@@ -15,6 +15,7 @@
 import com.android.tools.r8.utils.AndroidApiLevel;
 import java.io.IOException;
 import java.nio.file.Path;
+import java.util.List;
 import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -34,7 +35,7 @@
   }
 
   private int getNumberOfDebugInfos(Path file) throws IOException {
-    DexSection[] dexSections = DexParser.parseMapFrom(file);
+    List<DexSection> dexSections = DexParser.parseMapFrom(file);
     for (DexSection dexSection : dexSections) {
       if (dexSection.type == Constants.TYPE_DEBUG_INFO_ITEM) {
         return dexSection.length;
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java
index 2b5de3f..0ad53ed 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java
@@ -516,7 +516,8 @@
         GenerateLintFiles.createForTesting(
             libraryDesugaringSpecification.getSpecification(),
             libraryDesugaringSpecification.getDesugarJdkLibs(),
-            out);
+            out,
+            ToolHelper.getAndroidJar(AndroidApiLevel.U));
     AndroidApiLevel compileApi = desugaredApi.run();
     return new CodeInspector(
         out.resolve("compile_api_level_" + compileApi.getLevel())
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LintFilesTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LintFilesTest.java
index 446b84e..aeedca9 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LintFilesTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LintFilesTest.java
@@ -41,6 +41,8 @@
 @RunWith(Parameterized.class)
 public class LintFilesTest extends DesugaredLibraryTestBase {
 
+  private static final String ANDROID_JAR_34 = "third_party/android_jar/lib-v34/android.jar";
+
   private final LibraryDesugaringSpecification libraryDesugaringSpecification;
 
   private List<String> lintContents;
@@ -237,12 +239,19 @@
           spec == JDK8
               ? ToolHelper.DESUGARED_JDK_8_LIB_JAR
               : LibraryDesugaringSpecification.getTempLibraryJDK11Undesugar();
-      new GenerateHtmlDoc(spec.getSpecification().toString(), jdkLibJar.toString(), html.toString())
+      new GenerateHtmlDoc(
+              spec.getSpecification().toString(),
+              jdkLibJar.toString(),
+              html.toString(),
+              ANDROID_JAR_34)
           .run(spec + ".html");
       Path lint = top.resolve("lint_" + spec);
       Files.createDirectories(lint);
       new GenerateLintFiles(
-              spec.getSpecification().toString(), jdkLibJar.toString(), lint.toString())
+              spec.getSpecification().toString(),
+              jdkLibJar.toString(),
+              lint.toString(),
+              ANDROID_JAR_34)
           .run();
     }
   }
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/PartialDesugaringTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/PartialDesugaringTest.java
index dd7c1cf..eebc725 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/PartialDesugaringTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/PartialDesugaringTest.java
@@ -12,6 +12,7 @@
 import static org.junit.Assert.assertEquals;
 
 import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexMember;
@@ -129,7 +130,8 @@
   @Test
   public void test() throws Exception {
     SupportedClasses supportedClasses =
-        new SupportedClassesGenerator(new InternalOptions())
+        new SupportedClassesGenerator(
+                new InternalOptions(), ToolHelper.getAndroidJar(AndroidApiLevel.U))
             .run(librarySpecification.getDesugarJdkLibs(), librarySpecification.getSpecification());
 
     for (AndroidApiLevel api : getRelevantApiLevels()) {
@@ -205,10 +207,6 @@
         && api.isLessThan(AndroidApiLevel.T)) {
       expectedFailures.addAll(FAILURES_TO_ARRAY);
     }
-    if (librarySpecification == JDK8 && api.isLessThan(AndroidApiLevel.T)) {
-      // Interestingly that was added somehow to JDK8 desugared library at some point...
-      expectedFailures.addAll(FAILURES_TO_ARRAY);
-    }
     if (jdk11NonMinimal
         && api.isGreaterThanOrEqualTo(AndroidApiLevel.O)
         && api.isLessThan(AndroidApiLevel.U)) {
@@ -218,10 +216,6 @@
     if (librarySpecification == JDK8 && api.isGreaterThanOrEqualTo(AndroidApiLevel.O)) {
       expectedFailures.addAll(FAILURES_ERA);
     }
-    if (jdk11NonMinimal && api.isGreaterThanOrEqualTo(AndroidApiLevel.T)) {
-      // The method is present, but not in android.jar...
-      expectedFailures.addAll(FAILURES_ERA);
-    }
     return expectedFailures;
   }
 }
diff --git a/src/test/java/com/android/tools/r8/dex/container/DexContainerFormatBasicTest.java b/src/test/java/com/android/tools/r8/dex/container/DexContainerFormatBasicTest.java
index 28a01e0..25b028a 100644
--- a/src/test/java/com/android/tools/r8/dex/container/DexContainerFormatBasicTest.java
+++ b/src/test/java/com/android/tools/r8/dex/container/DexContainerFormatBasicTest.java
@@ -33,6 +33,7 @@
 import com.android.tools.r8.utils.BitUtils;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.DexVersion;
+import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.ZipUtils;
 import com.google.common.collect.ImmutableList;
 import com.google.common.io.ByteStreams;
@@ -106,7 +107,7 @@
   }
 
   @Test
-  public void testD8Experiment() throws Exception {
+  public void testD8ExperimentSimpleMerge() throws Exception {
     Path outputFromDexing =
         testForD8(Backend.DEX)
             .addProgramFiles(inputA)
@@ -116,10 +117,23 @@
             .compile()
             .writeToZip();
     validateSingleContainerDex(outputFromDexing);
+
+    Path outputFromMerging =
+        testForD8(Backend.DEX)
+            .addProgramFiles(outputFromDexing)
+            .addOptionsModification(
+                options -> options.getTestingOptions().dexContainerExperiment = true)
+            .compile()
+            .writeToZip();
+    validateSingleContainerDex(outputFromMerging);
+
+    // Identical DEX after re-merging.
+    assertArrayEquals(
+        unzipContent(outputFromDexing).get(0), unzipContent(outputFromMerging).get(0));
   }
 
   @Test
-  public void testD8Experiment2() throws Exception {
+  public void testD8ExperimentMoreMerge() throws Exception {
     Path outputA =
         testForD8(Backend.DEX)
             .addProgramFiles(inputA)
@@ -139,6 +153,27 @@
             .compile()
             .writeToZip();
     validateSingleContainerDex(outputB);
+
+    Path outputBoth =
+        testForD8(Backend.DEX)
+            .addProgramFiles(inputA, inputB)
+            .setMinApi(AndroidApiLevel.L)
+            .addOptionsModification(
+                options -> options.getTestingOptions().dexContainerExperiment = true)
+            .compile()
+            .writeToZip();
+    validateSingleContainerDex(outputBoth);
+
+    Path outputMerged =
+        testForD8(Backend.DEX)
+            .addProgramFiles(outputA, outputB)
+            .addOptionsModification(
+                options -> options.getTestingOptions().dexContainerExperiment = true)
+            .compile()
+            .writeToZip();
+    validateSingleContainerDex(outputMerged);
+
+    assertArrayEquals(unzipContent(outputBoth).get(0), unzipContent(outputMerged).get(0));
   }
 
   private void validateDex(Path output, int expectedDexes, DexVersion expectedVersion)
@@ -168,10 +203,14 @@
       int dataSize = buffer.getInt(offset + DATA_SIZE_OFFSET);
       int dataOffset = buffer.getInt(offset + DATA_OFF_OFFSET);
       int file_size = buffer.getInt(offset + FILE_SIZE_OFFSET);
-      if (!expectedVersion.isContainerDex()) {
+      if (expectedVersion.isContainerDex()) {
+        assertEquals(0, dataSize);
+        assertEquals(0, dataOffset);
+      } else {
         assertEquals(file_size, dataOffset + dataSize);
       }
       offset += expectedVersion.isContainerDex() ? file_size : dataOffset + dataSize;
+      assertEquals(file_size, offset - ListUtils.last(sections));
     }
     assertEquals(buffer.capacity(), offset);
 
diff --git a/src/test/java/com/android/tools/r8/naming/MappingFileHashTest.java b/src/test/java/com/android/tools/r8/naming/MappingFileHashTest.java
index efc6cf7..1468bd8 100644
--- a/src/test/java/com/android/tools/r8/naming/MappingFileHashTest.java
+++ b/src/test/java/com/android/tools/r8/naming/MappingFileHashTest.java
@@ -8,8 +8,7 @@
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.naming.ProguardMapSupplier.ProguardMapChecker;
-import com.android.tools.r8.naming.ProguardMapSupplier.ProguardMapChecker.VerifyMappingFileHashResult;
+import com.android.tools.r8.naming.ProguardMapChecker.VerifyMappingFileHashResult;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java b/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java
index 0446cd5..82a318d 100644
--- a/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java
+++ b/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java
@@ -11,6 +11,7 @@
 import com.android.tools.r8.SingleTestRunResult;
 import com.android.tools.r8.ToolHelper.DexVm;
 import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.retrace.MappingSupplier;
 import com.android.tools.r8.retrace.ProguardMapProducer;
 import com.android.tools.r8.retrace.ProguardMappingSupplier;
 import com.android.tools.r8.retrace.Retrace;
@@ -385,17 +386,21 @@
   }
 
   public StackTrace retrace(String map, boolean allowExperimentalMapping) {
+    return retrace(
+        ProguardMappingSupplier.builder()
+            .setProguardMapProducer(ProguardMapProducer.fromString(map))
+            .setAllowExperimental(allowExperimentalMapping)
+            .build());
+  }
+
+  public StackTrace retrace(MappingSupplier<?> mappingSupplier) {
     Box<List<String>> box = new Box<>();
     List<String> stackTrace =
         stackTraceLines.stream().map(line -> line.originalLine).collect(Collectors.toList());
     stackTrace.add(0, exceptionLine);
     Retrace.run(
         RetraceCommand.builder()
-            .setMappingSupplier(
-                ProguardMappingSupplier.builder()
-                    .setProguardMapProducer(ProguardMapProducer.fromString(map))
-                    .setAllowExperimental(allowExperimentalMapping)
-                    .build())
+            .setMappingSupplier(mappingSupplier)
             .setStackTrace(stackTrace)
             .setRetracedStackTraceConsumer(box::set)
             .build());
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/LambdaStaticLibraryMethodImplementationProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/LambdaStaticLibraryMethodImplementationProfileRewritingTest.java
index 2d38860..c83d81d 100644
--- a/src/test/java/com/android/tools/r8/profile/art/completeness/LambdaStaticLibraryMethodImplementationProfileRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/LambdaStaticLibraryMethodImplementationProfileRewritingTest.java
@@ -75,15 +75,22 @@
   }
 
   private void inspectD8(ArtProfileInspector profileInspector, CodeInspector inspector) {
-    inspect(profileInspector, inspector, false);
+    inspect(profileInspector, inspector, false, false);
   }
 
   private void inspectR8(ArtProfileInspector profileInspector, CodeInspector inspector) {
-    inspect(profileInspector, inspector, parameters.isCfRuntime());
+    inspect(
+        profileInspector,
+        inspector,
+        parameters.canHaveNonReboundConstructorInvoke(),
+        parameters.isCfRuntime());
   }
 
   public void inspect(
-      ArtProfileInspector profileInspector, CodeInspector inspector, boolean canUseLambdas) {
+      ArtProfileInspector profileInspector,
+      CodeInspector inspector,
+      boolean canHaveNonReboundConstructorInvoke,
+      boolean canUseLambdas) {
     ClassSubject mainClassSubject = inspector.clazz(Main.class);
     assertThat(mainClassSubject, isPresent());
 
@@ -96,21 +103,26 @@
     assertThat(lambdaClassSubject, notIf(isPresent(), canUseLambdas));
 
     MethodSubject lambdaInitializerSubject = lambdaClassSubject.uniqueInstanceInitializer();
-    assertThat(lambdaInitializerSubject, notIf(isPresent(), canUseLambdas));
+    assertThat(
+        lambdaInitializerSubject,
+        notIf(isPresent(), canHaveNonReboundConstructorInvoke || canUseLambdas));
 
     MethodSubject lambdaMainMethodSubject =
         lambdaClassSubject.uniqueMethodThatMatches(FoundMethodSubject::isVirtual);
     assertThat(lambdaMainMethodSubject, notIf(isPresent(), canUseLambdas));
 
     if (canUseLambdas) {
-      profileInspector.assertContainsMethodRule(mainMethodSubject).assertContainsNoOtherRules();
+      profileInspector.assertContainsMethodRule(mainMethodSubject);
     } else {
       profileInspector
           .assertContainsClassRules(lambdaClassSubject)
-          .assertContainsMethodRules(
-              mainMethodSubject, lambdaInitializerSubject, lambdaMainMethodSubject)
-          .assertContainsNoOtherRules();
+          .assertContainsMethodRules(mainMethodSubject, lambdaMainMethodSubject)
+          .applyIf(
+              !canHaveNonReboundConstructorInvoke,
+              i -> i.assertContainsMethodRule(lambdaInitializerSubject));
     }
+
+    profileInspector.assertContainsNoOtherRules();
   }
 
   static class Main {
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/NestBasedAccessBridgesProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/NestBasedAccessBridgesProfileRewritingTest.java
index 7d9e9ab..577518d 100644
--- a/src/test/java/com/android/tools/r8/profile/art/completeness/NestBasedAccessBridgesProfileRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/NestBasedAccessBridgesProfileRewritingTest.java
@@ -104,17 +104,22 @@
 
   private void inspectD8(ArtProfileInspector profileInspector, CodeInspector inspector)
       throws Exception {
-    inspect(profileInspector, inspector, parameters.canUseNestBasedAccessesWhenDesugaring());
+    inspect(profileInspector, inspector, false, parameters.canUseNestBasedAccessesWhenDesugaring());
   }
 
   private void inspectR8(ArtProfileInspector profileInspector, CodeInspector inspector)
       throws Exception {
-    inspect(profileInspector, inspector, parameters.canUseNestBasedAccesses());
+    inspect(
+        profileInspector,
+        inspector,
+        parameters.canHaveNonReboundConstructorInvoke(),
+        parameters.canUseNestBasedAccesses());
   }
 
   private void inspect(
       ArtProfileInspector profileInspector,
       CodeInspector inspector,
+      boolean canHaveNonReboundConstructorInvoke,
       boolean canUseNestBasedAccesses)
       throws Exception {
     ClassSubject nestMemberClassSubject = inspector.clazz(NestMember.class);
@@ -128,7 +133,7 @@
         syntheticConstructorArgumentClassSubject, notIf(isPresent(), canUseNestBasedAccesses));
 
     MethodSubject instanceInitializer = nestMemberClassSubject.init();
-    assertThat(instanceInitializer, isPresent());
+    assertThat(instanceInitializer, notIf(isPresent(), canHaveNonReboundConstructorInvoke));
 
     MethodSubject instanceInitializerWithSyntheticArgumentSubject =
         syntheticConstructorArgumentClassSubject.isPresent()
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java
index 5f1886d..6d03704 100644
--- a/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java
@@ -149,6 +149,7 @@
         profileInspector,
         inspector,
         SyntheticItemsTestUtils.syntheticRecordTagClass(),
+        false,
         parameters.canUseNestBasedAccessesWhenDesugaring(),
         parameters.canUseRecordsWhenDesugaring());
   }
@@ -158,6 +159,7 @@
         profileInspector,
         inspector,
         RECORD_REFERENCE,
+        parameters.canHaveNonReboundConstructorInvoke(),
         parameters.canUseNestBasedAccesses(),
         parameters.canUseRecords());
   }
@@ -166,6 +168,7 @@
       ArtProfileInspector profileInspector,
       CodeInspector inspector,
       ClassReference recordClassReference,
+      boolean canHaveNonReboundConstructorInvoke,
       boolean canUseNestBasedAccesses,
       boolean canUseRecords) {
     ClassSubject mainClassSubject = inspector.clazz(MAIN_REFERENCE);
@@ -177,11 +180,14 @@
     ClassSubject recordTagClassSubject = inspector.clazz(recordClassReference);
     assertThat(recordTagClassSubject, notIf(isPresent(), canUseRecords));
     if (!canUseRecords) {
-      assertEquals(1, recordTagClassSubject.allMethods().size());
+      assertEquals(
+          canHaveNonReboundConstructorInvoke ? 0 : 1, recordTagClassSubject.allMethods().size());
     }
 
     MethodSubject recordTagInstanceInitializerSubject = recordTagClassSubject.init();
-    assertThat(recordTagInstanceInitializerSubject, notIf(isPresent(), canUseRecords));
+    assertThat(
+        recordTagInstanceInitializerSubject,
+        notIf(isPresent(), canHaveNonReboundConstructorInvoke || canUseRecords));
 
     ClassSubject personRecordClassSubject = inspector.clazz(PERSON_REFERENCE);
     assertThat(personRecordClassSubject, isPresent());
@@ -290,11 +296,13 @@
                         hashCodeHelperClassSubject,
                         toStringHelperClassSubject)
                     .assertContainsMethodRules(
-                        recordTagInstanceInitializerSubject,
                         equalsHelperMethodSubject,
                         getFieldsAsObjectsMethodSubject,
                         hashCodeHelperMethodSubject,
-                        toStringHelperMethodSubject))
+                        toStringHelperMethodSubject)
+                    .applyIf(
+                        !canHaveNonReboundConstructorInvoke,
+                        j -> j.assertContainsMethodRule(recordTagInstanceInitializerSubject)))
         .assertContainsNoOtherRules();
   }
 }
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/SyntheticLambdaClassProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/SyntheticLambdaClassProfileRewritingTest.java
index 385f94b..ef6b3a3 100644
--- a/src/test/java/com/android/tools/r8/profile/art/completeness/SyntheticLambdaClassProfileRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/SyntheticLambdaClassProfileRewritingTest.java
@@ -68,6 +68,7 @@
     public void inspect(
         ArtProfileInspector profileInspector,
         CodeInspector inspector,
+        boolean canHaveNonReboundConstructorInvoke,
         boolean canUseLambdas,
         boolean canAccessModifyLambdaImplementationMethods) {
       ClassSubject mainClassSubject = inspector.clazz(Main.class);
@@ -106,7 +107,9 @@
       assertThat(lambdaClassSubject, notIf(isPresent(), canUseLambdas));
 
       MethodSubject lambdaInitializerSubject = lambdaClassSubject.uniqueInstanceInitializer();
-      assertThat(lambdaInitializerSubject, notIf(isPresent(), canUseLambdas));
+      assertThat(
+          lambdaInitializerSubject,
+          notIf(isPresent(), canHaveNonReboundConstructorInvoke || canUseLambdas));
 
       MethodSubject lambdaMainMethodSubject =
           lambdaClassSubject.uniqueMethodThatMatches(FoundMethodSubject::isVirtual);
@@ -119,7 +122,9 @@
 
       MethodSubject otherLambdaInitializerSubject =
           otherLambdaClassSubject.uniqueInstanceInitializer();
-      assertThat(otherLambdaInitializerSubject, notIf(isPresent(), canUseLambdas));
+      assertThat(
+          otherLambdaInitializerSubject,
+          notIf(isPresent(), canHaveNonReboundConstructorInvoke || canUseLambdas));
 
       MethodSubject otherLambdaMainMethodSubject =
           otherLambdaClassSubject.uniqueMethodThatMatches(FoundMethodSubject::isVirtual);
@@ -149,8 +154,12 @@
             // interface method implementation does not need to be included in the profile.
             profileInspector
                 .assertContainsClassRules(lambdaClassSubject, otherLambdaClassSubject)
-                .assertContainsMethodRules(
-                    mainMethodSubject, lambdaInitializerSubject, otherLambdaInitializerSubject)
+                .assertContainsMethodRules(mainMethodSubject)
+                .applyIf(
+                    !canHaveNonReboundConstructorInvoke,
+                    i ->
+                        i.assertContainsMethodRules(
+                            lambdaInitializerSubject, otherLambdaInitializerSubject))
                 .assertContainsNoOtherRules();
             break;
           case IMPLEMENTATION_METHOD:
@@ -221,11 +230,16 @@
   }
 
   private void inspectD8(ArtProfileInspector profileInspector, CodeInspector inspector) {
-    artProfileInputOutput.inspect(profileInspector, inspector, false, true);
+    artProfileInputOutput.inspect(profileInspector, inspector, false, false, true);
   }
 
   private void inspectR8(ArtProfileInspector profileInspector, CodeInspector inspector) {
-    artProfileInputOutput.inspect(profileInspector, inspector, parameters.isCfRuntime(), false);
+    artProfileInputOutput.inspect(
+        profileInspector,
+        inspector,
+        parameters.canHaveNonReboundConstructorInvoke(),
+        parameters.isCfRuntime(),
+        false);
   }
 
   static class Main {
diff --git a/src/test/java/com/android/tools/r8/retrace/partition/R8ZipContainerMappingFileTest.java b/src/test/java/com/android/tools/r8/retrace/partition/R8ZipContainerMappingFileTest.java
new file mode 100644
index 0000000..211121c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/partition/R8ZipContainerMappingFileTest.java
@@ -0,0 +1,154 @@
+// Copyright (c) 2023, 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.retrace.partition;
+
+import static com.android.tools.r8.naming.retrace.StackTrace.isSame;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.ProguardMapConsumer;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestDiagnosticMessagesImpl;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.naming.ProguardMapPartitionConsumer;
+import com.android.tools.r8.naming.retrace.StackTrace;
+import com.android.tools.r8.naming.retrace.StackTrace.StackTraceLine;
+import com.android.tools.r8.retrace.PartitionMappingSupplier;
+import com.android.tools.r8.retrace.partition.testclasses.R8ZipContainerMappingFileTestClasses;
+import com.android.tools.r8.retrace.partition.testclasses.R8ZipContainerMappingFileTestClasses.Main;
+import com.android.tools.r8.utils.ZipUtils.ZipBuilder;
+import com.google.common.io.ByteStreams;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+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 R8ZipContainerMappingFileTest extends TestBase {
+
+  private static final String SOURCE_FILE = "R8ZipContainerMappingFileTestClasses.java";
+
+  private final StackTrace EXPECTED =
+      StackTrace.builder()
+          .add(
+              StackTraceLine.builder()
+                  .setClassName(typeName(R8ZipContainerMappingFileTestClasses.Thrower.class))
+                  .setMethodName("throwError")
+                  .setFileName(SOURCE_FILE)
+                  .setLineNumber(13)
+                  .build())
+          .add(
+              StackTraceLine.builder()
+                  .setClassName(typeName(R8ZipContainerMappingFileTestClasses.Main.class))
+                  .setMethodName("main")
+                  .setFileName(SOURCE_FILE)
+                  .setLineNumber(21)
+                  .build())
+          .build();
+
+  @Parameter() public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  @Test
+  public void testRuntime() throws Exception {
+    testForRuntime(parameters)
+        .addInnerClasses(R8ZipContainerMappingFileTestClasses.class)
+        .run(parameters.getRuntime(), Main.class)
+        .assertFailureWithErrorThatThrows(RuntimeException.class)
+        .inspectStackTrace(
+            stackTrace -> {
+              assertThat(stackTrace, isSame(EXPECTED));
+            });
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    Path pgMapFile = temp.newFile("mapping.zip").toPath();
+    DiagnosticsHandler diagnosticsHandler = new TestDiagnosticMessagesImpl();
+    ProguardMapConsumer partitionZipConsumer =
+        createPartitionZipConsumer(pgMapFile, diagnosticsHandler);
+    StackTrace originalStackTrace =
+        testForR8(parameters.getBackend())
+            .addInnerClasses(R8ZipContainerMappingFileTestClasses.class)
+            .setMinApi(parameters)
+            .addKeepMainRule(Main.class)
+            .addKeepAttributeSourceFile()
+            .addKeepAttributeLineNumberTable()
+            .addOptionsModification(options -> options.proguardMapConsumer = partitionZipConsumer)
+            .run(parameters.getRuntime(), Main.class)
+            .assertFailureWithErrorThatThrows(RuntimeException.class)
+            .getOriginalStackTrace();
+
+    assertTrue(Files.exists(pgMapFile));
+    assertThat(
+        originalStackTrace.retrace(createMappingSupplierFromPartitionZip(pgMapFile)),
+        isSame(EXPECTED));
+  }
+
+  private ProguardMapConsumer createPartitionZipConsumer(
+      Path pgMapFile, DiagnosticsHandler diagnosticsHandler) throws IOException {
+    ZipBuilder zipBuilder = ZipBuilder.builder(pgMapFile);
+    return ProguardMapPartitionConsumer.builder()
+        .setMappingPartitionConsumer(
+            mappingPartition -> {
+              try {
+                zipBuilder.addBytes(mappingPartition.getKey(), mappingPartition.getPayload());
+              } catch (IOException e) {
+                throw new RuntimeException(e);
+              }
+            })
+        .setMetadataConsumer(
+            metadata -> {
+              try {
+                zipBuilder.addBytes("METADATA", metadata.getBytes());
+              } catch (IOException e) {
+                throw new RuntimeException(e);
+              }
+            })
+        .setFinishedConsumer(
+            () -> {
+              try {
+                zipBuilder.build();
+              } catch (IOException e) {
+                throw new RuntimeException(e);
+              }
+            })
+        .setDiagnosticsHandler(diagnosticsHandler)
+        .build();
+  }
+
+  private PartitionMappingSupplier createMappingSupplierFromPartitionZip(Path pgMapFile)
+      throws IOException {
+    ZipFile zipFile = new ZipFile(pgMapFile.toFile());
+    byte[] metadata = ByteStreams.toByteArray(zipFile.getInputStream(zipFile.getEntry("METADATA")));
+    return PartitionMappingSupplier.builder()
+        .setMetadata(metadata)
+        .setMappingPartitionFromKeySupplier(
+            key -> {
+              try {
+                // TODO(b/274735214): The key should exist.
+                ZipEntry entry = zipFile.getEntry(key);
+                return entry == null
+                    ? null
+                    : ByteStreams.toByteArray(zipFile.getInputStream(entry));
+              } catch (IOException e) {
+                throw new RuntimeException(e);
+              }
+            })
+        .build();
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/partition/RetracePartitionAndJoinIdentityTest.java b/src/test/java/com/android/tools/r8/retrace/partition/RetracePartitionAndJoinIdentityTest.java
new file mode 100644
index 0000000..83287ff
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/partition/RetracePartitionAndJoinIdentityTest.java
@@ -0,0 +1,91 @@
+// Copyright (c) 2023, 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.retrace.partition;
+
+import static org.junit.Assert.assertNotNull;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestDiagnosticMessagesImpl;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.retrace.MappingPartitionMetadata;
+import com.android.tools.r8.retrace.PartitionedToProguardMappingConverter;
+import com.android.tools.r8.retrace.ProguardMapProducer;
+import com.android.tools.r8.retrace.internal.MappingPartitionKeyStrategy;
+import com.android.tools.r8.retrace.internal.ProguardMapPartitionerOnClassNameToText.ProguardMapPartitionerBuilderImplInternal;
+import com.android.tools.r8.utils.StringUtils;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.HashMap;
+import java.util.List;
+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 RetracePartitionAndJoinIdentityTest extends TestBase {
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  public RetracePartitionAndJoinIdentityTest(TestParameters parameters) {
+    parameters.assertNoneRuntime();
+  }
+
+  @Test
+  public void testPartitionAndJoin() throws Exception {
+    Path mappingFile =
+        ToolHelper.RETRACE_MAPS_DIR.resolve(
+            "ad5c3e88ef2bae5ef324eb225fbc57345cd57863-r8lib.jar.map");
+    ProguardMapProducer proguardMapProducer = ProguardMapProducer.fromPath(mappingFile);
+    TestDiagnosticMessagesImpl diagnosticsHandler = new TestDiagnosticMessagesImpl();
+    Map<String, byte[]> partitions = new HashMap<>();
+    MappingPartitionMetadata metadataData =
+        new ProguardMapPartitionerBuilderImplInternal(diagnosticsHandler)
+            .setMappingPartitionKeyStrategy(
+                MappingPartitionKeyStrategy.OBFUSCATED_TYPE_NAME_AS_KEY_WITH_PARTITIONS)
+            .setProguardMapProducer(proguardMapProducer)
+            .setPartitionConsumer(
+                partition -> partitions.put(partition.getKey(), partition.getPayload()))
+            .build()
+            .run();
+    assertNotNull(metadataData);
+    diagnosticsHandler.assertNoMessages();
+
+    StringBuilder builder = new StringBuilder();
+    PartitionedToProguardMappingConverter.builder()
+        .setMetadata(metadataData.getBytes())
+        .setDiagnosticsHandler(diagnosticsHandler)
+        .setConsumer((string, handler) -> builder.append(string))
+        .setPartitionSupplier(partitions::get)
+        .build()
+        .run();
+    List<String> joinedMapLines = StringUtils.splitLines(builder.toString());
+    // TODO(b/274735214): Partitioning does not capture the preamble of the mapping file yet, so we
+    //  discard it before checking equality.
+    List<String> lines = Files.readAllLines(mappingFile);
+    List<String> filteredLines = lines.subList(computeFirstLine(lines), lines.size());
+    assertListsAreEqual(filteredLines, joinedMapLines);
+  }
+
+  private int computeFirstLine(List<String> lines) {
+    int firstLine = 0;
+    for (int i = 0; i < lines.size(); i++) {
+      String currentLine = lines.get(i).trim();
+      if (!currentLine.startsWith("#")
+          && currentLine.contains(" -> ")
+          && currentLine.endsWith(":")) {
+        firstLine = i;
+        break;
+      }
+    }
+    return firstLine;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/partition/RetracePartitionMetadataPartitionNamesTest.java b/src/test/java/com/android/tools/r8/retrace/partition/RetracePartitionMetadataPartitionNamesTest.java
new file mode 100644
index 0000000..376d072
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/partition/RetracePartitionMetadataPartitionNamesTest.java
@@ -0,0 +1,89 @@
+// Copyright (c) 2023, 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.retrace.partition;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.naming.MapVersion;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.retrace.MappingPartitionMetadata;
+import com.android.tools.r8.retrace.ProguardMapProducer;
+import com.android.tools.r8.retrace.internal.MappingPartitionKeyStrategy;
+import com.android.tools.r8.retrace.internal.MappingPartitionMetadataInternal;
+import com.android.tools.r8.retrace.internal.ProguardMapPartitionerOnClassNameToText.ProguardMapPartitionerBuilderImplInternal;
+import com.android.tools.r8.utils.StringUtils;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+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 RetracePartitionMetadataPartitionNamesTest extends TestBase {
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  public RetracePartitionMetadataPartitionNamesTest(TestParameters parameters) {
+    parameters.assertNoneRuntime();
+  }
+
+  public ClassReference obfuscatedClass1 = Reference.classFromTypeName("a.a");
+  public ClassReference obfuscatedClass2 = Reference.classFromTypeName("b.b");
+  public ClassReference obfuscatedClass3 = Reference.classFromTypeName("c.c");
+
+  public String mapping =
+      StringUtils.unixLines(
+          "# { id: 'com.android.tools.r8.mapping', version: '2.0' }",
+          "some.class1 -> " + obfuscatedClass1.getTypeName() + ":",
+          "  void field -> a",
+          "some.class2 -> " + obfuscatedClass2.getTypeName() + ":",
+          "  void field -> a",
+          "some.class3 -> " + obfuscatedClass3.getTypeName() + ":",
+          "  void field -> a");
+
+  @Test
+  public void test() throws Exception {
+    List<String> expectedPartitionKeys = new ArrayList<>();
+    expectedPartitionKeys.add(obfuscatedClass1.getTypeName());
+    expectedPartitionKeys.add(obfuscatedClass2.getTypeName());
+    expectedPartitionKeys.add(obfuscatedClass3.getTypeName());
+
+    ProguardMapProducer proguardMapProducer = ProguardMapProducer.fromString(mapping);
+    DiagnosticsHandler diagnosticsHandler = new DiagnosticsHandler() {};
+    Map<String, byte[]> partitions = new HashMap<>();
+    MappingPartitionMetadata metadataData =
+        new ProguardMapPartitionerBuilderImplInternal(diagnosticsHandler)
+            .setMappingPartitionKeyStrategy(
+                MappingPartitionKeyStrategy.OBFUSCATED_TYPE_NAME_AS_KEY_WITH_PARTITIONS)
+            .setProguardMapProducer(proguardMapProducer)
+            .setPartitionConsumer(
+                partition -> partitions.put(partition.getKey(), partition.getPayload()))
+            .build()
+            .run();
+    assertNotNull(metadataData);
+    assertEquals(new HashSet<>(expectedPartitionKeys), partitions.keySet());
+
+    byte[] bytes = metadataData.getBytes();
+    MappingPartitionMetadataInternal mappingPartitionMetadata =
+        MappingPartitionMetadataInternal.deserialize(
+            bytes, MapVersion.MAP_VERSION_NONE, diagnosticsHandler);
+    assertTrue(mappingPartitionMetadata.canGetPartitionKeys());
+    assertEquals(expectedPartitionKeys, mappingPartitionMetadata.getPartitionKeys());
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/partition/RetracePartitionMetadataUnknownTest.java b/src/test/java/com/android/tools/r8/retrace/partition/RetracePartitionMetadataUnknownTest.java
new file mode 100644
index 0000000..01159fa
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/partition/RetracePartitionMetadataUnknownTest.java
@@ -0,0 +1,53 @@
+// Copyright (c) 2023, 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.retrace.partition;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.naming.MapVersion;
+import com.android.tools.r8.retrace.internal.MappingPartitionMetadataInternal;
+import com.android.tools.r8.retrace.internal.RetracePartitionException;
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+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 RetracePartitionMetadataUnknownTest extends TestBase {
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  public RetracePartitionMetadataUnknownTest(TestParameters parameters) {
+    parameters.assertNoneRuntime();
+  }
+
+  @Test
+  public void test() throws Exception {
+    ByteArrayOutputStream temp = new ByteArrayOutputStream();
+    DataOutputStream dataOutputStream = new DataOutputStream(temp);
+    dataOutputStream.writeShort(Short.MAX_VALUE);
+    dataOutputStream.close();
+    byte[] bytes = temp.toByteArray();
+    DiagnosticsHandler diagnosticsHandler = new DiagnosticsHandler() {};
+    RetracePartitionException retracePartitionException =
+        assertThrows(
+            RetracePartitionException.class,
+            () ->
+                MappingPartitionMetadataInternal.deserialize(
+                    bytes, MapVersion.MAP_VERSION_NONE, diagnosticsHandler));
+    assertEquals(
+        "Unknown map partition strategy for metadata", retracePartitionException.getMessage());
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/partition/testclasses/R8ZipContainerMappingFileTestClasses.java b/src/test/java/com/android/tools/r8/retrace/partition/testclasses/R8ZipContainerMappingFileTestClasses.java
new file mode 100644
index 0000000..e81d031
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/partition/testclasses/R8ZipContainerMappingFileTestClasses.java
@@ -0,0 +1,24 @@
+// Copyright (c) 2023, 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.retrace.partition.testclasses;
+
+public class R8ZipContainerMappingFileTestClasses {
+
+  public static class Thrower {
+
+    public static void throwError() {
+      if (System.currentTimeMillis() > 0) {
+        throw new RuntimeException("Hello World");
+      }
+    }
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) {
+      Thrower.throwError();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/assumevalues/UnusedParameterAfterAssumeValuesFieldRuleTest.java b/src/test/java/com/android/tools/r8/shaking/assumevalues/UnusedParameterAfterAssumeValuesFieldRuleTest.java
new file mode 100644
index 0000000..f4f4aec
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/assumevalues/UnusedParameterAfterAssumeValuesFieldRuleTest.java
@@ -0,0 +1,82 @@
+// Copyright (c) 2023, 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.assumevalues;
+
+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.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.FoundMethodSubject;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class UnusedParameterAfterAssumeValuesFieldRuleTest 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)
+        .addKeepRules("-assumevalues class * { int f return 42; }")
+        .setMinApi(parameters)
+        .compile()
+        .inspect(
+            inspector -> {
+              ClassSubject aClassSubject = inspector.clazz(A.class);
+              assertThat(aClassSubject, isPresent());
+
+              List<FoundMethodSubject> aConstructorSubjects =
+                  aClassSubject.allMethods(FoundMethodSubject::isInstanceInitializer);
+              if (parameters.canHaveNonReboundConstructorInvoke()) {
+                assertEquals(0, aConstructorSubjects.size());
+              } else {
+                assertEquals(1, aConstructorSubjects.size());
+
+                FoundMethodSubject aConstructorSubject = aConstructorSubjects.get(0);
+                assertEquals(0, aConstructorSubject.getParameters().size());
+              }
+            })
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("42");
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      System.out.println(new A(args.length));
+    }
+  }
+
+  static class A {
+
+    int f;
+
+    A(int f) {
+      this.f = f;
+    }
+
+    @Override
+    public String toString() {
+      return Integer.toString(f);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/constructor/ForwardingConstructorUsedFromPlatformShakingOnDexTest.java b/src/test/java/com/android/tools/r8/shaking/constructor/ForwardingConstructorUsedFromPlatformShakingOnDexTest.java
index 534bbb9..7dacf64 100644
--- a/src/test/java/com/android/tools/r8/shaking/constructor/ForwardingConstructorUsedFromPlatformShakingOnDexTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/constructor/ForwardingConstructorUsedFromPlatformShakingOnDexTest.java
@@ -5,14 +5,21 @@
 package com.android.tools.r8.shaking.constructor;
 
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.onlyIf;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assume.assumeFalse;
 
 import com.android.tools.r8.NeverClassInline;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.apimodel.ApiModelingTestHelper;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -22,48 +29,168 @@
 @RunWith(Parameterized.class)
 public class ForwardingConstructorUsedFromPlatformShakingOnDexTest extends TestBase {
 
+  private static final String APPLICATION_INFO_DESCRIPTOR = "Landroid/content/pm/ApplicationInfo;";
+  private static final String FRAGMENT_DESCRIPTOR = "Landroid/app/Fragment;";
+  private static final String ZYGOTE_PRELOAD_DESCRIPTOR = "Landroid/app/ZygotePreload;";
+
+  private static final String EXPECTED_OUTPUT =
+      StringUtils.lines(
+          "Fragment.onCreate()", "MyFragment.onCreate()", "MyZygotePreload.doPreload()");
+
+  private static List<byte[]> transformedProgramClassFileData;
+  private static List<byte[]> transformedLibraryClassFileData;
+
   @Parameter(0)
+  public boolean enableModeling;
+
+  @Parameter(1)
   public TestParameters parameters;
 
-  @Parameters(name = "{0}")
-  public static TestParametersCollection data() {
-    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  @Parameters(name = "{1}, modeling: {0}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build());
+  }
+
+  @BeforeClass
+  public static void setup() throws Exception {
+    transformedProgramClassFileData = getTransformedProgramClasses();
+    transformedLibraryClassFileData = getTransformedLibraryClasses();
   }
 
   @Test
-  public void test() throws Exception {
+  public void testRuntime() throws Exception {
+    assumeFalse(enableModeling);
+    testForRuntime(parameters)
+        .addProgramClassFileData(transformedProgramClassFileData)
+        .addProgramClassFileData(transformedLibraryClassFileData)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  @Test
+  public void testR8() throws Exception {
     testForR8(parameters.getBackend())
-        .addProgramClasses(Main.class, MyFragment.class)
-        .addLibraryClasses(Fragment.class)
+        .addProgramClassFileData(transformedProgramClassFileData)
+        .addLibraryClassFileData(transformedLibraryClassFileData)
         .addLibraryFiles(parameters.getDefaultRuntimeLibrary())
         .addKeepMainRule(Main.class)
+        .applyIf(
+            !enableModeling,
+            testBuilder ->
+                testBuilder.addOptionsModification(
+                    options ->
+                        options
+                            .getRedundantBridgeRemovalOptions()
+                            .clearNoConstructorShrinkingHierarchiesForTesting()))
+        // Since Fragment is first defined in API 11.
+        .apply(ApiModelingTestHelper::disableStubbingOfClasses)
         .enableNeverClassInliningAnnotations()
         .setMinApi(parameters)
         .compile()
         .inspect(this::inspect)
-        .addBootClasspathClasses(Fragment.class)
+        .addRunClasspathClassFileData(transformedLibraryClassFileData)
         .run(parameters.getRuntime(), Main.class)
-        .assertSuccessWithOutputLines("Instantiating");
+        .applyIf(
+            enableModeling || !parameters.canHaveNonReboundConstructorInvoke(),
+            runResult -> runResult.assertSuccessWithOutput(EXPECTED_OUTPUT),
+            runResult -> runResult.assertFailureWithErrorThatThrows(NoSuchMethodException.class));
+  }
+
+  private static List<byte[]> getTransformedProgramClasses() throws Exception {
+    return ImmutableList.of(
+        transformer(Main.class)
+            .replaceClassDescriptorInMethodInstructions(
+                descriptor(Fragment.class), FRAGMENT_DESCRIPTOR)
+            .replaceClassDescriptorInMethodInstructions(
+                descriptor(ZygotePreload.class), ZYGOTE_PRELOAD_DESCRIPTOR)
+            .transform(),
+        transformer(MyFragment.class)
+            .replaceClassDescriptorInMethodInstructions(
+                descriptor(Fragment.class), FRAGMENT_DESCRIPTOR)
+            .setSuper(FRAGMENT_DESCRIPTOR)
+            .transform(),
+        transformer(MyZygotePreload.class)
+            .replaceClassDescriptorInMembers(
+                descriptor(ApplicationInfo.class), APPLICATION_INFO_DESCRIPTOR)
+            .setImplementsClassDescriptors(ZYGOTE_PRELOAD_DESCRIPTOR)
+            .transform());
+  }
+
+  private static List<byte[]> getTransformedLibraryClasses() throws Exception {
+    return ImmutableList.of(
+        transformer(ApplicationInfo.class)
+            .setClassDescriptor(APPLICATION_INFO_DESCRIPTOR)
+            .transform(),
+        transformer(Fragment.class).setClassDescriptor(FRAGMENT_DESCRIPTOR).transform(),
+        transformer(Platform.class)
+            .replaceClassDescriptorInMembers(descriptor(Fragment.class), FRAGMENT_DESCRIPTOR)
+            .replaceClassDescriptorInMethodInstructions(
+                descriptor(Fragment.class), FRAGMENT_DESCRIPTOR)
+            .replaceClassDescriptorInMembers(
+                descriptor(ZygotePreload.class), ZYGOTE_PRELOAD_DESCRIPTOR)
+            .replaceClassDescriptorInMethodInstructions(
+                descriptor(ApplicationInfo.class), APPLICATION_INFO_DESCRIPTOR)
+            .replaceClassDescriptorInMethodInstructions(
+                descriptor(ZygotePreload.class), ZYGOTE_PRELOAD_DESCRIPTOR)
+            .transform(),
+        transformer(ZygotePreload.class)
+            .replaceClassDescriptorInMembers(
+                descriptor(ApplicationInfo.class), APPLICATION_INFO_DESCRIPTOR)
+            .setClassDescriptor(ZYGOTE_PRELOAD_DESCRIPTOR)
+            .transform());
   }
 
   private void inspect(CodeInspector inspector) {
     ClassSubject myFragmentClassSubject = inspector.clazz(MyFragment.class);
     assertThat(myFragmentClassSubject, isPresent());
-    assertThat(myFragmentClassSubject.init(), isPresent());
+    assertThat(
+        myFragmentClassSubject.init(),
+        onlyIf(enableModeling || !parameters.canHaveNonReboundConstructorInvoke(), isPresent()));
+
+    ClassSubject myZygotePreloadClassSubject = inspector.clazz(MyZygotePreload.class);
+    assertThat(myZygotePreloadClassSubject, isPresent());
+    assertThat(
+        myZygotePreloadClassSubject.init(),
+        onlyIf(enableModeling || !parameters.canHaveNonReboundConstructorInvoke(), isPresent()));
   }
 
+  // Library classes.
+
+  public abstract static class ApplicationInfo {}
+
   public abstract static class Fragment {
 
-    public Fragment newInstance() throws Exception {
-      System.out.println("Instantiating");
-      return getClass().getDeclaredConstructor().newInstance();
+    public void onCreate() {
+      System.out.println("Fragment.onCreate()");
     }
   }
 
+  public interface ZygotePreload {
+
+    void doPreload(ApplicationInfo applicationInfo);
+  }
+
+  public static class Platform {
+
+    public static void accept(Fragment fragment) throws Exception {
+      Fragment newFragment = fragment.getClass().getDeclaredConstructor().newInstance();
+      newFragment.onCreate();
+    }
+
+    public static void accept(ZygotePreload runnable) throws Exception {
+      ZygotePreload newZygotePreload = runnable.getClass().getDeclaredConstructor().newInstance();
+      newZygotePreload.doPreload(null);
+    }
+  }
+
+  // Program classes.
+
   public static class Main {
 
     public static void main(String[] args) throws Exception {
-      new MyFragment().newInstance();
+      Platform.accept(new MyFragment());
+      Platform.accept(new MyZygotePreload());
     }
   }
 
@@ -71,5 +198,21 @@
   public static class MyFragment extends Fragment {
 
     public MyFragment() {}
+
+    @Override
+    public void onCreate() {
+      super.onCreate();
+      System.out.println("MyFragment.onCreate()");
+    }
+  }
+
+  public static class MyZygotePreload implements ZygotePreload {
+
+    public MyZygotePreload() {}
+
+    @Override
+    public void doPreload(ApplicationInfo applicationInfo) {
+      System.out.println("MyZygotePreload.doPreload()");
+    }
   }
 }
diff --git a/src/test/java/com/android/tools/r8/startup/MinimalStartupDexFromStartupMethodRuleTest.java b/src/test/java/com/android/tools/r8/startup/MinimalStartupDexFromStartupMethodRuleTest.java
index 6034910..ae0fe36 100644
--- a/src/test/java/com/android/tools/r8/startup/MinimalStartupDexFromStartupMethodRuleTest.java
+++ b/src/test/java/com/android/tools/r8/startup/MinimalStartupDexFromStartupMethodRuleTest.java
@@ -47,6 +47,7 @@
     runTest(testForR8(parameters.getBackend()).addKeepAllClassesRule());
   }
 
+  @SuppressWarnings("unchecked")
   private void runTest(TestCompilerBuilder<?, ?, ?, ?, ?> testCompilerBuilder) throws Exception {
     testCompilerBuilder
         .addInnerClasses(getClass())
diff --git a/src/test/java/com/android/tools/r8/tracereferences/TraceReferenceNonReboundConstructorCallTest.java b/src/test/java/com/android/tools/r8/tracereferences/TraceReferenceNonReboundConstructorCallTest.java
new file mode 100644
index 0000000..06835db
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/tracereferences/TraceReferenceNonReboundConstructorCallTest.java
@@ -0,0 +1,132 @@
+// Copyright (c) 2023, 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.tracereferences;
+
+import static com.android.tools.r8.utils.FileUtils.CLASS_EXTENSION;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.DiagnosticsChecker;
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+import com.android.tools.r8.utils.ZipUtils.ZipBuilder;
+import com.google.common.collect.ImmutableSet;
+import java.nio.file.Path;
+import java.util.HashSet;
+import java.util.Set;
+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 TraceReferenceNonReboundConstructorCallTest extends TestBase {
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  public TraceReferenceNonReboundConstructorCallTest(TestParameters parameters) {
+    parameters.assertNoneRuntime();
+  }
+
+  static class MissingReferencesConsumer implements TraceReferencesConsumer {
+
+    private Set<MethodReference> foundMethods = new HashSet<>();
+    private Set<MethodReference> missingMethods = new HashSet<>();
+
+    @Override
+    public void acceptType(TracedClass tracedClass, DiagnosticsHandler handler) {}
+
+    @Override
+    public void acceptField(TracedField tracedField, DiagnosticsHandler handler) {}
+
+    @Override
+    public void acceptMethod(TracedMethod tracedMethod, DiagnosticsHandler handler) {
+      if (tracedMethod.isMissingDefinition()) {
+        missingMethods.add(tracedMethod.getReference());
+      } else {
+        foundMethods.add(tracedMethod.getReference());
+      }
+    }
+  }
+
+  @Test
+  public void testCf() throws Throwable {
+    MissingReferencesConsumer consumer =
+        runTest(
+            ZipBuilder.builder(temp.newFile("source.jar").toPath())
+                .addFilesRelative(
+                    ToolHelper.getClassPathForTests(),
+                    ToolHelper.getClassFileForTestClass(Main.class))
+                .build());
+    ImmutableSet<MethodReference> expectedFoundMethods = ImmutableSet.of();
+    ImmutableSet<MethodReference> expectedMissingMethods =
+        ImmutableSet.of(MethodReferenceUtils.instanceConstructor(SubClass.class));
+    assertEquals(expectedFoundMethods, consumer.foundMethods);
+    assertEquals(expectedMissingMethods, consumer.missingMethods);
+  }
+
+  @Test
+  public void testDex() throws Throwable {
+    MissingReferencesConsumer consumer =
+        runTest(
+            testForD8(Backend.DEX)
+                .addProgramClasses(Main.class)
+                .release()
+                .setMinApi(AndroidApiLevel.B)
+                .compile()
+                .writeToZip());
+    ImmutableSet<MethodReference> expectedFoundMethods =
+        ImmutableSet.of(MethodReferenceUtils.instanceConstructor(SuperClass.class));
+    ImmutableSet<MethodReference> expectedMissingMethods = ImmutableSet.of();
+    assertEquals(expectedFoundMethods, consumer.foundMethods);
+    assertEquals(expectedMissingMethods, consumer.missingMethods);
+  }
+
+  private MissingReferencesConsumer runTest(Path sourceFile) throws Throwable {
+    Path dir = temp.newFolder().toPath();
+    Path targetJar =
+        ZipBuilder.builder(dir.resolve("target.jar"))
+            .addFilesRelative(
+                ToolHelper.getClassPathForTests(),
+                ToolHelper.getClassFileForTestClass(SuperClass.class))
+            .addBytes(
+                binaryName(SubClass.class) + CLASS_EXTENSION,
+                transformer(SubClass.class).removeMethodsWithName("<init>").transform())
+            .build();
+    DiagnosticsChecker diagnosticsChecker = new DiagnosticsChecker();
+    MissingReferencesConsumer consumer = new MissingReferencesConsumer();
+    TraceReferences.run(
+        TraceReferencesCommand.builder(diagnosticsChecker)
+            .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
+            .addSourceFiles(sourceFile)
+            .addTargetFiles(targetJar)
+            .setConsumer(consumer)
+            .build());
+    return consumer;
+  }
+
+  static class SuperClass {}
+
+  static class SubClass extends SuperClass {
+
+    // Removed by transformer.
+    SubClass() {}
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) {
+      new SubClass();
+    }
+  }
+}
diff --git a/third_party/r8mappings.tar.gz.sha1 b/third_party/r8mappings.tar.gz.sha1
index a00ac52..bb6bada 100644
--- a/third_party/r8mappings.tar.gz.sha1
+++ b/third_party/r8mappings.tar.gz.sha1
@@ -1 +1 @@
-baaf8e15c9f54677c30caf070a22319ccd46539c
\ No newline at end of file
+36c9b3f2335c084a6d5e270a7ac8fdcca0e72a74
\ No newline at end of file