Merge commit '1e6ec6788aec5b267c99d2d71771dc2c0499a15f' into dev-release
diff --git a/.gitignore b/.gitignore
index d76279a..ae1be52 100644
--- a/.gitignore
+++ b/.gitignore
@@ -40,6 +40,8 @@
 third_party/android_jar/lib
 third_party/android_jar/lib-v[0-9][0-9]
 third_party/android_jar/lib-v[0-9][0-9].tar.gz
+third_party/android_jar/lib-master
+third_party/android_jar/lib-master.tar.gz
 third_party/android_jar/lib.tar.gz
 third_party/android_jar/libcore_latest
 third_party/android_jar/libcore_latest.tar.gz
@@ -63,6 +65,8 @@
 third_party/benchmarks/wordpress.tar.gz
 third_party/binary_compatibility_tests/compiler_api_tests.tar.gz
 third_party/binary_compatibility_tests/compiler_api_tests
+third_party/bundletool/bundletool-1.11.0
+third_party/bundletool/bundletool-1.11.0.tar.gz
 third_party/cf_segments
 third_party/cf_segments.tar.gz
 third_party/chrome/*
@@ -272,6 +276,8 @@
 tools/*/host/art-12.0.0-beta4.tar.gz
 tools/*/host/art-13-dev
 tools/*/host/art-13-dev.tar.gz
+tools/*/host/art-master
+tools/*/host/art-master.tar.gz
 tools/*/art.tar.gz
 tools/*/dalvik
 tools/*/dalvik-4.0.4
diff --git a/Regress78493232.jar b/Regress78493232.jar
deleted file mode 100644
index 6dee6f0..0000000
--- a/Regress78493232.jar
+++ /dev/null
Binary files differ
diff --git a/build.gradle b/build.gradle
index a138d84..8f84e7f 100644
--- a/build.gradle
+++ b/build.gradle
@@ -349,9 +349,11 @@
                 "android_jar/lib-v31",
                 "android_jar/lib-v32",
                 "android_jar/lib-v33",
+		"android_jar/lib-master",
                 "api_database/api_database",
                 "api-outlining/simple-app-dump",
                 "binary_compatibility_tests/compiler_api_tests",
+                "bundletool/bundletool-1.11.0",
                 "core-lambda-stubs",
                 "dagger/2.41",
                 "dart-sdk",
@@ -410,6 +412,7 @@
                 "linux/art-10.0.0",
                 "linux/host/art-12.0.0-beta4",
                 "linux/host/art-13-dev",
+                "linux/host/art-master",
                 "linux/dalvik",
                 "linux/dalvik-4.0.4",
                 "${osString}/dx",
diff --git a/scripts/update-host-art.sh b/scripts/update-host-art.sh
index 58165cd..44e5a49 100755
--- a/scripts/update-host-art.sh
+++ b/scripts/update-host-art.sh
@@ -91,9 +91,24 @@
 ANDROID_TARGET_BUILD=$ANDROID_CHECKOUT/out/target
 DEST=$DEST_ROOT/$ART_DIR
 
-# Clean out the previous version of Art
+# Clean out the previous version of Art.
 rm -rf $DEST
 
+# Copy build_spec.xml for documentation.
+mkdir -p $DEST
+if [ -f $ANDROID_CHECKOUT/build_spec.xml ]; then
+  cp $ANDROID_CHECKOUT/build_spec.xml $DEST
+  # Remove the build spec to ensure it is created anew for a new build.
+  rm $ANDROID_CHECKOUT/build_spec.xml
+else
+  echo "File $ANDROID_CHECKOUT/build_spec.xml not found. Please run"
+  echo
+  echo "  repo manifest -r -o build_spec.xml"
+  echo
+  echo "in $ANDROID_CHECKOUT"
+  exit 1
+fi
+
 # Required binaries and scripts.
 mkdir -p $DEST/bin
 if [ -f $ANDROID_HOST_BUILD/bin/art ]; then
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs_path.json b/src/library_desugar/jdk11/desugar_jdk_libs_path.json
index e1298de..4128a53 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs_path.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs_path.json
@@ -437,6 +437,14 @@
           "j$.util.Optional": "java.util.Optional"
         }
       }
+    },
+    {
+      "api_level_below_or_equal": 18,
+      "retarget_static_field": {
+        "sun.nio.cs.US_ASCII sun.nio.cs.US_ASCII#INSTANCE": "java.nio.charset.Charset java.nio.charset.StandardCharsets#US_ASCII",
+        "sun.nio.cs.ISO_8859_1 sun.nio.cs.ISO_8859_1#INSTANCE": "java.nio.charset.Charset java.nio.charset.StandardCharsets#ISO_8859_1",
+        "sun.nio.cs.UTF_8 sun.nio.cs.UTF_8#INSTANCE": "java.nio.charset.Charset java.nio.charset.StandardCharsets#UTF_8"
+      }
     }
   ],
   "shrinker_config": [
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 03ec1dd..8be1456 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -11,7 +11,7 @@
 import com.android.tools.r8.dex.ApplicationWriter;
 import com.android.tools.r8.dex.Marker;
 import com.android.tools.r8.dex.Marker.Tool;
-import com.android.tools.r8.experimental.startup.StartupInstrumentation;
+import com.android.tools.r8.experimental.startup.instrumentation.StartupInstrumentation;
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppServices;
 import com.android.tools.r8.graph.AppView;
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 7498045..9818440 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -10,7 +10,6 @@
 import com.android.tools.r8.dump.DumpOptions;
 import com.android.tools.r8.errors.DexFileOverflowDiagnostic;
 import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
-import com.android.tools.r8.experimental.startup.StartupConfiguration;
 import com.android.tools.r8.features.FeatureSplitConfiguration;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.inspector.Inspector;
@@ -975,11 +974,6 @@
 
     internal.featureSplitConfiguration = featureSplitConfiguration;
 
-    internal
-        .getStartupOptions()
-        .setStartupConfiguration(
-            StartupConfiguration.createStartupConfiguration(getDexItemFactory(), getReporter()));
-
     internal.syntheticProguardRulesConsumer = syntheticProguardRulesConsumer;
 
     internal.outputInspections = InspectorImpl.wrapInspections(getOutputInspections());
diff --git a/src/main/java/com/android/tools/r8/StartupProfileProvider.java b/src/main/java/com/android/tools/r8/StartupProfileProvider.java
new file mode 100644
index 0000000..fc5e4b1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/StartupProfileProvider.java
@@ -0,0 +1,13 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8;
+
+/** Interface for providing a startup profile to the compiler. */
+@FunctionalInterface
+public interface StartupProfileProvider {
+
+  /** Return the startup profile. */
+  String get();
+}
diff --git a/src/main/java/com/android/tools/r8/StringResource.java b/src/main/java/com/android/tools/r8/StringResource.java
index a28f936..515f2ab 100644
--- a/src/main/java/com/android/tools/r8/StringResource.java
+++ b/src/main/java/com/android/tools/r8/StringResource.java
@@ -33,7 +33,7 @@
    *
    * @param file Path of file with UTF-8 encoded text.
    */
-  static StringResource fromFile(Path file) {
+  static FileResource fromFile(Path file) {
     return fromFile(file, StandardCharsets.UTF_8);
   }
 
@@ -45,7 +45,7 @@
    * @param file Path of file.
    * @param charset Charset coding of file.
    */
-  static StringResource fromFile(Path file, Charset charset) {
+  static FileResource fromFile(Path file, Charset charset) {
     return new FileResource(file, charset);
   }
 
@@ -74,7 +74,7 @@
     }
 
     @Override
-    public String getString() throws ResourceException {
+    public String getString() {
       return content;
     }
   }
@@ -105,5 +105,13 @@
         throw new ResourceException(origin, e);
       }
     }
+
+    public String getStringWithRuntimeException() {
+      try {
+        return getString();
+      } catch (ResourceException e) {
+        throw new RuntimeException(e);
+      }
+    }
   }
 }
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 fec4b65..eaf3a3d 100644
--- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelCompute.java
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelCompute.java
@@ -20,7 +20,7 @@
   public AndroidApiLevelCompute() {
     knownApiLevelCache = new KnownApiLevel[AndroidApiLevel.LATEST.getLevel() + 1];
     for (AndroidApiLevel value : AndroidApiLevel.values()) {
-      if (value != AndroidApiLevel.ANDROID_PLATFORM) {
+      if (value != AndroidApiLevel.ANDROID_PLATFORM && value != AndroidApiLevel.MASTER) {
         knownApiLevelCache[value.getLevel()] = new KnownApiLevel(value);
       }
     }
@@ -30,6 +30,9 @@
     if (apiLevel == AndroidApiLevel.ANDROID_PLATFORM) {
       return ComputedApiLevel.platform();
     }
+    if (apiLevel == AndroidApiLevel.MASTER) {
+      return ComputedApiLevel.master();
+    }
     return knownApiLevelCache[apiLevel.getLevel()];
   }
 
@@ -55,9 +58,13 @@
   }
 
   public ComputedApiLevel computeInitialMinApiLevel(InternalOptions options) {
-      return options.getMinApiLevel() == AndroidApiLevel.ANDROID_PLATFORM
-          ? ComputedApiLevel.platform()
-          : new KnownApiLevel(options.getMinApiLevel());
+    if (options.getMinApiLevel() == AndroidApiLevel.ANDROID_PLATFORM) {
+      return ComputedApiLevel.platform();
+    }
+    if (options.getMinApiLevel() == AndroidApiLevel.MASTER) {
+      return ComputedApiLevel.master();
+    }
+    return new KnownApiLevel(options.getMinApiLevel());
   }
 
   public ComputedApiLevel getPlatformApiLevelOrUnknown(AppView<?> appView) {
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 b1b581b..50855bb 100644
--- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
@@ -76,7 +76,10 @@
     if (reference.getContextType() == factory.objectType) {
       return appView.computedMinApiLevel();
     }
-    if (appView.options().machineDesugaredLibrarySpecification.isSupported(reference)) {
+    if (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/androidapi/ComputedApiLevel.java b/src/main/java/com/android/tools/r8/androidapi/ComputedApiLevel.java
index 3ff4dcb..7562429 100644
--- a/src/main/java/com/android/tools/r8/androidapi/ComputedApiLevel.java
+++ b/src/main/java/com/android/tools/r8/androidapi/ComputedApiLevel.java
@@ -27,6 +27,10 @@
     return KnownApiLevel.PLATFORM_INSTANCE;
   }
 
+  static KnownApiLevel master() {
+    return KnownApiLevel.MASTER_INSTANCE;
+  }
+
   default boolean isNotSetApiLevel() {
     return false;
   }
@@ -151,6 +155,8 @@
     private static final KnownApiLevel PLATFORM_INSTANCE =
         new KnownApiLevel(AndroidApiLevel.ANDROID_PLATFORM);
 
+    private static final KnownApiLevel MASTER_INSTANCE = new KnownApiLevel(AndroidApiLevel.MASTER);
+
     private final AndroidApiLevel apiLevel;
 
     KnownApiLevel(AndroidApiLevel apiLevel) {
diff --git a/src/main/java/com/android/tools/r8/cf/LoadStoreHelper.java b/src/main/java/com/android/tools/r8/cf/LoadStoreHelper.java
index faf9046..f180e60 100644
--- a/src/main/java/com/android/tools/r8/cf/LoadStoreHelper.java
+++ b/src/main/java/com/android/tools/r8/cf/LoadStoreHelper.java
@@ -204,11 +204,17 @@
     }
     add(store, storeBlock, instruction.getPosition(), it);
     if (hasCatchHandlers && !instruction.instructionTypeCanThrow()) {
-      it.split(this.code, this.blockIterator);
-      this.blockIterator.previous();
+      splitAfterStoredOutValue(it);
     }
   }
 
+  // DebugLocalWrite encodes a store and it needs to consistently split out the catch range after
+  // its store.
+  public void splitAfterStoredOutValue(InstructionListIterator it) {
+    it.split(this.code, this.blockIterator);
+    this.blockIterator.previous();
+  }
+
   public void popOutType(DexType type, Instruction instruction, InstructionListIterator it) {
     popOutValue(createStackValue(type, 0), instruction, it);
   }
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index b0d4999..4932691 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -21,8 +21,10 @@
 import com.android.tools.r8.debuginfo.DebugRepresentation.DebugRepresentationPredicate;
 import com.android.tools.r8.dex.FileWriter.ByteBufferResult;
 import com.android.tools.r8.dex.VirtualFile.FilePerInputClassDistributor;
+import com.android.tools.r8.dex.VirtualFile.ItemUseInfo;
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.experimental.startup.StartupCompleteness;
+import com.android.tools.r8.experimental.startup.StartupOrder;
 import com.android.tools.r8.features.FeatureSplitConfiguration.DataResourceProvidersAndConsumer;
 import com.android.tools.r8.graph.AppServices;
 import com.android.tools.r8.graph.AppView;
@@ -34,6 +36,7 @@
 import com.android.tools.r8.graph.DexEncodedArray;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItem;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
@@ -53,6 +56,7 @@
 import com.android.tools.r8.utils.Box;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.ExceptionUtils;
+import com.android.tools.r8.utils.IntBox;
 import com.android.tools.r8.utils.InternalGlobalSyntheticsProgramConsumer;
 import com.android.tools.r8.utils.InternalGlobalSyntheticsProgramConsumer.InternalGlobalSyntheticsDexIndexedConsumer;
 import com.android.tools.r8.utils.InternalGlobalSyntheticsProgramConsumer.InternalGlobalSyntheticsDexPerFileConsumer;
@@ -214,7 +218,16 @@
         && options.enableMainDexListCheck) {
       distributor = new VirtualFile.MonoDexDistributor(this, classes, options);
     } else {
-      distributor = new VirtualFile.FillFilesDistributor(this, classes, options, executorService);
+      // Retrieve the startup order for writing the app. In R8, the startup order is created
+      // up-front to guide optimizations through-out the compilation. In D8, the startup
+      // order is only used for writing the app, so we create it here for the first time.
+      StartupOrder startupOrder =
+          appView.appInfo().hasClassHierarchy()
+              ? appView.appInfoWithClassHierarchy().getStartupOrder()
+              : StartupOrder.createInitialStartupOrder(options);
+      distributor =
+          new VirtualFile.FillFilesDistributor(
+              this, classes, options, executorService, startupOrder);
     }
 
     List<VirtualFile> virtualFiles = distributor.run();
@@ -493,12 +506,39 @@
     timing.end();
   }
 
+  private <T extends DexItem> void printUse(Map<T, ItemUseInfo> useMap, String label) {
+    assert options.testing.calculateItemUseCountInDex;
+    List<IntBox> notMany = new ArrayList<>();
+    for (int i = 0; i < ItemUseInfo.getManyCount() - 1; i++) {
+      notMany.add(new IntBox());
+    }
+    IntBox many = new IntBox();
+    useMap.forEach(
+        (item, itemUseInfo) -> {
+          if (itemUseInfo.isMany()) {
+            many.increment();
+          } else {
+            assert itemUseInfo.getSize() >= 1;
+            notMany.get(itemUseInfo.getSize() - 1).increment();
+          }
+        });
+
+    System.out.print(label);
+    for (int i = 0; i < ItemUseInfo.getManyCount() - 1; i++) {
+      System.out.print("," + notMany.get(i).get());
+      notMany.add(new IntBox());
+    }
+    System.out.println("," + many.get());
+  }
+
   private void writeVirtualFile(
       VirtualFile virtualFile, Timing timing, List<DexString> forcedStrings) {
     if (virtualFile.isEmpty()) {
       return;
     }
 
+    printItemUseInfo(virtualFile);
+
     ProgramConsumer consumer;
     ByteBufferProvider byteBufferProvider;
 
@@ -886,4 +926,39 @@
       return value;
     }
   }
+
+  private void printItemUseInfo(VirtualFile virtualFile) {
+    if (options.testing.calculateItemUseCountInDex) {
+      synchronized (System.out) {
+        System.out.print("\"Item\"");
+        for (int i = 0; i < ItemUseInfo.getManyCount() - 1; i++) {
+          System.out.print(",\"" + (i + 1) + " use\"");
+        }
+        System.out.println(",\"Not single use\"");
+        printUse(virtualFile.indexedItems.stringsUse, "Strings");
+        printUse(virtualFile.indexedItems.typesUse, "Types");
+        printUse(virtualFile.indexedItems.protosUse, "Protos");
+        printUse(virtualFile.indexedItems.fieldsUse, "Fields");
+        printUse(virtualFile.indexedItems.methodsUse, "Methods");
+        printUse(virtualFile.indexedItems.callSitesUse, "CallSites");
+        printUse(virtualFile.indexedItems.methodHandlesUse, "MethodHandles");
+        if (options.testing.calculateItemUseCountInDexDumpSingleUseStrings) {
+          virtualFile.indexedItems.stringsUse.forEach(
+              (string, info) -> {
+                if (info.getSizeOrMany() == 1) {
+                  System.out.println(info.getUse().iterator().next() + ": " + string.toString());
+                }
+              });
+        }
+      }
+    } else {
+      assert virtualFile.indexedItems.stringsUse.isEmpty();
+      assert virtualFile.indexedItems.typesUse.isEmpty();
+      assert virtualFile.indexedItems.protosUse.isEmpty();
+      assert virtualFile.indexedItems.fieldsUse.isEmpty();
+      assert virtualFile.indexedItems.methodsUse.isEmpty();
+      assert virtualFile.indexedItems.callSitesUse.isEmpty();
+      assert virtualFile.indexedItems.methodHandlesUse.isEmpty();
+    }
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/dex/MixedSectionLayoutStrategy.java b/src/main/java/com/android/tools/r8/dex/MixedSectionLayoutStrategy.java
index 2381e6f..13a4a2c 100644
--- a/src/main/java/com/android/tools/r8/dex/MixedSectionLayoutStrategy.java
+++ b/src/main/java/com/android/tools/r8/dex/MixedSectionLayoutStrategy.java
@@ -22,15 +22,16 @@
 
   public static MixedSectionLayoutStrategy create(
       AppView<?> appView, MixedSectionOffsets mixedSectionOffsets, VirtualFile virtualFile) {
-    StartupOrder startupOrderForWriting =
-        appView.options().getStartupOptions().isStartupLayoutOptimizationsEnabled()
-                && virtualFile.getId() == 0
-                && appView.hasClassHierarchy()
-            ? appView
-                .appInfoWithClassHierarchy()
-                .getStartupOrder()
-                .toStartupOrderForWriting(appView)
-            : StartupOrder.empty();
+    StartupOrder startupOrderForWriting;
+    if (virtualFile.getStartupOrder().isEmpty()) {
+      startupOrderForWriting = StartupOrder.empty();
+    } else {
+      assert virtualFile.getId() == 0;
+      startupOrderForWriting =
+          appView.options().getStartupOptions().isStartupLayoutOptimizationsEnabled()
+              ? virtualFile.getStartupOrder().toStartupOrderForWriting(appView)
+              : StartupOrder.empty();
+    }
     MixedSectionLayoutStrategy mixedSectionLayoutStrategy =
         startupOrderForWriting.isEmpty()
             ? new DefaultMixedSectionLayoutStrategy(appView, mixedSectionOffsets)
diff --git a/src/main/java/com/android/tools/r8/dex/VirtualFile.java b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
index 55923ed..fafe700 100644
--- a/src/main/java/com/android/tools/r8/dex/VirtualFile.java
+++ b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
@@ -4,7 +4,6 @@
 package com.android.tools.r8.dex;
 
 import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
-import static com.google.common.base.Predicates.alwaysFalse;
 
 import com.android.tools.r8.FeatureSplit;
 import com.android.tools.r8.debuginfo.DebugRepresentation;
@@ -56,6 +55,7 @@
 import java.util.HashSet;
 import java.util.IdentityHashMap;
 import java.util.Iterator;
+import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
@@ -70,36 +70,38 @@
   public static final int MAX_ENTRIES = Constants.U16BIT_MAX + 1;
 
   private final int id;
-  private final VirtualFileIndexedItemCollection indexedItems;
+  public final VirtualFileIndexedItemCollection indexedItems;
   private final IndexedItemTransaction transaction;
   private final FeatureSplit featureSplit;
+  private final StartupOrder startupOrder;
 
   private final DexString primaryClassDescriptor;
   private DebugRepresentation debugRepresentation;
 
   VirtualFile(int id, AppView<?> appView) {
-    this(id, appView, null, null);
+    this(id, appView, null, null, StartupOrder.empty());
   }
 
   VirtualFile(
       int id,
       AppView<?> appView,
       FeatureSplit featureSplit) {
-    this(id, appView, null, featureSplit);
+    this(id, appView, null, featureSplit, StartupOrder.empty());
   }
 
   private VirtualFile(
       int id,
       AppView<?> appView,
       DexProgramClass primaryClass) {
-    this(id, appView, primaryClass, null);
+    this(id, appView, primaryClass, null, StartupOrder.empty());
   }
 
   private VirtualFile(
       int id,
       AppView<?> appView,
       DexProgramClass primaryClass,
-      FeatureSplit featureSplit) {
+      FeatureSplit featureSplit,
+      StartupOrder startupOrder) {
     this.id = id;
     this.indexedItems = new VirtualFileIndexedItemCollection(appView);
     this.transaction = new IndexedItemTransaction(indexedItems, appView);
@@ -108,6 +110,7 @@
             ? null
             : appView.getNamingLens().lookupClassDescriptor(primaryClass.type);
     this.featureSplit = featureSplit;
+    this.startupOrder = startupOrder;
   }
 
   public int getId() {
@@ -127,6 +130,10 @@
     return featureSplit;
   }
 
+  public StartupOrder getStartupOrder() {
+    return startupOrder;
+  }
+
   public String getPrimaryClassDescriptor() {
     return primaryClassDescriptor == null ? null : primaryClassDescriptor.toString();
   }
@@ -351,13 +358,17 @@
     protected final InternalOptions options;
 
     DistributorBase(
-        ApplicationWriter writer, Collection<DexProgramClass> classes, InternalOptions options) {
+        ApplicationWriter writer,
+        Collection<DexProgramClass> classes,
+        InternalOptions options,
+        StartupOrder startupOrder) {
       super(writer);
       this.options = options;
       this.classes = SetUtils.newIdentityHashSet(classes);
 
-      // Create the primary dex file. The distribution will add more if needed.
-      mainDexFile = new VirtualFile(0, appView);
+      // Create the primary dex file. The distribution will add more if needed. We use the startup
+      // order (if any) to guide the layout of the primary dex file.
+      mainDexFile = new VirtualFile(0, appView, null, null, startupOrder);
       assert virtualFiles.isEmpty();
       virtualFiles.add(mainDexFile);
       addMarkers(mainDexFile);
@@ -428,7 +439,7 @@
     }
 
     protected void addFeatureSplitFiles(
-        Map<FeatureSplit, Set<DexProgramClass>> featureSplitClasses) {
+        Map<FeatureSplit, Set<DexProgramClass>> featureSplitClasses, StartupOrder startupOrder) {
       if (featureSplitClasses.isEmpty()) {
         return;
       }
@@ -451,6 +462,7 @@
                 appView,
                 featureSplitSetEntry.getValue(),
                 originalNames,
+                startupOrder,
                 nextFileId)
             .run();
       }
@@ -458,15 +470,19 @@
   }
 
   public static class FillFilesDistributor extends DistributorBase {
+
     private final ExecutorService executorService;
+    private final StartupOrder startupOrder;
 
     FillFilesDistributor(
         ApplicationWriter writer,
         Collection<DexProgramClass> classes,
         InternalOptions options,
-        ExecutorService executorService) {
-      super(writer, classes, options);
+        ExecutorService executorService,
+        StartupOrder startupOrder) {
+      super(writer, classes, options, startupOrder);
       this.executorService = executorService;
+      this.startupOrder = startupOrder;
     }
 
     @Override
@@ -507,10 +523,16 @@
             .distribute();
       } else {
         new PackageSplitPopulator(
-                virtualFiles, filesForDistribution, appView, classes, originalNames, nextFileId)
+                virtualFiles,
+                filesForDistribution,
+                appView,
+                classes,
+                originalNames,
+                startupOrder,
+                nextFileId)
             .run();
       }
-      addFeatureSplitFiles(featureSplitClasses);
+      addFeatureSplitFiles(featureSplitClasses, startupOrder);
 
       assert totalClassNumber == virtualFiles.stream().mapToInt(dex -> dex.classes().size()).sum();
       return virtualFiles;
@@ -520,7 +542,7 @@
   public static class MonoDexDistributor extends DistributorBase {
     MonoDexDistributor(
         ApplicationWriter writer, Collection<DexProgramClass> classes, InternalOptions options) {
-      super(writer, classes, options);
+      super(writer, classes, options, StartupOrder.empty());
     }
 
     @Override
@@ -536,14 +558,63 @@
       if (options.featureSplitConfiguration != null) {
         if (!featureSplitClasses.isEmpty()) {
           // TODO(141334414): Figure out if we allow multidex in features even when mono-dexing
-          addFeatureSplitFiles(featureSplitClasses);
+          addFeatureSplitFiles(featureSplitClasses, StartupOrder.empty());
         }
       }
       return virtualFiles;
     }
   }
 
-  private static class VirtualFileIndexedItemCollection implements IndexedItemCollection {
+  public static class ItemUseInfo {
+
+    private static final Set<DexProgramClass> MANY = null;
+    private static final int manyCount = 3;
+
+    private Set<DexProgramClass> use;
+
+    ItemUseInfo(Set<DexProgramClass> initialUse) {
+      use = initialUse.size() >= manyCount ? MANY : initialUse;
+    }
+
+    public void addUse(Set<DexProgramClass> moreUse) {
+      if (use == MANY) {
+        return;
+      }
+      if (moreUse.size() >= manyCount) {
+        use = MANY;
+        return;
+      }
+      use.addAll(moreUse);
+      if (use.size() >= manyCount) {
+        use = MANY;
+      }
+    }
+
+    public static int getManyCount() {
+      return manyCount;
+    }
+
+    public boolean isMany() {
+      return use == MANY;
+    }
+
+    public int getSize() {
+      assert use != MANY;
+      return use.size();
+    }
+
+    public int getSizeOrMany() {
+      if (use == MANY) return -1;
+      return use.size();
+    }
+
+    public Set<DexProgramClass> getUse() {
+      assert !isMany();
+      return use;
+    }
+  }
+
+  public static class VirtualFileIndexedItemCollection implements IndexedItemCollection {
 
     private final GraphLens graphLens;
     private final InitClassLens initClassLens;
@@ -558,6 +629,14 @@
     private final Set<DexCallSite> callSites = Sets.newIdentityHashSet();
     private final Set<DexMethodHandle> methodHandles = Sets.newIdentityHashSet();
 
+    public final Map<DexString, ItemUseInfo> stringsUse = new IdentityHashMap<>();
+    public final Map<DexType, ItemUseInfo> typesUse = new IdentityHashMap<>();
+    public final Map<DexProto, ItemUseInfo> protosUse = new IdentityHashMap<>();
+    public final Map<DexField, ItemUseInfo> fieldsUse = new IdentityHashMap<>();
+    public final Map<DexMethod, ItemUseInfo> methodsUse = new IdentityHashMap<>();
+    public final Map<DexCallSite, ItemUseInfo> callSitesUse = new IdentityHashMap<>();
+    public final Map<DexMethodHandle, ItemUseInfo> methodHandlesUse = new IdentityHashMap<>();
+
     public VirtualFileIndexedItemCollection(AppView<?> appView) {
       this.graphLens = appView.graphLens();
       this.initClassLens = appView.initClassLens();
@@ -616,6 +695,238 @@
 
   public static class IndexedItemTransaction implements IndexedItemCollection {
 
+    public interface ClassUseCollector {
+
+      void collectClassDependencies(DexProgramClass clazz);
+
+      void collectClassDependenciesDone();
+
+      void clear();
+
+      Map<DexString, Set<DexProgramClass>> getStringsUse();
+
+      Map<DexType, Set<DexProgramClass>> getTypesUse();
+
+      Map<DexProto, Set<DexProgramClass>> getProtosUse();
+
+      Map<DexField, Set<DexProgramClass>> getFieldsUse();
+
+      Map<DexMethod, Set<DexProgramClass>> getMethodsUse();
+
+      Map<DexCallSite, Set<DexProgramClass>> getCallSitesUse();
+
+      Map<DexMethodHandle, Set<DexProgramClass>> getMethodHandlesUse();
+    }
+
+    public static class EmptyIndexedItemUsedByClasses implements ClassUseCollector {
+      @Override
+      public void collectClassDependencies(DexProgramClass clazz) {
+        // Do nothing.
+      }
+
+      @Override
+      public void collectClassDependenciesDone() {
+        // Do nothing.
+      }
+
+      @Override
+      public void clear() {
+        // DO nothing.
+      }
+
+      @Override
+      public Map<DexString, Set<DexProgramClass>> getStringsUse() {
+        assert false;
+        return null;
+      }
+
+      @Override
+      public Map<DexType, Set<DexProgramClass>> getTypesUse() {
+        assert false;
+        return null;
+      }
+
+      @Override
+      public Map<DexProto, Set<DexProgramClass>> getProtosUse() {
+        assert false;
+        return null;
+      }
+
+      @Override
+      public Map<DexField, Set<DexProgramClass>> getFieldsUse() {
+        assert false;
+        return null;
+      }
+
+      @Override
+      public Map<DexMethod, Set<DexProgramClass>> getMethodsUse() {
+        assert false;
+        return null;
+      }
+
+      @Override
+      public Map<DexCallSite, Set<DexProgramClass>> getCallSitesUse() {
+        assert false;
+        return null;
+      }
+
+      @Override
+      public Map<DexMethodHandle, Set<DexProgramClass>> getMethodHandlesUse() {
+        assert false;
+        return null;
+      }
+    }
+
+    public static class IndexedItemsUsedByClassesInTransaction
+        implements IndexedItemCollection, ClassUseCollector {
+
+      private final AppView<?> appView;
+      private final LensCodeRewriterUtils rewriter;
+      private final VirtualFileIndexedItemCollection base;
+      private final IndexedItemTransaction transaction;
+
+      private final Set<DexProgramClass> classes = new LinkedHashSet<>();
+
+      private final Map<DexString, Set<DexProgramClass>> stringsUse = new IdentityHashMap<>();
+      private final Map<DexType, Set<DexProgramClass>> typesUse = new IdentityHashMap<>();
+      private final Map<DexProto, Set<DexProgramClass>> protosUse = new IdentityHashMap<>();
+      private final Map<DexField, Set<DexProgramClass>> fieldsUse = new IdentityHashMap<>();
+      private final Map<DexMethod, Set<DexProgramClass>> methodsUse = new IdentityHashMap<>();
+      private final Map<DexCallSite, Set<DexProgramClass>> callSitesUse = new IdentityHashMap<>();
+      private final Map<DexMethodHandle, Set<DexProgramClass>> methodHandlessUse =
+          new LinkedHashMap<>();
+
+      DexProgramClass currentClass = null;
+
+      private IndexedItemsUsedByClassesInTransaction(
+          AppView<?> appView,
+          LensCodeRewriterUtils rewriter,
+          VirtualFileIndexedItemCollection base,
+          IndexedItemTransaction transaction) {
+        this.appView = appView;
+        this.rewriter = rewriter;
+        this.base = base;
+        this.transaction = transaction;
+      }
+
+      @Override
+      public void collectClassDependencies(DexProgramClass clazz) {
+        clazz.collectIndexedItems(appView, this, rewriter);
+      }
+
+      @Override
+      public boolean addClass(DexProgramClass clazz) {
+        assert currentClass == null;
+        currentClass = clazz;
+        assert !classes.contains(clazz);
+        classes.add(clazz);
+        return true;
+      }
+
+      @Override
+      public void collectClassDependenciesDone() {
+        currentClass = null;
+      }
+
+      @Override
+      public boolean addString(DexString string) {
+        collectUse(string, transaction.strings, base.strings, stringsUse);
+        return true;
+      }
+
+      @Override
+      public boolean addType(DexType type) {
+        collectUse(type, transaction.types, base.types, typesUse);
+        return true;
+      }
+
+      @Override
+      public boolean addProto(DexProto proto) {
+        collectUse(proto, transaction.protos, base.protos, protosUse);
+        return true;
+      }
+
+      @Override
+      public boolean addField(DexField field) {
+        collectUse(field, transaction.fields, base.fields, fieldsUse);
+        return true;
+      }
+
+      @Override
+      public boolean addMethod(DexMethod method) {
+        collectUse(method, transaction.methods, base.methods, methodsUse);
+        return true;
+      }
+
+      @Override
+      public boolean addCallSite(DexCallSite callSite) {
+        collectUse(callSite, transaction.callSites, base.callSites, callSitesUse);
+        return true;
+      }
+
+      @Override
+      public boolean addMethodHandle(DexMethodHandle methodHandle) {
+        collectUse(methodHandle, transaction.methodHandles, base.methodHandles, methodHandlessUse);
+        return true;
+      }
+
+      private <T extends DexItem> void collectUse(
+          T item, Set<T> set, Set<T> baseSet, Map<T, Set<DexProgramClass>> use) {
+        assert baseSet.contains(item) || set.contains(item);
+        if (set.contains(item)) {
+          assert classes.contains(currentClass);
+        }
+        use.computeIfAbsent(item, unused -> Sets.newIdentityHashSet()).add(currentClass);
+      }
+
+      @Override
+      public Map<DexString, Set<DexProgramClass>> getStringsUse() {
+        return stringsUse;
+      }
+
+      @Override
+      public Map<DexType, Set<DexProgramClass>> getTypesUse() {
+        return typesUse;
+      }
+
+      @Override
+      public Map<DexProto, Set<DexProgramClass>> getProtosUse() {
+        return protosUse;
+      }
+
+      @Override
+      public Map<DexField, Set<DexProgramClass>> getFieldsUse() {
+        return fieldsUse;
+      }
+
+      @Override
+      public Map<DexMethod, Set<DexProgramClass>> getMethodsUse() {
+        return methodsUse;
+      }
+
+      @Override
+      public Map<DexCallSite, Set<DexProgramClass>> getCallSitesUse() {
+        return callSitesUse;
+      }
+
+      @Override
+      public Map<DexMethodHandle, Set<DexProgramClass>> getMethodHandlesUse() {
+        return methodHandlessUse;
+      }
+
+      @Override
+      public void clear() {
+        classes.clear();
+        stringsUse.clear();
+        typesUse.clear();
+        protosUse.clear();
+        fieldsUse.clear();
+        methodsUse.clear();
+        callSitesUse.clear();
+        methodHandlessUse.clear();
+      }
+    }
+
     private final AppView<?> appView;
     private final VirtualFileIndexedItemCollection base;
     private final LensCodeRewriterUtils rewriter;
@@ -629,10 +940,16 @@
     private final Set<DexCallSite> callSites = new LinkedHashSet<>();
     private final Set<DexMethodHandle> methodHandles = new LinkedHashSet<>();
 
+    private final ClassUseCollector indexedItemsReferencedFromClassesInTransaction;
+
     private IndexedItemTransaction(VirtualFileIndexedItemCollection base, AppView<?> appView) {
       this.appView = appView;
       this.base = base;
       this.rewriter = new LensCodeRewriterUtils(appView, true);
+      this.indexedItemsReferencedFromClassesInTransaction =
+          appView.options().testing.calculateItemUseCountInDex
+              ? new IndexedItemsUsedByClassesInTransaction(appView, rewriter, base, this)
+              : new EmptyIndexedItemUsedByClasses();
     }
 
     private NamingLens getNamingLens() {
@@ -640,55 +957,82 @@
     }
 
     private <T extends DexItem> boolean maybeInsert(T item, Set<T> set, Set<T> baseSet) {
-      if (baseSet.contains(item) || set.contains(item)) {
+      return maybeInsert(item, set, baseSet, true);
+    }
+
+    private <T extends DexItem> boolean maybeInsert(
+        T item, Set<T> set, Set<T> baseSet, boolean requireCurrentClass) {
+      if (baseSet.contains(item)) {
         return false;
       }
-      set.add(item);
-      return true;
+      boolean added = set.add(item);
+      assert !added || !requireCurrentClass || classes.contains(currentClass);
+      return added;
     }
 
     void addClassAndDependencies(DexProgramClass clazz) {
       clazz.collectIndexedItems(appView, this, rewriter);
+      addClassDone();
+      indexedItemsReferencedFromClassesInTransaction.collectClassDependencies(clazz);
+      indexedItemsReferencedFromClassesInTransaction.collectClassDependenciesDone();
     }
 
+    DexProgramClass currentClass = null;
+
     @Override
     public boolean addClass(DexProgramClass dexProgramClass) {
+      assert currentClass == null;
+      currentClass = dexProgramClass;
       return maybeInsert(dexProgramClass, classes, base.classes);
     }
 
+    public void addClassDone() {
+      currentClass = null;
+    }
+
     @Override
     public boolean addField(DexField field) {
+      assert currentClass != null;
       return maybeInsert(field, fields, base.fields);
     }
 
     @Override
     public boolean addMethod(DexMethod method) {
+      assert currentClass != null;
       return maybeInsert(method, methods, base.methods);
     }
 
     @Override
     public boolean addString(DexString string) {
-      return maybeInsert(string, strings, base.strings);
+      if (currentClass == null) {
+        // Only marker strings can be added outside a class context.
+        assert string.startsWith("~~");
+      }
+      return maybeInsert(string, strings, base.strings, false);
     }
 
     @Override
     public boolean addProto(DexProto proto) {
+      assert currentClass != null;
       return maybeInsert(proto, protos, base.protos);
     }
 
     @Override
     public boolean addType(DexType type) {
+      assert currentClass != null;
       assert SyntheticNaming.verifyNotInternalSynthetic(type);
       return maybeInsert(type, types, base.types);
     }
 
     @Override
     public boolean addCallSite(DexCallSite callSite) {
+      assert currentClass != null;
       return maybeInsert(callSite, callSites, base.callSites);
     }
 
     @Override
     public boolean addMethodHandle(DexMethodHandle methodHandle) {
+      assert currentClass != null;
       return maybeInsert(methodHandle, methodHandles, base.methodHandles);
     }
 
@@ -721,6 +1065,40 @@
       commitItemsIn(strings, base::addString);
       commitItemsIn(callSites, base::addCallSite);
       commitItemsIn(methodHandles, base::addMethodHandle);
+
+      if (appView.options().testing.calculateItemUseCountInDex) {
+        transferUsedBy(
+            indexedItemsReferencedFromClassesInTransaction.getStringsUse(), base.stringsUse);
+        transferUsedBy(indexedItemsReferencedFromClassesInTransaction.getTypesUse(), base.typesUse);
+        transferUsedBy(
+            indexedItemsReferencedFromClassesInTransaction.getProtosUse(), base.protosUse);
+        transferUsedBy(
+            indexedItemsReferencedFromClassesInTransaction.getFieldsUse(), base.fieldsUse);
+        transferUsedBy(
+            indexedItemsReferencedFromClassesInTransaction.getMethodsUse(), base.methodsUse);
+        transferUsedBy(
+            indexedItemsReferencedFromClassesInTransaction.getCallSitesUse(), base.callSitesUse);
+        transferUsedBy(
+            indexedItemsReferencedFromClassesInTransaction.getMethodHandlesUse(),
+            base.methodHandlesUse);
+      }
+    }
+
+    private <T extends DexItem> void transferUsedBy(
+        Map<T, Set<DexProgramClass>> classesInTransactionReferringToItem,
+        Map<T, ItemUseInfo> itemUse) {
+      assert appView.options().testing.calculateItemUseCountInDex;
+      classesInTransactionReferringToItem.forEach(
+          (item, classes) -> {
+            ItemUseInfo currentItemUse = itemUse.get(item);
+            if (currentItemUse == null) {
+              itemUse.put(item, new ItemUseInfo(classes));
+            } else {
+              currentItemUse.addUse(classes);
+            }
+          });
+
+      classesInTransactionReferringToItem.clear();
     }
 
     void abort() {
@@ -730,6 +1108,8 @@
       protos.clear();
       types.clear();
       strings.clear();
+
+      indexedItemsReferencedFromClassesInTransaction.clear();
     }
 
     public boolean isEmpty() {
@@ -883,12 +1263,13 @@
 
       public static PackageSplitClassPartioning create(
           Collection<DexProgramClass> classes,
-          AppView<?> appView,
-          Map<DexProgramClass, String> originalNames) {
+          Map<DexProgramClass, String> originalNames,
+          StartupOrder startupOrder,
+          SyntheticItems syntheticItems) {
         return create(
             classes,
             getClassesByPackageComparator(originalNames),
-            getStartupClassPredicate(appView));
+            getStartupClassPredicate(startupOrder, syntheticItems));
       }
 
       private static PackageSplitClassPartioning create(
@@ -938,12 +1319,8 @@
         };
       }
 
-      private static Predicate<DexProgramClass> getStartupClassPredicate(AppView<?> appView) {
-        if (!appView.hasClassHierarchy()) {
-          return alwaysFalse();
-        }
-        StartupOrder startupOrder = appView.appInfoWithClassHierarchy().getStartupOrder();
-        SyntheticItems syntheticItems = appView.getSyntheticItems();
+      private static Predicate<DexProgramClass> getStartupClassPredicate(
+          StartupOrder startupOrder, SyntheticItems syntheticItems) {
         return clazz -> startupOrder.contains(clazz.getType(), syntheticItems);
       }
 
@@ -980,8 +1357,11 @@
         AppView<?> appView,
         Collection<DexProgramClass> classes,
         Map<DexProgramClass, String> originalNames,
+        StartupOrder startupOrder,
         IntBox nextFileId) {
-      this.classPartioning = PackageSplitClassPartioning.create(classes, appView, originalNames);
+      this.classPartioning =
+          PackageSplitClassPartioning.create(
+              classes, originalNames, startupOrder, appView.getSyntheticItems());
       this.originalNames = originalNames;
       this.dexItemFactory = appView.dexItemFactory();
       this.options = appView.options();
@@ -1016,7 +1396,7 @@
         return;
       }
 
-      assert options.getStartupOptions().hasStartupConfiguration();
+      assert options.getStartupOptions().hasStartupProfileProvider();
 
       // In practice, all startup classes should fit in a single dex file, so optimistically try to
       // commit the startup classes using a single transaction.
diff --git a/src/main/java/com/android/tools/r8/errors/UnsupportedDesugaredLibraryConfigurationVersionDiagnostic.java b/src/main/java/com/android/tools/r8/errors/UnsupportedDesugaredLibraryConfigurationVersionDiagnostic.java
new file mode 100644
index 0000000..7e2455a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/errors/UnsupportedDesugaredLibraryConfigurationVersionDiagnostic.java
@@ -0,0 +1,46 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.errors;
+
+import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
+
+@Keep
+public class UnsupportedDesugaredLibraryConfigurationVersionDiagnostic implements Diagnostic {
+
+  private static final String dagRoot = "https://developer.android.com";
+  private static final String versionMatrixUrl =
+      dagRoot + "/studio/build/library-desugaring-versions";
+  private static final String desugaredLibraryUrl = dagRoot + "/studio/build/library-desugaring";
+
+  private final Origin origin;
+
+  public UnsupportedDesugaredLibraryConfigurationVersionDiagnostic(Origin origin) {
+    this.origin = origin;
+  }
+
+  @Override
+  public Origin getOrigin() {
+    return origin;
+  }
+
+  @Override
+  public Position getPosition() {
+    return Position.UNKNOWN;
+  }
+
+  @Override
+  public String getDiagnosticMessage() {
+    return "Unsupported desugared library configuration version, please upgrade the D8/R8"
+        + " compiler."
+        + " See "
+        + versionMatrixUrl
+        + "."
+        + " To learn more about library desugaring read "
+        + desugaredLibraryUrl
+        + ".";
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupClass.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupClass.java
index 7ae4d30..b6c6311 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupClass.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupClass.java
@@ -56,6 +56,17 @@
     return this;
   }
 
+  @Override
+  public void serializeToString(
+      StringBuilder builder,
+      Function<C, String> classSerializer,
+      Function<M, String> methodSerializer) {
+    if (isSynthetic()) {
+      builder.append('S');
+    }
+    builder.append(classSerializer.apply(getReference()));
+  }
+
   public static class Builder<C, M> extends StartupItem.Builder<C, M, Builder<C, M>> {
 
     @Override
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 8d75d7c..ca0b88f 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
@@ -31,7 +31,7 @@
     this.startupOrder =
         appView.hasClassHierarchy()
             ? appView.appInfoWithClassHierarchy().getStartupOrder()
-            : StartupOrder.empty();
+            : StartupOrder.createInitialStartupOrder(appView.options());
   }
 
   /**
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupConfiguration.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupConfiguration.java
deleted file mode 100644
index 2f1a23d..0000000
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupConfiguration.java
+++ /dev/null
@@ -1,125 +0,0 @@
-// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.experimental.startup;
-
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.utils.ExceptionDiagnostic;
-import com.android.tools.r8.utils.FileUtils;
-import com.android.tools.r8.utils.Reporter;
-import com.android.tools.r8.utils.StringDiagnostic;
-import com.google.common.collect.ImmutableList;
-import java.io.IOException;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.function.Consumer;
-
-public class StartupConfiguration {
-
-  private final List<StartupItem<DexType, DexMethod, ?>> startupItems;
-
-  public StartupConfiguration(List<StartupItem<DexType, DexMethod, ?>> startupItems) {
-    this.startupItems = startupItems;
-  }
-
-  public static Builder builder() {
-    return new Builder();
-  }
-
-  /**
-   * Parses the supplied startup configuration, if any. The startup configuration is a list of class
-   * and method descriptors.
-   *
-   * <p>Example:
-   *
-   * <pre>
-   * Landroidx/compose/runtime/ComposerImpl;->updateValue(Ljava/lang/Object;)V
-   * Landroidx/compose/runtime/ComposerImpl;->updatedNodeCount(I)I
-   * Landroidx/compose/runtime/ComposerImpl;->validateNodeExpected()V
-   * Landroidx/compose/runtime/CompositionImpl;->applyChanges()V
-   * Landroidx/compose/runtime/ComposerKt;->findLocation(Ljava/util/List;I)I
-   * Landroidx/compose/runtime/ComposerImpl;
-   * </pre>
-   */
-  public static StartupConfiguration createStartupConfiguration(
-      DexItemFactory dexItemFactory, Reporter reporter) {
-    String propertyValue = System.getProperty("com.android.tools.r8.startup.config");
-    return propertyValue != null
-        ? createStartupConfigurationFromFile(dexItemFactory, reporter, Paths.get(propertyValue))
-        : null;
-  }
-
-  public static StartupConfiguration createStartupConfigurationFromFile(
-      DexItemFactory dexItemFactory, Reporter reporter, Path path) {
-    reporter.warning("Use of startupconfig is experimental");
-
-    List<String> startupDescriptors;
-    try {
-      startupDescriptors = FileUtils.readAllLines(path);
-    } catch (IOException e) {
-      throw reporter.fatalError(new ExceptionDiagnostic(e));
-    }
-
-    if (startupDescriptors.isEmpty()) {
-      return null;
-    }
-
-    return createStartupConfigurationFromLines(dexItemFactory, reporter, startupDescriptors);
-  }
-
-  public static StartupConfiguration createStartupConfigurationFromLines(
-      DexItemFactory dexItemFactory, Reporter reporter, List<String> startupDescriptors) {
-    List<StartupItem<DexType, DexMethod, ?>> startupItems = new ArrayList<>();
-    StartupConfigurationParser.createDexParser(dexItemFactory)
-        .parseLines(
-            startupDescriptors,
-            startupItems::add,
-            startupItems::add,
-            error ->
-                reporter.warning(
-                    new StringDiagnostic(
-                        "Invalid descriptor for startup class or method: " + error)));
-    return new StartupConfiguration(startupItems);
-  }
-
-  public boolean hasStartupItems() {
-    return !startupItems.isEmpty();
-  }
-
-  public List<StartupItem<DexType, DexMethod, ?>> getStartupItems() {
-    return startupItems;
-  }
-
-  public static class Builder {
-
-    private final ImmutableList.Builder<StartupItem<DexType, DexMethod, ?>> startupItemsBuilder =
-        ImmutableList.builder();
-
-    public Builder addStartupItem(StartupItem<DexType, DexMethod, ?> startupItem) {
-      this.startupItemsBuilder.add(startupItem);
-      return this;
-    }
-
-    public Builder addStartupClass(StartupClass<DexType, DexMethod> startupClass) {
-      return addStartupItem(startupClass);
-    }
-
-    public Builder addStartupMethod(StartupMethod<DexType, DexMethod> startupMethod) {
-      return addStartupItem(startupMethod);
-    }
-
-    public Builder apply(Consumer<Builder> consumer) {
-      consumer.accept(this);
-      return this;
-    }
-
-    public StartupConfiguration build() {
-      return new StartupConfiguration(startupItemsBuilder.build());
-    }
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupItem.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupItem.java
index 74e69e2..e2ea09b 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupItem.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupItem.java
@@ -64,6 +64,11 @@
     return (flags & FLAG_SYNTHETIC) != 0;
   }
 
+  public abstract void serializeToString(
+      StringBuilder builder,
+      Function<C, String> classSerializer,
+      Function<M, String> methodSerializer);
+
   @Override
   public boolean equals(Object obj) {
     if (this == obj) {
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupMethod.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupMethod.java
index 871e179..00ddca9 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupMethod.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupMethod.java
@@ -43,6 +43,17 @@
     return this;
   }
 
+  @Override
+  public void serializeToString(
+      StringBuilder builder,
+      Function<C, String> classSerializer,
+      Function<M, String> methodSerializer) {
+    if (isSynthetic()) {
+      builder.append('S');
+    }
+    builder.append(methodSerializer.apply(getReference()));
+  }
+
   public static class Builder<C, M> extends StartupItem.Builder<C, M, Builder<C, M>> {
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupOptions.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupOptions.java
index e737c9d..7d1b4dc 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupOptions.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupOptions.java
@@ -4,9 +4,13 @@
 
 package com.android.tools.r8.experimental.startup;
 
-import static com.android.tools.r8.utils.SystemPropertyUtils.getSystemPropertyForDevelopment;
 import static com.android.tools.r8.utils.SystemPropertyUtils.parseSystemPropertyForDevelopmentOrDefault;
 
+import com.android.tools.r8.StartupProfileProvider;
+import com.android.tools.r8.StringResource;
+import com.android.tools.r8.utils.SystemPropertyUtils;
+import java.nio.file.Paths;
+
 public class StartupOptions {
 
   /**
@@ -15,7 +19,7 @@
    */
   private boolean enableMinimalStartupDex =
       parseSystemPropertyForDevelopmentOrDefault(
-          "com.android.tools.r8.startup.minimalstartupdex", false);
+          "com.android.tools.r8.startup.minimalstartupdex", true);
 
   /**
    * When enabled, optimizations crossing the startup/non-startup boundary will be allowed.
@@ -39,75 +43,18 @@
           "com.android.tools.r8.startup.completenesscheck", false);
 
   /**
-   * When enabled, each method will be instrumented to notify the startup InstrumentationServer that
-   * it has been executed.
-   *
-   * <p>This will also inject the startup runtime library (i.e., the InstrumentationServer) into the
-   * app.
-   */
-  private boolean enableStartupInstrumentation =
-      parseSystemPropertyForDevelopmentOrDefault("com.android.tools.r8.startup.instrument", false);
-
-  /**
    * When enabled, the layout of the primary dex file will be generated using the startup list,
    * using {@link com.android.tools.r8.dex.StartupMixedSectionLayoutStrategy}.
    */
   private boolean enableStartupLayoutOptimizations =
       parseSystemPropertyForDevelopmentOrDefault("com.android.tools.r8.startup.layout", true);
 
-  /**
-   * Specifies the synthetic context of the startup runtime library. When this is set, the startup
-   * runtime library will only be injected into the app when the synthetic context is in the
-   * program. This can be used to avoid that the startup runtime library is injected multiple times
-   * in presence of separate compilation.
-   *
-   * <p>Example synthetic context: "app.tivi.home.MainActivity".
-   *
-   * <p>Note that this is only meaningful when {@link #enableStartupInstrumentation} is set to true.
-   */
-  private String startupInstrumentationServerSyntheticContext =
-      getSystemPropertyForDevelopment(
-          "com.android.tools.r8.startup.instrumentationserversyntheticcontext");
-
-  /**
-   * Specifies the logcat tag that should be used by the InstrumentationServer when logging events.
-   *
-   * <p>When a logcat tag is not specified, the InstrumentationServer will not print events to
-   * logcat. Instead, the startup events must be obtained by requesting the InstrumentationServer to
-   * write the events to a file.
-   */
-  private String startupInstrumentationTag =
-      getSystemPropertyForDevelopment("com.android.tools.r8.startup.instrumentationtag");
-
-  private StartupConfiguration startupConfiguration;
-
-  public boolean hasStartupInstrumentationServerSyntheticContext() {
-    return startupInstrumentationServerSyntheticContext != null;
-  }
-
-  public String getStartupInstrumentationServerSyntheticContext() {
-    return startupInstrumentationServerSyntheticContext;
-  }
-
-  public StartupOptions setStartupInstrumentationServerSyntheticContext(
-      String startupInstrumentationServerSyntheticContext) {
-    this.startupInstrumentationServerSyntheticContext =
-        startupInstrumentationServerSyntheticContext;
-    return this;
-  }
-
-  public boolean hasStartupInstrumentationTag() {
-    return startupInstrumentationTag != null;
-  }
-
-  public String getStartupInstrumentationTag() {
-    return startupInstrumentationTag;
-  }
-
-  public StartupOptions setStartupInstrumentationTag(String startupInstrumentationTag) {
-    this.startupInstrumentationTag = startupInstrumentationTag;
-    return this;
-  }
+  private StartupProfileProvider startupProfileProvider =
+      SystemPropertyUtils.applySystemProperty(
+          "com.android.tools.r8.startup.profile",
+          propertyValue ->
+              StringResource.fromFile(Paths.get(propertyValue))::getStringWithRuntimeException,
+          () -> null);
 
   public boolean isMinimalStartupDexEnabled() {
     return enableMinimalStartupDex;
@@ -122,12 +69,9 @@
     return enableStartupBoundaryOptimizations;
   }
 
-  public boolean isStartupInstrumentationEnabled() {
-    return enableStartupInstrumentation;
-  }
-
-  public StartupOptions setEnableStartupInstrumentation() {
-    enableStartupInstrumentation = true;
+  public StartupOptions setEnableStartupBoundaryOptimizations(
+      boolean enableStartupBoundaryOptimizations) {
+    this.enableStartupBoundaryOptimizations = enableStartupBoundaryOptimizations;
     return this;
   }
 
@@ -149,16 +93,16 @@
     return this;
   }
 
-  public boolean hasStartupConfiguration() {
-    return startupConfiguration != null;
+  public boolean hasStartupProfileProvider() {
+    return startupProfileProvider != null;
   }
 
-  public StartupConfiguration getStartupConfiguration() {
-    return startupConfiguration;
+  public StartupProfileProvider getStartupProfileProvider() {
+    return startupProfileProvider;
   }
 
-  public StartupOptions setStartupConfiguration(StartupConfiguration startupConfiguration) {
-    this.startupConfiguration = startupConfiguration;
+  public StartupOptions setStartupProfileProvider(StartupProfileProvider startupProfileProvider) {
+    this.startupProfileProvider = startupProfileProvider;
     return this;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupOrder.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupOrder.java
index b1803eb..9f9bf65 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupOrder.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupOrder.java
@@ -19,15 +19,11 @@
   StartupOrder() {}
 
   public static StartupOrder createInitialStartupOrder(InternalOptions options) {
-    if (!options.getStartupOptions().hasStartupConfiguration()) {
+    StartupProfile startupProfile = StartupProfile.parseStartupProfile(options);
+    if (startupProfile == null || startupProfile.getStartupItems().isEmpty()) {
       return empty();
     }
-    StartupConfiguration startupConfiguration =
-        options.getStartupOptions().getStartupConfiguration();
-    if (!startupConfiguration.hasStartupItems()) {
-      return empty();
-    }
-    return new NonEmptyStartupOrder(new LinkedHashSet<>(startupConfiguration.getStartupItems()));
+    return new NonEmptyStartupOrder(new LinkedHashSet<>(startupProfile.getStartupItems()));
   }
 
   public static StartupOrder empty() {
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
new file mode 100644
index 0000000..20cd31b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupProfile.java
@@ -0,0 +1,109 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.experimental.startup;
+
+import com.android.tools.r8.StartupProfileProvider;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.StringDiagnostic;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+public class StartupProfile {
+
+  private final List<StartupItem<DexType, DexMethod, ?>> startupItems;
+
+  public StartupProfile(List<StartupItem<DexType, DexMethod, ?>> startupItems) {
+    this.startupItems = startupItems;
+  }
+
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  /**
+   * Parses the supplied startup configuration, if any. The startup configuration is a list of class
+   * and method descriptors.
+   *
+   * <p>Example:
+   *
+   * <pre>
+   * Landroidx/compose/runtime/ComposerImpl;->updateValue(Ljava/lang/Object;)V
+   * Landroidx/compose/runtime/ComposerImpl;->updatedNodeCount(I)I
+   * Landroidx/compose/runtime/ComposerImpl;->validateNodeExpected()V
+   * Landroidx/compose/runtime/CompositionImpl;->applyChanges()V
+   * Landroidx/compose/runtime/ComposerKt;->findLocation(Ljava/util/List;I)I
+   * Landroidx/compose/runtime/ComposerImpl;
+   * </pre>
+   */
+  public static StartupProfile parseStartupProfile(InternalOptions options) {
+    if (!options.getStartupOptions().hasStartupProfileProvider()) {
+      return null;
+    }
+    StartupProfileProvider resource = options.getStartupOptions().getStartupProfileProvider();
+    List<String> startupDescriptors = StringUtils.splitLines(resource.get());
+    return createStartupConfigurationFromLines(options, startupDescriptors);
+  }
+
+  public static StartupProfile createStartupConfigurationFromLines(
+      InternalOptions options, List<String> startupDescriptors) {
+    List<StartupItem<DexType, DexMethod, ?>> startupItems = new ArrayList<>();
+    StartupConfigurationParser.createDexParser(options.dexItemFactory())
+        .parseLines(
+            startupDescriptors,
+            startupItems::add,
+            startupItems::add,
+            error ->
+                options.reporter.warning(
+                    new StringDiagnostic(
+                        "Invalid descriptor for startup class or method: " + error)));
+    return new StartupProfile(startupItems);
+  }
+
+  public List<StartupItem<DexType, DexMethod, ?>> getStartupItems() {
+    return startupItems;
+  }
+
+  public String serializeToString() {
+    StringBuilder builder = new StringBuilder();
+    for (StartupItem<DexType, DexMethod, ?> startupItem : startupItems) {
+      startupItem.serializeToString(builder, DexType::toSmaliString, DexMethod::toSmaliString);
+      builder.append('\n');
+    }
+    return builder.toString();
+  }
+
+  public static class Builder {
+
+    private final ImmutableList.Builder<StartupItem<DexType, DexMethod, ?>> startupItemsBuilder =
+        ImmutableList.builder();
+
+    public Builder addStartupItem(StartupItem<DexType, DexMethod, ?> startupItem) {
+      this.startupItemsBuilder.add(startupItem);
+      return this;
+    }
+
+    public Builder addStartupClass(StartupClass<DexType, DexMethod> startupClass) {
+      return addStartupItem(startupClass);
+    }
+
+    public Builder addStartupMethod(StartupMethod<DexType, DexMethod> startupMethod) {
+      return addStartupItem(startupMethod);
+    }
+
+    public Builder apply(Consumer<Builder> consumer) {
+      consumer.accept(this);
+      return this;
+    }
+
+    public StartupProfile build() {
+      return new StartupProfile(startupItemsBuilder.build());
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupInstrumentation.java b/src/main/java/com/android/tools/r8/experimental/startup/instrumentation/StartupInstrumentation.java
similarity index 87%
rename from src/main/java/com/android/tools/r8/experimental/startup/StartupInstrumentation.java
rename to src/main/java/com/android/tools/r8/experimental/startup/instrumentation/StartupInstrumentation.java
index 76abc9d..7e8db58 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupInstrumentation.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/instrumentation/StartupInstrumentation.java
@@ -2,7 +2,7 @@
 // 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.experimental.startup;
+package com.android.tools.r8.experimental.startup.instrumentation;
 
 import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
 import static com.android.tools.r8.utils.PredicateUtils.not;
@@ -54,21 +54,21 @@
   private final IRConverter converter;
   private final DexItemFactory dexItemFactory;
   private final InternalOptions options;
-  private final StartupReferences references;
-  private final StartupOptions startupOptions;
+  private final StartupInstrumentationReferences references;
+  private final StartupInstrumentationOptions startupInstrumentationOptions;
 
   private StartupInstrumentation(AppView<AppInfo> appView) {
     this.appView = appView;
     this.converter = new IRConverter(appView, Timing.empty());
     this.dexItemFactory = appView.dexItemFactory();
     this.options = appView.options();
-    this.references = new StartupReferences(dexItemFactory);
-    this.startupOptions = options.getStartupOptions();
+    this.references = new StartupInstrumentationReferences(dexItemFactory);
+    this.startupInstrumentationOptions = options.getStartupInstrumentationOptions();
   }
 
   public static void run(AppView<AppInfo> appView, ExecutorService executorService)
       throws ExecutionException {
-    if (appView.options().getStartupOptions().isStartupInstrumentationEnabled()) {
+    if (appView.options().getStartupInstrumentationOptions().isStartupInstrumentationEnabled()) {
       StartupInstrumentation startupInstrumentation = new StartupInstrumentation(appView);
       startupInstrumentation.instrumentAllClasses(executorService);
       startupInstrumentation.injectStartupRuntimeLibrary(executorService);
@@ -89,11 +89,11 @@
     // If the startup options has a synthetic context for the startup instrumentation server, then
     // only inject the runtime library if the synthetic context exists in program to avoid injecting
     // the runtime library multiple times when there is separate compilation.
-    if (startupOptions.hasStartupInstrumentationServerSyntheticContext()) {
+    if (startupInstrumentationOptions.hasStartupInstrumentationServerSyntheticContext()) {
       DexType syntheticContext =
           dexItemFactory.createType(
               DescriptorUtils.javaTypeToDescriptor(
-                  startupOptions.getStartupInstrumentationServerSyntheticContext()));
+                  startupInstrumentationOptions.getStartupInstrumentationServerSyntheticContext()));
       if (asProgramClassOrNull(appView.definitionFor(syntheticContext)) == null) {
         return;
       }
@@ -113,7 +113,7 @@
   private List<DexProgramClass> createStartupRuntimeLibraryClasses() {
     DexProgramClass instrumentationServerImplClass =
         InstrumentationServerImplFactory.createClass(dexItemFactory);
-    if (startupOptions.hasStartupInstrumentationTag()) {
+    if (startupInstrumentationOptions.hasStartupInstrumentationTag()) {
       instrumentationServerImplClass
           .lookupUniqueStaticFieldWithName(dexItemFactory.createString("writeToLogcat"))
           .setStaticValue(DexValueBoolean.create(true));
@@ -121,7 +121,8 @@
           .lookupUniqueStaticFieldWithName(dexItemFactory.createString("logcatTag"))
           .setStaticValue(
               new DexValueString(
-                  dexItemFactory.createString(startupOptions.getStartupInstrumentationTag())));
+                  dexItemFactory.createString(
+                      startupInstrumentationOptions.getStartupInstrumentationTag())));
     }
 
     return ImmutableList.of(
@@ -174,8 +175,11 @@
 
     // Insert invoke to record that the enclosing class is a startup class.
     SyntheticItems syntheticItems = appView.getSyntheticItems();
-    boolean isSyntheticClass = syntheticItems.isSyntheticClass(method.getHolder());
-    if (method.getDefinition().isClassInitializer() && !isSyntheticClass) {
+    boolean generalizeSyntheticToSyntheticOfSyntheticContexts =
+        startupInstrumentationOptions.isGeneralizationOfSyntheticsToSyntheticContextEnabled()
+            && syntheticItems.isSyntheticClass(method.getHolder());
+    if (method.getDefinition().isClassInitializer()
+        && !generalizeSyntheticToSyntheticOfSyntheticContexts) {
       DexMethod methodToInvoke = references.addNonSyntheticMethod;
       DexType classToPrint = method.getHolderType();
       Value descriptorValue =
@@ -193,7 +197,7 @@
     if (!skipMethodLogging) {
       DexMethod methodToInvoke;
       DexReference referenceToPrint;
-      if (isSyntheticClass) {
+      if (generalizeSyntheticToSyntheticOfSyntheticContexts) {
         Collection<DexType> synthesizingContexts =
             syntheticItems.getSynthesizingContextTypes(method.getHolderType());
         assert synthesizingContexts.size() == 1;
@@ -216,6 +220,8 @@
               .build());
     }
 
+    converter.deadCodeRemover.run(code, Timing.empty());
+
     DexCode instrumentedCode =
         new IRToDexFinalizer(appView, converter.deadCodeRemover)
             .finalizeCode(code, BytecodeMetadataProvider.empty(), Timing.empty());
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/instrumentation/StartupInstrumentationOptions.java b/src/main/java/com/android/tools/r8/experimental/startup/instrumentation/StartupInstrumentationOptions.java
new file mode 100644
index 0000000..bfb3f87
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/experimental/startup/instrumentation/StartupInstrumentationOptions.java
@@ -0,0 +1,104 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.experimental.startup.instrumentation;
+
+import static com.android.tools.r8.utils.SystemPropertyUtils.getSystemPropertyForDevelopment;
+import static com.android.tools.r8.utils.SystemPropertyUtils.parseSystemPropertyForDevelopmentOrDefault;
+
+public class StartupInstrumentationOptions {
+
+  /**
+   * When enabled, the instrumentation for synthetics will print the name of the synthetic context
+   * instead of printing the name of the synthetic itself.
+   */
+  private boolean enableGeneralizationOfSyntheticsToSyntheticContext =
+      parseSystemPropertyForDevelopmentOrDefault(
+          "com.android.tools.r8.startup.instrumentation.generalizesynthetics", false);
+
+  /**
+   * When enabled, each method will be instrumented to notify the startup InstrumentationServer that
+   * it has been executed.
+   *
+   * <p>This will also inject the startup runtime library (i.e., the InstrumentationServer) into the
+   * app.
+   */
+  private boolean enableStartupInstrumentation =
+      parseSystemPropertyForDevelopmentOrDefault(
+          "com.android.tools.r8.startup.instrumentation.instrument", false);
+
+  /**
+   * Specifies the synthetic context of the startup runtime library. When this is set, the startup
+   * runtime library will only be injected into the app when the synthetic context is in the
+   * program. This can be used to avoid that the startup runtime library is injected multiple times
+   * in presence of separate compilation.
+   *
+   * <p>Example synthetic context: "app.tivi.home.MainActivity".
+   *
+   * <p>Note that this is only meaningful when {@link #enableStartupInstrumentation} is set to true.
+   */
+  private String startupInstrumentationServerSyntheticContext =
+      getSystemPropertyForDevelopment(
+          "com.android.tools.r8.startup.instrumentation.instrumentationserversyntheticcontext");
+
+  /**
+   * Specifies the logcat tag that should be used by the InstrumentationServer when logging events.
+   *
+   * <p>When a logcat tag is not specified, the InstrumentationServer will not print events to
+   * logcat. Instead, the startup events must be obtained by requesting the InstrumentationServer to
+   * write the events to a file.
+   */
+  private String startupInstrumentationTag =
+      getSystemPropertyForDevelopment(
+          "com.android.tools.r8.startup.instrumentation.instrumentationtag");
+
+  public boolean hasStartupInstrumentationServerSyntheticContext() {
+    return startupInstrumentationServerSyntheticContext != null;
+  }
+
+  public String getStartupInstrumentationServerSyntheticContext() {
+    return startupInstrumentationServerSyntheticContext;
+  }
+
+  public StartupInstrumentationOptions setStartupInstrumentationServerSyntheticContext(
+      String startupInstrumentationServerSyntheticContext) {
+    this.startupInstrumentationServerSyntheticContext =
+        startupInstrumentationServerSyntheticContext;
+    return this;
+  }
+
+  public boolean hasStartupInstrumentationTag() {
+    return startupInstrumentationTag != null;
+  }
+
+  public String getStartupInstrumentationTag() {
+    return startupInstrumentationTag;
+  }
+
+  public StartupInstrumentationOptions setStartupInstrumentationTag(
+      String startupInstrumentationTag) {
+    this.startupInstrumentationTag = startupInstrumentationTag;
+    return this;
+  }
+
+  public boolean isGeneralizationOfSyntheticsToSyntheticContextEnabled() {
+    return enableGeneralizationOfSyntheticsToSyntheticContext;
+  }
+
+  public StartupInstrumentationOptions setEnableGeneralizationOfSyntheticsToSyntheticContext(
+      boolean enableGeneralizationOfSyntheticsToSyntheticContext) {
+    this.enableGeneralizationOfSyntheticsToSyntheticContext =
+        enableGeneralizationOfSyntheticsToSyntheticContext;
+    return this;
+  }
+
+  public boolean isStartupInstrumentationEnabled() {
+    return enableStartupInstrumentation;
+  }
+
+  public StartupInstrumentationOptions setEnableStartupInstrumentation() {
+    enableStartupInstrumentation = true;
+    return this;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupReferences.java b/src/main/java/com/android/tools/r8/experimental/startup/instrumentation/StartupInstrumentationReferences.java
similarity index 87%
rename from src/main/java/com/android/tools/r8/experimental/startup/StartupReferences.java
rename to src/main/java/com/android/tools/r8/experimental/startup/instrumentation/StartupInstrumentationReferences.java
index a041f1e..fbfa5e2 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupReferences.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/instrumentation/StartupInstrumentationReferences.java
@@ -2,20 +2,20 @@
 // 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.experimental.startup;
+package com.android.tools.r8.experimental.startup.instrumentation;
 
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
 
-public class StartupReferences {
+class StartupInstrumentationReferences {
 
   final DexType instrumentationServerType;
   final DexType instrumentationServerImplType;
   final DexMethod addNonSyntheticMethod;
   final DexMethod addSyntheticMethod;
 
-  StartupReferences(DexItemFactory dexItemFactory) {
+  StartupInstrumentationReferences(DexItemFactory dexItemFactory) {
     instrumentationServerType =
         dexItemFactory.createType("Lcom/android/tools/r8/startup/InstrumentationServer;");
     instrumentationServerImplType =
diff --git a/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java b/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java
index 5b8e3db..68af78a 100644
--- a/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java
+++ b/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java
@@ -164,7 +164,6 @@
     FeatureSplit feature;
     boolean isSynthetic = syntheticItems.isSyntheticClass(type);
     if (isSynthetic) {
-      assert !classToFeatureSplitMap.containsKey(type);
       if (syntheticItems.isSyntheticOfKind(type, k -> k.ENUM_UNBOXING_SHARED_UTILITY_CLASS)) {
         // Use the startup base if there is one, such that we don't merge non-startup classes with
         // the shared utility class in case it is used during startup. The use of base startup
@@ -176,6 +175,9 @@
             : FeatureSplit.BASE_STARTUP;
       }
       feature = syntheticItems.getContextualFeatureSplitOrDefault(type, FeatureSplit.BASE);
+      // Verify the synthetic is not in the class to feature split map or the synthetic has the same
+      // feature split as its context.
+      assert classToFeatureSplitMap.getOrDefault(type, feature) == feature;
     } else {
       feature = classToFeatureSplitMap.getOrDefault(type, FeatureSplit.BASE);
     }
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
index 1aa41dc..f6edecd 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
@@ -221,10 +221,6 @@
     return accessFlags.isProtected();
   }
 
-  public boolean isPublic() {
-    return accessFlags.isPublic();
-  }
-
   @Override
   public boolean isStaticMember() {
     return isStatic();
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMember.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMember.java
index 7f42e49..806b7a7 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMember.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMember.java
@@ -73,6 +73,10 @@
     return getAccessFlags().isPrivate();
   }
 
+  public final boolean isPublic() {
+    return getAccessFlags().isPublic();
+  }
+
   public abstract ProgramMember<D, R> asProgramMember(DexDefinitionSupplier definitions);
 
   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 ce22d5c..05b5030 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -189,6 +189,10 @@
     return accessFlags;
   }
 
+  public int getArgumentIndexFromParameterIndex(int parameterIndex) {
+    return parameterIndex + getFirstNonReceiverArgumentIndex();
+  }
+
   public DexType getArgumentType(int argumentIndex) {
     return getReference().getArgumentType(argumentIndex, isStatic());
   }
@@ -324,6 +328,11 @@
     return getReference().getParameters();
   }
 
+  public int getParameterIndexFromArgumentIndex(int argumentIndex) {
+    assert argumentIndex >= getFirstNonReceiverArgumentIndex();
+    return argumentIndex - getFirstNonReceiverArgumentIndex();
+  }
+
   public DexType getReturnType() {
     return getReference().getReturnType();
   }
@@ -434,10 +443,6 @@
     return accessFlags.isNative();
   }
 
-  public boolean isPublic() {
-    return accessFlags.isPublic();
-  }
-
   public boolean isSynchronized() {
     return accessFlags.isSynchronized();
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexTypeList.java b/src/main/java/com/android/tools/r8/graph/DexTypeList.java
index 880b51d..7be2dce 100644
--- a/src/main/java/com/android/tools/r8/graph/DexTypeList.java
+++ b/src/main/java/com/android/tools/r8/graph/DexTypeList.java
@@ -8,6 +8,7 @@
 import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.dex.MixedSectionCollection;
 import com.android.tools.r8.utils.ArrayUtils;
+import com.android.tools.r8.utils.IntObjConsumer;
 import com.android.tools.r8.utils.structural.StructuralItem;
 import com.android.tools.r8.utils.structural.StructuralMapping;
 import com.android.tools.r8.utils.structural.StructuralSpecification;
@@ -104,6 +105,13 @@
     }
   }
 
+  public void forEach(IntObjConsumer<DexType> consumer) {
+    for (int parameterIndex = 0; parameterIndex < values.length; parameterIndex++) {
+      DexType parameter = values[parameterIndex];
+      consumer.accept(parameterIndex, parameter);
+    }
+  }
+
   public void forEachReverse(Consumer<? super DexType> consumer) {
     for (int i = values.length - 1; i >= 0; i--) {
       consumer.accept(values[i]);
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java
index 70f752e..21fccf4 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java
@@ -51,6 +51,7 @@
 import com.android.tools.r8.horizontalclassmerging.policies.PreventClassMethodAndDefaultMethodCollisions;
 import com.android.tools.r8.horizontalclassmerging.policies.RespectPackageBoundaries;
 import com.android.tools.r8.horizontalclassmerging.policies.SameFeatureSplit;
+import com.android.tools.r8.horizontalclassmerging.policies.SameFilePolicy;
 import com.android.tools.r8.horizontalclassmerging.policies.SameInstanceFields;
 import com.android.tools.r8.horizontalclassmerging.policies.SameMainDexGroup;
 import com.android.tools.r8.horizontalclassmerging.policies.SameNestHost;
@@ -273,6 +274,7 @@
     ImmediateProgramSubtypingInfo immediateSubtypingInfo =
         ImmediateProgramSubtypingInfo.create(appView);
     builder.add(
+        new SameFilePolicy(appView),
         new CheckAbstractClasses(appView),
         new NoClassAnnotationCollisions(),
         new SameFeatureSplit(appView),
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameFilePolicy.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameFilePolicy.java
new file mode 100644
index 0000000..5643d8c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameFilePolicy.java
@@ -0,0 +1,34 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.horizontalclassmerging.policies;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.horizontalclassmerging.MultiClassSameReferencePolicy;
+import com.android.tools.r8.utils.InternalOptions.HorizontalClassMergerOptions;
+
+public class SameFilePolicy extends MultiClassSameReferencePolicy<String> {
+
+  private final HorizontalClassMergerOptions options;
+
+  public SameFilePolicy(AppView<?> appView) {
+    this.options = appView.options().horizontalClassMergerOptions();
+  }
+
+  @Override
+  public String getMergeKey(DexProgramClass clazz) {
+    return clazz.getType().toDescriptorString().replaceAll("^([^$]+)\\$.*", "$1");
+  }
+
+  @Override
+  public String getName() {
+    return "SameFilePolicy";
+  }
+
+  @Override
+  public boolean shouldSkipPolicy() {
+    return !options.isSameFilePolicyEnabled();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java
index 6101905..26993a6 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java
@@ -479,14 +479,17 @@
     if (s1.isEmpty() || s2.isEmpty()) {
       return InterfaceCollection.empty();
     }
-    InterfaceCollection cached =
-        appView.dexItemFactory().leastUpperBoundOfInterfacesTable.get(s1, s2);
-    if (cached != null) {
-      return cached;
-    }
-    cached = appView.dexItemFactory().leastUpperBoundOfInterfacesTable.get(s2, s1);
-    if (cached != null) {
-      return cached;
+    // Synchronization is required, see b/242286733.
+    synchronized (appView.dexItemFactory().leastUpperBoundOfInterfacesTable) {
+      InterfaceCollection cached =
+          appView.dexItemFactory().leastUpperBoundOfInterfacesTable.get(s1, s2);
+      if (cached != null) {
+        return cached;
+      }
+      cached = appView.dexItemFactory().leastUpperBoundOfInterfacesTable.get(s2, s1);
+      if (cached != null) {
+        return cached;
+      }
     }
     Map<DexType, InterfaceMarker> seen = new IdentityHashMap<>();
     Queue<InterfaceWithMarker> worklist = new ArrayDeque<>();
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstantValueUtils.java b/src/main/java/com/android/tools/r8/ir/code/ConstantValueUtils.java
index 86d04e5..9855ee6 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstantValueUtils.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstantValueUtils.java
@@ -13,11 +13,12 @@
 
   /**
    * If the given value is a constant class, then returns the corresponding {@link DexType}.
-   * Otherwise returns null.
+   * Otherwise returns null. This should only be used for tracing.
    */
-  public static DexType getDexTypeRepresentedByValue(
+  public static DexType getDexTypeRepresentedByValueForTracing(
       Value value, DexDefinitionSupplier definitions) {
-    Value alias = value.getAliasedValue();
+    Value alias =
+        value.getAliasedValue(IgnoreDebugLocalWriteAliasedValueConfiguration.getInstance());
     if (alias.isPhi()) {
       return null;
     }
diff --git a/src/main/java/com/android/tools/r8/ir/code/DebugLocalWrite.java b/src/main/java/com/android/tools/r8/ir/code/DebugLocalWrite.java
index ad9be48..b17536e 100644
--- a/src/main/java/com/android/tools/r8/ir/code/DebugLocalWrite.java
+++ b/src/main/java/com/android/tools/r8/ir/code/DebugLocalWrite.java
@@ -65,6 +65,10 @@
   public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
     helper.loadInValues(this, it);
     // A local-write does not have an outgoing stack value, but in writes directly to the local.
+    assert !instructionTypeCanThrow();
+    if (getBlock().hasCatchHandlers()) {
+      helper.splitAfterStoredOutValue(it);
+    }
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/IgnoreDebugLocalWriteAliasedValueConfiguration.java b/src/main/java/com/android/tools/r8/ir/code/IgnoreDebugLocalWriteAliasedValueConfiguration.java
new file mode 100644
index 0000000..c9dcfa2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/IgnoreDebugLocalWriteAliasedValueConfiguration.java
@@ -0,0 +1,31 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.code;
+
+public class IgnoreDebugLocalWriteAliasedValueConfiguration implements AliasedValueConfiguration {
+
+  private static final IgnoreDebugLocalWriteAliasedValueConfiguration INSTANCE =
+      new IgnoreDebugLocalWriteAliasedValueConfiguration();
+
+  private IgnoreDebugLocalWriteAliasedValueConfiguration() {}
+
+  public static IgnoreDebugLocalWriteAliasedValueConfiguration getInstance() {
+    return INSTANCE;
+  }
+
+  @Override
+  public boolean isIntroducingAnAlias(Instruction instruction) {
+    return instruction.isAssume() || instruction.isDebugLocalWrite();
+  }
+
+  @Override
+  public Value getAliasForOutValue(Instruction instruction) {
+    assert instruction.isAssume() || instruction.isDebugLocalWrite();
+
+    return instruction.isAssume()
+        ? instruction.asAssume().src()
+        : instruction.asDebugLocalWrite().src();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
index 3dcfa35..224b457 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
@@ -13,6 +13,7 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.ConsumerUtils;
 import com.android.tools.r8.utils.InternalOptions;
 import com.google.common.collect.Sets;
@@ -141,6 +142,10 @@
   boolean removeOrReplaceCurrentInstructionByInitClassIfPossible(
       AppView<?> appView, IRCode code, DexType type, Consumer<InitClass> consumer);
 
+  default void replaceCurrentInstructionWithConstBoolean(IRCode code, boolean value) {
+    replaceCurrentInstructionWithConstInt(code, BooleanUtils.intValue(value));
+  }
+
   void replaceCurrentInstructionWithConstClass(
       AppView<?> appView, IRCode code, DexType type, DebugLocalInfo localInfo);
 
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/apimodel/ApiInvokeOutlinerDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/apimodel/ApiInvokeOutlinerDesugaring.java
index 3c0046f..3050bdc 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/apimodel/ApiInvokeOutlinerDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/apimodel/ApiInvokeOutlinerDesugaring.java
@@ -8,17 +8,21 @@
 
 import com.android.tools.r8.androidapi.AndroidApiLevelCompute;
 import com.android.tools.r8.androidapi.ComputedApiLevel;
+import com.android.tools.r8.cf.code.CfFieldInstruction;
 import com.android.tools.r8.cf.code.CfInstruction;
 import com.android.tools.r8.cf.code.CfInvoke;
 import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
 import com.android.tools.r8.contexts.CompilationContext.UniqueContext;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedMember;
 import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexLibraryClass;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -27,11 +31,16 @@
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.FreshLocalProvider;
 import com.android.tools.r8.ir.desugar.LocalStackAllocator;
+import com.android.tools.r8.ir.synthetic.FieldAccessorBuilder;
 import com.android.tools.r8.ir.synthetic.ForwardMethodBuilder;
+import com.android.tools.r8.synthesis.SyntheticMethodBuilder;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.TraversalContinuation;
 import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.List;
+import java.util.function.Function;
 
 /**
  * This desugaring will outline calls to library methods that are introduced after the min-api
@@ -57,11 +66,12 @@
       MethodProcessingContext methodProcessingContext,
       CfInstructionDesugaringCollection desugaringCollection,
       DexItemFactory dexItemFactory) {
-    ComputedApiLevel computedApiLevel = getComputedApiLevelForMethodOnHolderWithMinApi(instruction);
+    ComputedApiLevel computedApiLevel =
+        getComputedApiLevelInstructionOnHolderWithMinApi(instruction);
     if (computedApiLevel.isGreaterThan(appView.computedMinApiLevel())) {
       return desugarLibraryCall(
           methodProcessingContext.createUniqueContext(),
-          instruction.asInvoke(),
+          instruction,
           computedApiLevel,
           dexItemFactory,
           eventConsumer,
@@ -75,59 +85,68 @@
     if (context.getDefinition().isD8R8Synthesized()) {
       return false;
     }
-    return getComputedApiLevelForMethodOnHolderWithMinApi(instruction)
+    return getComputedApiLevelInstructionOnHolderWithMinApi(instruction)
         .isGreaterThan(appView.computedMinApiLevel());
   }
 
-  private ComputedApiLevel getComputedApiLevelForMethodOnHolderWithMinApi(
+  private ComputedApiLevel getComputedApiLevelInstructionOnHolderWithMinApi(
       CfInstruction instruction) {
-    if (!instruction.isInvoke()) {
+    if (!instruction.isInvoke() && !instruction.isFieldInstruction()) {
       return appView.computedMinApiLevel();
     }
-    CfInvoke cfInvoke = instruction.asInvoke();
-    if (cfInvoke.isInvokeSpecial()) {
+    DexReference reference;
+    if (instruction.isInvoke()) {
+      CfInvoke cfInvoke = instruction.asInvoke();
+      if (cfInvoke.isInvokeSpecial()) {
+        return appView.computedMinApiLevel();
+      }
+      reference = cfInvoke.getMethod();
+    } else {
+      reference = instruction.asFieldInstruction().getField();
+    }
+    if (!reference.getContextType().isClassType()) {
       return appView.computedMinApiLevel();
     }
-    DexType holderType = cfInvoke.getMethod().getHolderType();
-    if (!holderType.isClassType()) {
-      return appView.computedMinApiLevel();
-    }
-    DexClass holder = appView.definitionFor(holderType);
+    DexClass holder = appView.definitionFor(reference.getContextType());
     if (holder == null || !holder.isLibraryClass()) {
       return appView.computedMinApiLevel();
     }
-    ComputedApiLevel methodApiLevel =
-        apiLevelCompute.computeApiLevelForLibraryReference(
-            cfInvoke.getMethod(), ComputedApiLevel.unknown());
-    if (appView.computedMinApiLevel().isGreaterThanOrEqualTo(methodApiLevel)
-        || isApiLevelLessThanOrEqualTo9(methodApiLevel)
-        || methodApiLevel.isUnknownApiLevel()) {
+    ComputedApiLevel referenceApiLevel =
+        apiLevelCompute.computeApiLevelForLibraryReference(reference, ComputedApiLevel.unknown());
+    if (appView.computedMinApiLevel().isGreaterThanOrEqualTo(referenceApiLevel)
+        || isApiLevelLessThanOrEqualTo9(referenceApiLevel)
+        || referenceApiLevel.isUnknownApiLevel()) {
       return appView.computedMinApiLevel();
     }
     // Check for protected or package private access flags before outlining.
     if (holder.isInterface()) {
-      return methodApiLevel;
+      return referenceApiLevel;
     } else {
-      DexEncodedMethod methodDefinition =
-          simpleLookupInClassHierarchy(holder.asLibraryClass(), cfInvoke.getMethod());
-      return methodDefinition != null && methodDefinition.isPublic()
-          ? methodApiLevel
+      DexEncodedMember<?, ?> definition =
+          simpleLookupInClassHierarchy(
+              holder.asLibraryClass(),
+              reference.isDexMethod()
+                  ? x -> x.lookupMethod(reference.asDexMethod())
+                  : x -> x.lookupField(reference.asDexField()));
+      return definition != null && definition.isPublic()
+          ? referenceApiLevel
           : appView.computedMinApiLevel();
     }
   }
 
-  private DexEncodedMethod simpleLookupInClassHierarchy(DexLibraryClass holder, DexMethod method) {
-    DexEncodedMethod result = holder.lookupMethod(method);
+  private DexEncodedMember<?, ?> simpleLookupInClassHierarchy(
+      DexLibraryClass holder, Function<DexClass, DexEncodedMember<?, ?>> lookup) {
+    DexEncodedMember<?, ?> result = lookup.apply(holder);
     if (result != null) {
       return result;
     }
-    TraversalContinuation<DexEncodedMethod, ?> traversalResult =
+    TraversalContinuation<DexEncodedMember<?, ?>, ?> traversalResult =
         appView
             .appInfoForDesugaring()
             .traverseSuperClasses(
                 holder,
                 (ignored, superClass, ignored_) -> {
-                  DexEncodedMethod definition = superClass.lookupMethod(method);
+                  DexEncodedMember<?, ?> definition = lookup.apply(superClass);
                   if (definition != null) {
                     return TraversalContinuation.doBreak(definition);
                   }
@@ -143,29 +162,24 @@
 
   private Collection<CfInstruction> desugarLibraryCall(
       UniqueContext uniqueContext,
-      CfInvoke invoke,
+      CfInstruction instruction,
       ComputedApiLevel computedApiLevel,
       DexItemFactory factory,
       ApiInvokeOutlinerDesugaringEventConsumer eventConsumer,
       ProgramMethod context) {
-    DexMethod method = invoke.getMethod();
+    assert instruction.isInvoke() || instruction.isFieldInstruction();
     ProgramMethod outlinedMethod =
-        ensureOutlineMethod(uniqueContext, method, computedApiLevel, factory, invoke);
+        ensureOutlineMethod(uniqueContext, instruction, computedApiLevel, factory, context);
     eventConsumer.acceptOutlinedMethod(outlinedMethod, context);
     return ImmutableList.of(new CfInvoke(INVOKESTATIC, outlinedMethod.getReference(), false));
   }
 
   private ProgramMethod ensureOutlineMethod(
       UniqueContext context,
-      DexMethod apiMethod,
+      CfInstruction instruction,
       ComputedApiLevel apiLevel,
       DexItemFactory factory,
-      CfInvoke invoke) {
-    DexClass libraryHolder = appView.definitionFor(apiMethod.getHolderType());
-    assert libraryHolder != null;
-    boolean isVirtualMethod = invoke.isInvokeVirtual() || invoke.isInvokeInterface();
-    assert verifyLibraryHolderAndInvoke(libraryHolder, apiMethod, isVirtualMethod);
-    DexProto proto = factory.prependHolderToProtoIf(apiMethod, isVirtualMethod);
+      ProgramMethod programContext) {
     return appView
         .getSyntheticItems()
         .createMethod(
@@ -174,7 +188,6 @@
             appView,
             syntheticMethodBuilder -> {
               syntheticMethodBuilder
-                  .setProto(proto)
                   .setAccessFlags(
                       MethodAccessFlags.builder()
                           .setPublic()
@@ -183,24 +196,83 @@
                           .setBridge()
                           .build())
                   .setApiLevelForDefinition(apiLevel)
-                  .setApiLevelForCode(apiLevel)
-                  .setCode(
-                      m -> {
-                        if (isVirtualMethod) {
-                          return ForwardMethodBuilder.builder(factory)
-                              .setVirtualTarget(apiMethod, libraryHolder.isInterface())
-                              .setNonStaticSource(apiMethod)
-                              .build();
-                        } else {
-                          return ForwardMethodBuilder.builder(factory)
-                              .setStaticTarget(apiMethod, libraryHolder.isInterface())
-                              .setStaticSource(apiMethod)
-                              .build();
-                        }
-                      });
+                  .setApiLevelForCode(apiLevel);
+              if (instruction.isInvoke()) {
+                setCodeForInvoke(syntheticMethodBuilder, instruction.asInvoke(), factory);
+              } else {
+                assert instruction.isCfInstruction();
+                setCodeForFieldInstruction(
+                    syntheticMethodBuilder,
+                    instruction.asFieldInstruction(),
+                    factory,
+                    programContext);
+              }
             });
   }
 
+  private void setCodeForInvoke(
+      SyntheticMethodBuilder methodBuilder, CfInvoke invoke, DexItemFactory factory) {
+    DexMethod method = invoke.getMethod();
+    DexClass libraryHolder = appView.definitionFor(method.getHolderType());
+    assert libraryHolder != null;
+    boolean isVirtualMethod = invoke.isInvokeVirtual() || invoke.isInvokeInterface();
+    assert verifyLibraryHolderAndInvoke(libraryHolder, method, isVirtualMethod);
+    DexProto proto = factory.prependHolderToProtoIf(method, isVirtualMethod);
+    methodBuilder
+        .setProto(proto)
+        .setCode(
+            m -> {
+              if (isVirtualMethod) {
+                return ForwardMethodBuilder.builder(factory)
+                    .setVirtualTarget(method, libraryHolder.isInterface())
+                    .setNonStaticSource(method)
+                    .build();
+              } else {
+                return ForwardMethodBuilder.builder(factory)
+                    .setStaticTarget(method, libraryHolder.isInterface())
+                    .setStaticSource(method)
+                    .build();
+              }
+            });
+  }
+
+  private void setCodeForFieldInstruction(
+      SyntheticMethodBuilder methodBuilder,
+      CfFieldInstruction fieldInstruction,
+      DexItemFactory factory,
+      ProgramMethod programContext) {
+    DexField field = fieldInstruction.getField();
+    DexClass libraryHolder = appView.definitionFor(field.getHolderType());
+    assert libraryHolder != null;
+    boolean isInstance =
+        fieldInstruction.isInstanceFieldPut() || fieldInstruction.isInstanceFieldGet();
+    // Outlined field references will return a value if getter and only takes arguments if
+    // instance or if put or two arguments if both.
+    DexType returnType = fieldInstruction.isFieldGet() ? field.getType() : factory.voidType;
+    List<DexType> parameters = new ArrayList<>();
+    if (isInstance) {
+      parameters.add(libraryHolder.getType());
+    }
+    if (fieldInstruction.isFieldPut()) {
+      parameters.add(field.getType());
+    }
+    methodBuilder
+        .setProto(factory.createProto(returnType, parameters))
+        .setCode(
+            m ->
+                FieldAccessorBuilder.builder()
+                    .applyIf(
+                        isInstance,
+                        thenConsumer -> thenConsumer.setInstanceField(field),
+                        elseConsumer -> elseConsumer.setStaticField(field))
+                    .applyIf(
+                        fieldInstruction.isFieldGet(),
+                        FieldAccessorBuilder::setGetter,
+                        FieldAccessorBuilder::setSetter)
+                    .setSourceMethod(programContext.getReference())
+                    .build());
+  }
+
   private boolean verifyLibraryHolderAndInvoke(
       DexClass libraryHolder, DexMethod apiMethod, boolean isVirtualInvoke) {
     DexEncodedMethod libraryApiMethodDefinition = libraryHolder.lookupMethod(apiMethod);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecificationParser.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecificationParser.java
index 5f8e35a..0be57c5 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecificationParser.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecificationParser.java
@@ -290,8 +290,7 @@
       for (Map.Entry<String, JsonElement> retarget :
           jsonFlagSet.get(RETARGET_STATIC_FIELD_KEY).getAsJsonObject().entrySet()) {
         builder.retargetStaticField(
-            parseField(retarget.getKey()),
-            stringDescriptorToDexType(retarget.getValue().getAsString()));
+            parseField(retarget.getKey()), parseField(retarget.getValue().getAsString()));
       }
     }
     if (jsonFlagSet.has(RETARGET_METHOD_KEY)) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanRewritingFlags.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanRewritingFlags.java
index 5c70c34..a927cdf 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanRewritingFlags.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanRewritingFlags.java
@@ -30,7 +30,7 @@
   private final Set<String> maintainPrefix;
   private final Map<String, Map<String, String>> rewriteDerivedPrefix;
   private final Map<DexType, DexType> emulatedInterfaces;
-  private final Map<DexField, DexType> retargetStaticField;
+  private final Map<DexField, DexField> retargetStaticField;
   private final Map<DexMethod, DexType> covariantRetarget;
   private final Map<DexMethod, DexType> retargetMethod;
   private final Map<DexMethod, DexType> retargetMethodEmulatedDispatch;
@@ -49,7 +49,7 @@
       Set<String> maintainPrefix,
       Map<String, Map<String, String>> rewriteDerivedPrefix,
       Map<DexType, DexType> emulateLibraryInterface,
-      Map<DexField, DexType> retargetStaticField,
+      Map<DexField, DexField> retargetStaticField,
       Map<DexMethod, DexType> covariantRetarget,
       Map<DexMethod, DexType> retargetMethod,
       Map<DexMethod, DexType> retargetMethodEmulatedDispatch,
@@ -148,7 +148,7 @@
     return emulatedInterfaces;
   }
 
-  public Map<DexField, DexType> getRetargetStaticField() {
+  public Map<DexField, DexField> getRetargetStaticField() {
     return retargetStaticField;
   }
 
@@ -217,7 +217,7 @@
     private final Set<String> maintainPrefix;
     private final Map<String, Map<String, String>> rewriteDerivedPrefix;
     private final Map<DexType, DexType> emulatedInterfaces;
-    private final Map<DexField, DexType> retargetStaticField;
+    private final Map<DexField, DexField> retargetStaticField;
     private final Map<DexMethod, DexType> covariantRetarget;
     private final Map<DexMethod, DexType> retargetMethod;
     private final Map<DexMethod, DexType> retargetMethodEmulatedDispatch;
@@ -261,7 +261,7 @@
         Set<String> maintainPrefix,
         Map<String, Map<String, String>> rewriteDerivedPrefix,
         Map<DexType, DexType> emulateLibraryInterface,
-        Map<DexField, DexType> retargetStaticField,
+        Map<DexField, DexField> retargetStaticField,
         Map<DexMethod, DexType> covariantRetarget,
         Map<DexMethod, DexType> retargetMethod,
         Map<DexMethod, DexType> retargetMethodEmulatedDispatch,
@@ -388,11 +388,11 @@
       return this;
     }
 
-    public Builder retargetStaticField(DexField key, DexType rewrittenType) {
+    public Builder retargetStaticField(DexField key, DexField value) {
       put(
           retargetStaticField,
           key,
-          rewrittenType,
+          value,
           HumanDesugaredLibrarySpecificationParser.RETARGET_STATIC_FIELD_KEY);
       return this;
     }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyDesugaredLibrarySpecificationParser.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyDesugaredLibrarySpecificationParser.java
index e17494f..1779022 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyDesugaredLibrarySpecificationParser.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyDesugaredLibrarySpecificationParser.java
@@ -8,6 +8,7 @@
 import static com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecificationParser.isHumanSpecification;
 
 import com.android.tools.r8.StringResource;
+import com.android.tools.r8.errors.UnsupportedDesugaredLibraryConfigurationVersionDiagnostic;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.TopLevelFlagsBuilder;
 import com.android.tools.r8.origin.Origin;
@@ -172,11 +173,7 @@
     int formatVersion = formatVersionElement.getAsInt();
     if (formatVersion > MAX_SUPPORTED_VERSION) {
       throw reporter.fatalError(
-          new StringDiagnostic(
-              "Unsupported desugared library configuration version, please upgrade the D8/R8"
-                  + " compiler."
-                  + " See https://developer.android.com/studio/build/library-desugaring-versions.",
-              origin));
+          new UnsupportedDesugaredLibraryConfigurationVersionDiagnostic(origin));
     }
 
     String version = required(jsonConfig, VERSION_KEY).getAsString();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java
index dad5fd5..1b83bf8 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java
@@ -197,11 +197,7 @@
   }
 
   public boolean isSupported(DexReference reference) {
-    // Support through type rewriting.
-    if (getRewriteType().containsKey(reference.getContextType())) {
-      return true;
-    }
-    if (getMaintainType().contains(reference.getContextType())) {
+    if (isContextTypeMaintainedOrRewritten(reference)) {
       return true;
     }
     if (!reference.isDexMethod()) {
@@ -223,6 +219,12 @@
     return false;
   }
 
+  public boolean isContextTypeMaintainedOrRewritten(DexReference reference) {
+    // Support through type rewriting.
+    return getRewriteType().containsKey(reference.getContextType())
+        || getMaintainType().contains(reference.getContextType());
+  }
+
   @Override
   public MachineDesugaredLibrarySpecification toMachineSpecification(
       DexApplication app, Timing timing) throws IOException {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeter.java
index b9c92a6..302c08c 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeter.java
@@ -75,7 +75,7 @@
       MethodProcessingContext methodProcessingContext,
       CfInstructionDesugaringCollection desugaringCollection,
       DexItemFactory dexItemFactory) {
-    if (instruction.isFieldInstruction() && needsDesugaring(instruction, context)) {
+    if (instruction.isStaticFieldGet() && needsDesugaring(instruction, context)) {
       return desugarFieldInstruction(instruction.asFieldInstruction(), context);
     } else if (instruction.isInvoke() && needsDesugaring(instruction, context)) {
       return desugarInvoke(instruction.asInvoke(), eventConsumer, context, methodProcessingContext);
@@ -87,7 +87,7 @@
       CfFieldInstruction fieldInstruction, ProgramMethod context) {
     DexField fieldRetarget = fieldRetarget(fieldInstruction, context);
     assert fieldRetarget != null;
-    assert fieldInstruction.isStaticFieldGet() || fieldInstruction.isStaticFieldPut();
+    assert fieldInstruction.isStaticFieldGet();
     return Collections.singletonList(fieldInstruction.createWithField(fieldRetarget));
   }
 
@@ -106,7 +106,7 @@
 
   @Override
   public boolean needsDesugaring(CfInstruction instruction, ProgramMethod context) {
-    if (instruction.isFieldInstruction()) {
+    if (instruction.isStaticFieldGet()) {
       return fieldRetarget(instruction.asFieldInstruction(), context) != null;
     } else if (instruction.isInvoke()) {
       return computeNewInvokeTarget(instruction.asInvoke(), context).hasNewInvokeTarget();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineRetargetConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineRetargetConverter.java
index 7dcb8a8..6078e9d 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineRetargetConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineRetargetConverter.java
@@ -40,7 +40,7 @@
       BiConsumer<String, Set<? extends DexReference>> warnConsumer) {
     rewritingFlags
         .getRetargetStaticField()
-        .forEach((field, type) -> convertRetargetField(builder, field, type));
+        .forEach((field, rewrittenField) -> convertRetargetField(builder, field, rewrittenField));
     rewritingFlags
         .getCovariantRetarget()
         .forEach((method, type) -> convertCovariantRetarget(builder, method, type));
@@ -56,15 +56,14 @@
   }
 
   private void convertRetargetField(
-      MachineRewritingFlags.Builder builder, DexField field, DexType type) {
+      MachineRewritingFlags.Builder builder, DexField field, DexField rewrittenField) {
     DexClass holder = appInfo.definitionFor(field.holder);
     DexEncodedField foundField = holder.lookupField(field);
     if (foundField == null) {
       missingReferences.add(field);
       return;
     }
-    builder.putStaticFieldRetarget(
-        field, appInfo.dexItemFactory().createField(type, field.type, field.name));
+    builder.putStaticFieldRetarget(field, rewrittenField);
   }
 
   private void convertRetargetMethodEmulatedDispatch(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index 85802c1..36b96dc 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -2045,23 +2045,6 @@
       // Locate the closest dominator block for all user blocks.
       DominatorTree dominatorTree = dominatorTreeMemoization.computeIfAbsent();
       BasicBlock dominator = dominatorTree.closestDominator(userBlocks);
-      // If the closest dominator block is a block that uses the constant for a phi the constant
-      // needs to go in the immediate dominator block so that it is available for phi moves.
-      for (Phi phi : instruction.outValue().uniquePhiUsers()) {
-        if (phi.getBlock() == dominator) {
-          if (instruction.outValue().numberOfAllUsers() == 1 &&
-              phi.usesValueOneTime(instruction.outValue())) {
-            // Out value is used only one time, move the constant directly to the corresponding
-            // branch rather than into the dominator to avoid to generate a const on paths which
-            // does not required it.
-            int predIndex = phi.getOperands().indexOf(instruction.outValue());
-            dominator = dominator.getPredecessors().get(predIndex);
-          } else {
-            dominator = dominatorTree.immediateDominator(dominator);
-          }
-          break;
-        }
-      }
 
       if (instruction.instructionTypeCanThrow()) {
         if (block.hasCatchHandlers() || dominator.hasCatchHandlers()) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
index 10cdd99..543eeb7 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
@@ -38,6 +38,7 @@
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.If;
 import com.android.tools.r8.ir.code.InstanceGet;
+import com.android.tools.r8.ir.code.InstanceOf;
 import com.android.tools.r8.ir.code.InstancePut;
 import com.android.tools.r8.ir.code.Instruction;
 import com.android.tools.r8.ir.code.InstructionListIterator;
@@ -589,6 +590,15 @@
   private void removeMiscUsages(IRCode code, Set<Value> affectedValues) {
     boolean needToRemoveUnreachableBlocks = false;
     for (Instruction user : eligibleInstance.uniqueUsers()) {
+      if (user.isInstanceOf()) {
+        InstanceOf instanceOf = user.asInstanceOf();
+        InstructionListIterator instructionIterator =
+            user.getBlock().listIterator(code, instanceOf);
+        instructionIterator.replaceCurrentInstructionWithConstBoolean(
+            code, appView.appInfo().isSubtype(eligibleClass.getType(), instanceOf.type()));
+        continue;
+      }
+
       if (user.isInvokeMethod()) {
         InvokeMethod invoke = user.asInvokeMethod();
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
index 525041d..b1ea459 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
@@ -489,7 +489,8 @@
     }
 
     if (user.isInvokeStatic()) {
-      DexClassAndMethod singleTarget = user.asInvokeStatic().lookupSingleTarget(appView, context);
+      InvokeStatic invoke = user.asInvokeStatic();
+      DexClassAndMethod singleTarget = invoke.lookupSingleTarget(appView, context);
       if (singleTarget == null) {
         return false;
       }
@@ -498,6 +499,33 @@
         // in the valueOf utility method.
         addRequiredNameData(enumClass);
         markMethodDependsOnLibraryModelisation(context);
+        // The out-value must be cast before it is used, or an assume instruction must strengthen
+        // its dynamic type, so that the out-value is analyzed by the enum unboxing analysis.
+        if (invoke.hasOutValue()) {
+          if (invoke.outValue().hasPhiUsers()) {
+            return false;
+          }
+          for (Instruction enumUser : invoke.outValue().uniqueUsers()) {
+            if (enumUser.isAssumeWithDynamicTypeAssumption()) {
+              Assume assume = enumUser.asAssume();
+              if (assume
+                  .getDynamicTypeAssumption()
+                  .getDynamicType()
+                  .getDynamicUpperBoundType()
+                  .equalUpToNullability(enumClass.getType().toTypeElement(appView))) {
+                // OK.
+                continue;
+              }
+            } else if (enumUser.isCheckCast()) {
+              CheckCast checkCast = enumUser.asCheckCast();
+              if (checkCast.getType() == enumClass.getType()) {
+                // OK.
+                continue;
+              }
+            }
+            return false;
+          }
+        }
         return true;
       }
       if (singleTarget.getReference()
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 f9266b0..050b1ff 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
@@ -504,7 +504,7 @@
 
     private List<MappingInformation> additionalMappingInfo = EMPTY_MAPPING_INFORMATION;
 
-    private MappedRange(
+    MappedRange(
         Range minifiedRange, MethodSignature signature, Range originalRange, String renamedName) {
       this.minifiedRange = minifiedRange;
       this.signature = signature;
diff --git a/src/main/java/com/android/tools/r8/naming/ComposingBuilder.java b/src/main/java/com/android/tools/r8/naming/ComposingBuilder.java
new file mode 100644
index 0000000..6e1bc00
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/ComposingBuilder.java
@@ -0,0 +1,577 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.naming;
+
+import static com.android.tools.r8.utils.FunctionUtils.ignoreArgument;
+
+import com.android.tools.r8.naming.ClassNamingForNameMapper.MappedRange;
+import com.android.tools.r8.naming.ClassNamingForNameMapper.MappedRangesOfName;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.naming.mappinginformation.MapVersionMappingInformation;
+import com.android.tools.r8.naming.mappinginformation.MappingInformation;
+import com.android.tools.r8.naming.mappinginformation.RewriteFrameMappingInformation;
+import com.android.tools.r8.naming.mappinginformation.RewriteFrameMappingInformation.ThrowsCondition;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.BiMapContainer;
+import com.android.tools.r8.utils.ChainableStringConsumer;
+import com.android.tools.r8.utils.ConsumerUtils;
+import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.SegmentTree;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+public class ComposingBuilder {
+
+  /**
+   * To ensure we can do alpha renaming of classes and members without polluting the existing
+   * mappping, we use a committed map that we update for each class name mapping. That allows us to
+   * rename to existing renamed names as long as these are also renamed later in the map.
+   */
+  private final Map<String, ComposingClassBuilder> committed = new HashMap<>();
+
+  private Map<String, ComposingClassBuilder> current = new HashMap<>();
+
+  private MapVersionMappingInformation currentMapVersion = null;
+
+  private final ComposingSharedData sharedData = new ComposingSharedData();
+
+  public void compose(ClassNameMapper classNameMapper) throws MappingComposeException {
+    MapVersionMappingInformation thisMapVersion = classNameMapper.getFirstMapVersionInformation();
+    if (thisMapVersion != null) {
+      if (currentMapVersion == null
+          || currentMapVersion.getMapVersion().isLessThan(thisMapVersion.getMapVersion())) {
+        currentMapVersion = thisMapVersion;
+      }
+    }
+    sharedData.patchupMappingInformation(classNameMapper);
+    for (ClassNamingForNameMapper classMapping : classNameMapper.getClassNameMappings().values()) {
+      compose(classMapping);
+    }
+    commit();
+  }
+
+  private void compose(ClassNamingForNameMapper classMapping) throws MappingComposeException {
+    String originalName = classMapping.originalName;
+    ComposingClassBuilder composingClassBuilder = committed.get(originalName);
+    String renamedName = classMapping.renamedName;
+    if (composingClassBuilder == null) {
+      composingClassBuilder = new ComposingClassBuilder(originalName, renamedName, sharedData);
+    } else {
+      composingClassBuilder.setRenamedName(renamedName);
+      committed.remove(originalName);
+    }
+    ComposingClassBuilder duplicateMapping = current.put(renamedName, composingClassBuilder);
+    if (duplicateMapping != null) {
+      throw new MappingComposeException(
+          "Duplicate class mapping. Both '"
+              + duplicateMapping.getOriginalName()
+              + "' and '"
+              + originalName
+              + "' maps to '"
+              + renamedName
+              + "'.");
+    }
+    composingClassBuilder.compose(classMapping);
+  }
+
+  private void commit() throws MappingComposeException {
+    for (Entry<String, ComposingClassBuilder> newEntry : current.entrySet()) {
+      String renamedName = newEntry.getKey();
+      ComposingClassBuilder classBuilder = newEntry.getValue();
+      ComposingClassBuilder duplicateMapping = committed.put(renamedName, classBuilder);
+      if (duplicateMapping != null) {
+        throw new MappingComposeException(
+            "Duplicate class mapping. Both '"
+                + duplicateMapping.getOriginalName()
+                + "' and '"
+                + classBuilder.getOriginalName()
+                + "' maps to '"
+                + renamedName
+                + "'.");
+      }
+    }
+    current = new HashMap<>();
+  }
+
+  @Override
+  public String toString() {
+    List<ComposingClassBuilder> classBuilders = new ArrayList<>(committed.values());
+    classBuilders.sort(Comparator.comparing(ComposingClassBuilder::getOriginalName));
+    StringBuilder sb = new StringBuilder();
+    // TODO(b/241763080): Keep preamble of mapping files"
+    if (currentMapVersion != null) {
+      sb.append("# ").append(currentMapVersion.serialize()).append("\n");
+    }
+    ChainableStringConsumer wrap = ChainableStringConsumer.wrap(sb::append);
+    for (ComposingClassBuilder classBuilder : classBuilders) {
+      classBuilder.write(wrap);
+    }
+    return sb.toString();
+  }
+
+  public static class ComposingSharedData {
+
+    /**
+     * RewriteFrameInformation contains condition clauses that are bound to the residual program. As
+     * a result of that, we have to patch up the conditions when we compose new class mappings.
+     */
+    private final List<RewriteFrameMappingInformation> mappingInformationToPatchUp =
+        new ArrayList<>();
+
+    private void patchupMappingInformation(ClassNameMapper classNameMapper) {
+      BiMapContainer<String, String> obfuscatedToOriginalMapping =
+          classNameMapper.getObfuscatedToOriginalMapping();
+      for (RewriteFrameMappingInformation rewriteMappingInfo : mappingInformationToPatchUp) {
+        rewriteMappingInfo
+            .getConditions()
+            .forEach(
+                rewriteCondition -> {
+                  ThrowsCondition throwsCondition = rewriteCondition.asThrowsCondition();
+                  if (throwsCondition != null) {
+                    String originalName = throwsCondition.getClassReference().getTypeName();
+                    String obfuscatedName = obfuscatedToOriginalMapping.inverse.get(originalName);
+                    if (obfuscatedName != null) {
+                      throwsCondition.setClassReferenceInternal(
+                          Reference.classFromTypeName(obfuscatedName));
+                    }
+                  }
+                });
+      }
+    }
+  }
+
+  public static class ComposingClassBuilder {
+
+    private static final String INDENTATION = "    ";
+    private static final int NO_RANGE_FROM = -1;
+
+    private final String originalName;
+    private String renamedName;
+    private final Map<String, List<MemberNaming>> fieldMembers = new HashMap<>();
+    // Ideally we would have liked to use the signature as a key since this uniquely specifies a
+    // method. However, because a mapping file has no right hand side, we therefore assume that a
+    // starting position uniquely identifies a method. If no position is given there can be only
+    // one method since any shrinker should put in line numbers for overloads.
+    private final Map<String, SegmentTree<List<MappedRange>>> methodMembers = new HashMap<>();
+    private List<MappingInformation> additionalMappingInfo = null;
+    private final ComposingSharedData sharedData;
+
+    private ComposingClassBuilder(
+        String originalName, String renamedName, ComposingSharedData sharedData) {
+      this.originalName = originalName;
+      this.renamedName = renamedName;
+      this.sharedData = sharedData;
+    }
+
+    public void setRenamedName(String renamedName) {
+      this.renamedName = renamedName;
+    }
+
+    public String getOriginalName() {
+      return originalName;
+    }
+
+    public String getRenamedName() {
+      return renamedName;
+    }
+
+    public void compose(ClassNamingForNameMapper mapper) throws MappingComposeException {
+      List<MappingInformation> newMappingInfo = mapper.getAdditionalMappingInfo();
+      if (newMappingInfo != null && !newMappingInfo.isEmpty()) {
+        if (additionalMappingInfo == null) {
+          additionalMappingInfo = new ArrayList<>();
+        }
+        additionalMappingInfo.addAll(newMappingInfo);
+      }
+      composeFieldNamings(mapper);
+      composeMethodNamings(mapper);
+    }
+
+    private void composeFieldNamings(ClassNamingForNameMapper mapper)
+        throws MappingComposeException {
+      mapper.forAllFieldNaming(
+          fieldNaming -> {
+            List<MemberNaming> memberNamings = fieldMembers.get(fieldNaming.getOriginalName());
+            if (memberNamings == null) {
+              fieldMembers
+                  .computeIfAbsent(fieldNaming.getRenamedName(), ignoreArgument(ArrayList::new))
+                  .add(fieldNaming);
+              return;
+            }
+            // There is no right-hand side of field mappings thus if we have seen an existing
+            // mapping we cannot compose the type. For fields we check that the original type is
+            // the same or we throw an error since we cannot guarantee a proper composition.
+            for (int i = 0; i < memberNamings.size(); i++) {
+              MemberNaming memberNaming = memberNamings.get(i);
+              assert memberNaming.getRenamedName().equals(fieldNaming.getOriginalName());
+              if (memberNaming.renamedSignature.equals(fieldNaming.getOriginalSignature())) {
+                memberNamings.set(
+                    i,
+                    new MemberNaming(
+                        memberNaming.getOriginalSignature(), fieldNaming.getRenamedName()));
+                return;
+              }
+            }
+            throw new MappingComposeException(
+                "Unable to compose field naming '"
+                    + fieldNaming
+                    + "' since the original type has changed.");
+          });
+    }
+
+    private void composeMethodNamings(ClassNamingForNameMapper mapper)
+        throws MappingComposeException {
+      for (Entry<String, MappedRangesOfName> entry : mapper.mappedRangesByRenamedName.entrySet()) {
+        MappedRangesOfName value = entry.getValue();
+        List<MappedRange> mappedRanges = value.getMappedRanges();
+        MappedRangeResult mappedRangeResult;
+        int index = 0;
+        while ((mappedRangeResult = getMappedRangesForMethod(mappedRanges, index)) != null) {
+          index = mappedRangeResult.endIndex;
+          MappedRange newMappedRange = mappedRangeResult.lastRange;
+          String originalName = newMappedRange.signature.getName();
+          SegmentTree<List<MappedRange>> listSegmentTree = methodMembers.get(originalName);
+          List<MappedRange> existingMappedRanges = null;
+          if (listSegmentTree != null) {
+            Entry<Integer, List<MappedRange>> existingEntry =
+                listSegmentTree.findEntry(mappedRangeResult.startOriginalPosition);
+            // We assume that all new minified ranges for a method are rewritten in the new map
+            // such that no previous existing positions exists.
+            if (existingEntry != null) {
+              listSegmentTree.removeSegment(existingEntry.getKey());
+              existingMappedRanges = existingEntry.getValue();
+            }
+          }
+          Range minifiedRange = mappedRangeResult.lastRange.minifiedRange;
+          int endMinifiedPosition = minifiedRange == null ? NO_RANGE_FROM : minifiedRange.to;
+          methodMembers
+              .computeIfAbsent(newMappedRange.renamedName, ignored -> new SegmentTree<>(false))
+              .add(
+                  mappedRangeResult.startMinifiedPosition,
+                  endMinifiedPosition,
+                  composeMappedRangesForMethod(existingMappedRanges, mappedRangeResult.allRanges));
+        }
+      }
+    }
+
+    /***
+     * Iterates over mapped ranges in order, starting from index, and adds to an internal result as
+     * long as the current mapped range is the same method and return a mapped range result
+     * containing all ranges for a method along with some additional information.
+     */
+    private MappedRangeResult getMappedRangesForMethod(List<MappedRange> mappedRanges, int index) {
+      if (index >= mappedRanges.size()) {
+        return null;
+      }
+      List<MappedRange> seenMappedRanges = new ArrayList<>();
+      MappedRange lastSeen = mappedRanges.get(index);
+      seenMappedRanges.add(lastSeen);
+      if (lastSeen.minifiedRange == null) {
+        return new MappedRangeResult(
+            NO_RANGE_FROM, NO_RANGE_FROM, index + 1, lastSeen, seenMappedRanges);
+      }
+      int startMinifiedPosition = lastSeen.minifiedRange.from;
+      int startOriginalPosition =
+          lastSeen.originalRange == null ? NO_RANGE_FROM : lastSeen.originalRange.from;
+      for (int i = index + 1; i < mappedRanges.size(); i++) {
+        MappedRange thisMappedRange = mappedRanges.get(i);
+        // We assume that if we see a mapping where the minified range is 0 then we will not find
+        // another mapping where it is not null.
+        if (thisMappedRange.minifiedRange == null) {
+          assert !lastSeen.signature.equals(thisMappedRange.signature);
+          break;
+        }
+        // Otherwise break if we see a signature that is not equal to the current one and it
+        // has another minified range meaning it is not an inlinee.
+        if (!thisMappedRange.signature.equals(lastSeen.signature)
+            && !thisMappedRange.minifiedRange.equals(lastSeen.minifiedRange)
+            && !isInlineMappedRange(mappedRanges, i)) {
+          break;
+        }
+        for (MappingInformation mappingInformation : thisMappedRange.getAdditionalMappingInfo()) {
+          if (mappingInformation.isRewriteFrameMappingInformation()) {
+            sharedData.mappingInformationToPatchUp.add(
+                mappingInformation.asRewriteFrameMappingInformation());
+          }
+        }
+        seenMappedRanges.add(thisMappedRange);
+        lastSeen = thisMappedRange;
+      }
+      return new MappedRangeResult(
+          startMinifiedPosition,
+          startOriginalPosition,
+          index + seenMappedRanges.size(),
+          lastSeen,
+          seenMappedRanges);
+    }
+
+    private List<MappedRange> composeMappedRangesForMethod(
+        List<MappedRange> existingRanges, List<MappedRange> newRanges)
+        throws MappingComposeException {
+      assert !newRanges.isEmpty();
+      if (existingRanges == null || existingRanges.isEmpty()) {
+        return newRanges;
+      }
+      // The new minified ranges may have ranges that range over positions that has additional
+      // information, such as inlinees:
+      // Existing:
+      //  1:2:void caller():14:15 -> x
+      //  3:3:void inlinee():42:42 -> x
+      //  3:3:void caller():16:16 -> x
+      //  4:10:void caller():17:23 -> x
+      //  ...
+      // New mapping:
+      //  1:5:void x():1:5 -> y
+      //  6:10:void x():6:6 -> y
+      //  ...
+      // It is important that we therefore split up the new ranges to map everything back to the
+      // existing original mappings:
+      // Resulting mapping:
+      //  1:2:void caller():14:15 -> y
+      //  3:3:void inlinee():42:42 -> y
+      //  3:3:void caller():16:16 -> y
+      //  4:5:void caller():17:18 -> y
+      //  6:10:void caller():19:19 -> y
+      //  ...
+      Int2ReferenceMap<List<MappedRange>> mappedRangesForPosition =
+          getExistingMapping(existingRanges);
+      List<MappedRange> newComposedRanges = new ArrayList<>();
+      for (int i = 0; i < newRanges.size(); i++) {
+        if (isInlineMappedRange(newRanges, i)) {
+          throw new MappingComposeException(
+              "Unexpected inline frame '" + existingRanges.get(i) + "' in composing map.");
+        }
+        MappedRange newRange = newRanges.get(i);
+        if (newRange.originalRange == null) {
+          List<MappedRange> existingMappedRanges = mappedRangesForPosition.get(NO_RANGE_FROM);
+          assert existingMappedRanges.size() <= 1;
+          if (existingMappedRanges.isEmpty()) {
+            newComposedRanges.add(newRange);
+          } else {
+            MappedRange existingRange = existingMappedRanges.get(0);
+            newComposedRanges.add(
+                new MappedRange(
+                    newRange.minifiedRange,
+                    existingRange.signature,
+                    existingRange.originalRange,
+                    newRange.renamedName));
+          }
+        } else {
+          // First check if the original range matches the existing minified range.
+          List<MappedRange> existingMappedRanges =
+              mappedRangesForPosition.get(newRange.originalRange.from);
+          if (existingMappedRanges == null) {
+            throw new MappingComposeException(
+                "Unexpected missing original position for '" + newRange + "'.");
+          }
+          if (ListUtils.last(existingMappedRanges).minifiedRange.equals(newRange.originalRange)) {
+            computeComposedMappedRange(
+                newComposedRanges,
+                newRange,
+                existingMappedRanges,
+                newRange.minifiedRange.from,
+                newRange.minifiedRange.to);
+          } else {
+            // Otherwise, we have a situation where the minified range references over multiple
+            // existing ranges. We simply chop op when the range changes on the right hand side. To
+            // ensure we do not mess up the spans from the original range, we have to check if the
+            // current starting position is inside an original range, and then chop it off.
+            // Similarly, when writing the last block, we have to cut it off to match.
+            int lastStartingMinifiedFrom = newRange.minifiedRange.from;
+            for (int position = newRange.minifiedRange.from;
+                position <= newRange.minifiedRange.to;
+                position++) {
+              List<MappedRange> existingMappedRangesForPosition =
+                  mappedRangesForPosition.get(newRange.getOriginalLineNumber(position));
+              if (existingMappedRangesForPosition == null) {
+                throw new MappingComposeException(
+                    "Unexpected missing original position for '" + newRange + "'.");
+              }
+              if (!ListUtils.last(existingMappedRanges)
+                  .minifiedRange
+                  .equals(ListUtils.last(existingMappedRangesForPosition).minifiedRange)) {
+                computeComposedMappedRange(
+                    newComposedRanges,
+                    newRange,
+                    existingMappedRanges,
+                    lastStartingMinifiedFrom,
+                    position - 1);
+                lastStartingMinifiedFrom = position;
+                existingMappedRanges = existingMappedRangesForPosition;
+              }
+            }
+            computeComposedMappedRange(
+                newComposedRanges,
+                newRange,
+                existingMappedRanges,
+                lastStartingMinifiedFrom,
+                newRange.minifiedRange.to);
+          }
+        }
+      }
+      return newComposedRanges;
+    }
+
+    /***
+     * Builds a position to mapped ranges for mappings for looking up all mapped ranges for a given
+     * position.
+     */
+    private Int2ReferenceMap<List<MappedRange>> getExistingMapping(
+        List<MappedRange> existingRanges) {
+      Int2ReferenceMap<List<MappedRange>> mappedRangesForPosition =
+          new Int2ReferenceOpenHashMap<>();
+      List<MappedRange> currentRangesForPosition = new ArrayList<>();
+      for (int i = 0; i < existingRanges.size(); i++) {
+        MappedRange mappedRange = existingRanges.get(i);
+        currentRangesForPosition.add(mappedRange);
+        if (!isInlineMappedRange(existingRanges, i)) {
+          if (mappedRange.minifiedRange == null) {
+            mappedRangesForPosition.put(NO_RANGE_FROM, currentRangesForPosition);
+          } else {
+            for (int position = mappedRange.minifiedRange.from;
+                position <= mappedRange.minifiedRange.to;
+                position++) {
+              mappedRangesForPosition.put(position, currentRangesForPosition);
+            }
+          }
+          currentRangesForPosition = new ArrayList<>();
+        }
+      }
+      return mappedRangesForPosition;
+    }
+
+    private void computeComposedMappedRange(
+        List<MappedRange> newComposedRanges,
+        MappedRange newMappedRange,
+        List<MappedRange> existingMappedRanges,
+        int lastStartingMinifiedFrom,
+        int position) {
+      Range existingRange = existingMappedRanges.get(0).minifiedRange;
+      assert existingMappedRanges.stream().allMatch(x -> x.minifiedRange.equals(existingRange));
+      Range newMinifiedRange = new Range(lastStartingMinifiedFrom, position);
+      boolean copyOriginalRange = existingRange.equals(newMappedRange.originalRange);
+      for (MappedRange existingMappedRange : existingMappedRanges) {
+        Range existingOriginalRange = existingMappedRange.originalRange;
+        Range newOriginalRange;
+        if (copyOriginalRange || existingOriginalRange.span() == 1) {
+          newOriginalRange = existingOriginalRange;
+        } else {
+          // Find the window that the new range points to into the original range.
+          int existingMinifiedPos = newMappedRange.getOriginalLineNumber(lastStartingMinifiedFrom);
+          int newOriginalStart = existingMappedRange.getOriginalLineNumber(existingMinifiedPos);
+          if (newMappedRange.originalRange.span() == 1) {
+            newOriginalRange = new Range(newOriginalStart, newOriginalStart);
+          } else {
+            assert newMinifiedRange.span() <= existingOriginalRange.span();
+            newOriginalRange =
+                new Range(newOriginalStart, newOriginalStart + newMinifiedRange.span() - 1);
+          }
+        }
+        MappedRange computedRange =
+            new MappedRange(
+                newMinifiedRange,
+                existingMappedRange.signature,
+                newOriginalRange,
+                newMappedRange.renamedName);
+        existingMappedRange
+            .getAdditionalMappingInfo()
+            .forEach(
+                info -> computedRange.addMappingInformation(info, ConsumerUtils.emptyConsumer()));
+        newComposedRanges.add(computedRange);
+      }
+    }
+
+    private boolean isInlineMappedRange(List<MappedRange> mappedRanges, int index) {
+      if (index + 1 >= mappedRanges.size()) {
+        return false;
+      }
+      return mappedRanges
+          .get(index)
+          .minifiedRange
+          .equals(mappedRanges.get(index + 1).minifiedRange);
+    }
+
+    public void write(ChainableStringConsumer consumer) {
+      consumer.accept(originalName).accept(" -> ").accept(renamedName).accept(":\n");
+      if (additionalMappingInfo != null) {
+        additionalMappingInfo.forEach(
+            info -> consumer.accept("# " + info.serialize()).accept("\n"));
+      }
+      writeFields(consumer);
+      writeMethods(consumer);
+    }
+
+    private void writeFields(ChainableStringConsumer consumer) {
+      ArrayList<MemberNaming> fieldNamings = new ArrayList<>();
+      for (List<MemberNaming> namingsForKey : fieldMembers.values()) {
+        fieldNamings.addAll(namingsForKey);
+      }
+      fieldNamings.sort(Comparator.comparing(MemberNaming::getOriginalName));
+      fieldNamings.forEach(
+          naming -> {
+            consumer.accept(INDENTATION).accept(naming.toString()).accept("\n");
+          });
+    }
+
+    private void writeMethods(ChainableStringConsumer consumer) {
+      Map<String, List<MappedRange>> signatureToMappedRanges = new HashMap<>();
+      methodMembers
+          .values()
+          .forEach(
+              segmentTree -> {
+                segmentTree.visitSegments(
+                    mappedRanges -> {
+                      MethodSignature originalSignature = ListUtils.last(mappedRanges).signature;
+                      List<MappedRange> put =
+                          signatureToMappedRanges.put(
+                              originalSignature.getName() + "_" + originalSignature, mappedRanges);
+                      assert put == null;
+                    });
+              });
+      ArrayList<String> strings = new ArrayList<>(signatureToMappedRanges.keySet());
+      Collections.sort(strings);
+      for (String key : strings) {
+        signatureToMappedRanges
+            .get(key)
+            .forEach(
+                mappedRange -> {
+                  consumer.accept(INDENTATION).accept(mappedRange.toString()).accept("\n");
+                  for (MappingInformation info : mappedRange.getAdditionalMappingInfo()) {
+                    consumer.accept(INDENTATION).accept("# ").accept(info.serialize()).accept("\n");
+                  }
+                });
+      }
+    }
+
+    private static class MappedRangeResult {
+
+      private final int startMinifiedPosition;
+      private final int startOriginalPosition;
+      private final int endIndex;
+      private final MappedRange lastRange;
+      private final List<MappedRange> allRanges;
+
+      public MappedRangeResult(
+          int startMinifiedPosition,
+          int startOriginalPosition,
+          int endIndex,
+          MappedRange lastRange,
+          List<MappedRange> allRanges) {
+        this.startMinifiedPosition = startMinifiedPosition;
+        this.startOriginalPosition = startOriginalPosition;
+        this.endIndex = endIndex;
+        this.lastRange = lastRange;
+        this.allRanges = allRanges;
+      }
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/naming/MappingComposeException.java b/src/main/java/com/android/tools/r8/naming/MappingComposeException.java
new file mode 100644
index 0000000..f443d0e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/MappingComposeException.java
@@ -0,0 +1,15 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.naming;
+
+import com.android.tools.r8.Keep;
+
+@Keep
+public class MappingComposeException extends Exception {
+
+  public MappingComposeException(String message) {
+    super(message);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/naming/MappingComposer.java b/src/main/java/com/android/tools/r8/naming/MappingComposer.java
new file mode 100644
index 0000000..6450ab6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/MappingComposer.java
@@ -0,0 +1,24 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.naming;
+
+/**
+ * MappingComposer is a utility to do composition of mapping files to map line numbers correctly
+ * when having shrunken input that will end up using DEX PC mappings.
+ */
+public class MappingComposer {
+
+  public static String compose(ClassNameMapper... classNameMappers) throws MappingComposeException {
+    assert classNameMappers.length > 0;
+    if (classNameMappers.length == 1) {
+      return classNameMappers[0].toString();
+    }
+    ComposingBuilder builder = new ComposingBuilder();
+    for (ClassNameMapper classNameMapper : classNameMappers) {
+      builder.compose(classNameMapper);
+    }
+    return builder.toString();
+  }
+}
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 bf0a8dd..0c833bd 100644
--- a/src/main/java/com/android/tools/r8/naming/MemberNaming.java
+++ b/src/main/java/com/android/tools/r8/naming/MemberNaming.java
@@ -162,6 +162,10 @@
       }
     }
 
+    public String getName() {
+      return name;
+    }
+
     enum SignatureKind {
       METHOD,
       FIELD
diff --git a/src/main/java/com/android/tools/r8/naming/mappinginformation/RewriteFrameMappingInformation.java b/src/main/java/com/android/tools/r8/naming/mappinginformation/RewriteFrameMappingInformation.java
index 82b2a0b..00eb800 100644
--- a/src/main/java/com/android/tools/r8/naming/mappinginformation/RewriteFrameMappingInformation.java
+++ b/src/main/java/com/android/tools/r8/naming/mappinginformation/RewriteFrameMappingInformation.java
@@ -162,7 +162,7 @@
 
     static final String FUNCTION_NAME = "throws";
 
-    private final ClassReference classReference;
+    private ClassReference classReference;
 
     private ThrowsCondition(ClassReference classReference) {
       this.classReference = classReference;
@@ -183,6 +183,14 @@
       return this;
     }
 
+    public void setClassReferenceInternal(ClassReference reference) {
+      this.classReference = reference;
+    }
+
+    public ClassReference getClassReference() {
+      return classReference;
+    }
+
     @Override
     public boolean evaluate(RetraceStackTraceContextImpl context) {
       return classReference.equals(context.getThrownException());
diff --git a/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java b/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java
index 16e3dfb..3dd736b 100644
--- a/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java
+++ b/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java
@@ -78,13 +78,16 @@
   }
 
   private boolean isTargetingSuperMethod(ProgramMethod method, InvokeKind kind, DexMethod target) {
+    if (kind == InvokeKind.ILLEGAL) {
+      return false;
+    }
     if (kind == InvokeKind.SUPER) {
       return true;
     }
     if (kind == InvokeKind.STATIC) {
       return appView.appInfo().isStrictSubtypeOf(method.getHolderType(), target.holder);
     }
-    assert false : "Unexpected invoke-kind for visibility bridge";
+    assert false : "Unexpected invoke-kind for visibility bridge: " + kind;
     return false;
   }
 
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
index f968d56..bf1afad 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
@@ -42,6 +42,7 @@
 import com.android.tools.r8.utils.AccessUtils;
 import com.android.tools.r8.utils.AndroidApiLevelUtils;
 import com.android.tools.r8.utils.BooleanBox;
+import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.IntBox;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.InternalOptions.CallSiteOptimizationOptions;
@@ -84,45 +85,63 @@
   static class AllowedPrototypeChanges {
 
     private static final AllowedPrototypeChanges EMPTY =
-        new AllowedPrototypeChanges(null, Int2ReferenceMaps.emptyMap(), IntSets.EMPTY_SET);
+        new AllowedPrototypeChanges(false, null, Int2ReferenceMaps.emptyMap(), IntSets.EMPTY_SET);
 
+    boolean canBeConvertedToStaticMethod;
     DexType newReturnType;
     Int2ReferenceMap<DexType> newParameterTypes;
     IntSet removableParameterIndices;
 
     AllowedPrototypeChanges(
+        boolean canBeConvertedToStaticMethod,
         DexType newReturnType,
         Int2ReferenceMap<DexType> newParameterTypes,
         IntSet removableParameterIndices) {
+      this.canBeConvertedToStaticMethod = canBeConvertedToStaticMethod;
       this.newReturnType = newReturnType;
       this.newParameterTypes = newParameterTypes;
       this.removableParameterIndices = removableParameterIndices;
     }
 
-    public static AllowedPrototypeChanges create(RewrittenPrototypeDescription prototypeChanges) {
+    public static AllowedPrototypeChanges create(
+        ProgramMethod method, RewrittenPrototypeDescription prototypeChanges) {
       if (prototypeChanges.isEmpty()) {
         return empty();
       }
+      ArgumentInfoCollection argumentInfoCollection = prototypeChanges.getArgumentInfoCollection();
+      boolean canBeConvertedToStaticMethod = argumentInfoCollection.isConvertedToStaticMethod();
+      assert !canBeConvertedToStaticMethod
+          || (method.getDefinition().isInstance()
+              && argumentInfoCollection.getArgumentInfo(0).isRemovedReceiverInfo());
       DexType newReturnType =
           prototypeChanges.hasRewrittenReturnInfo()
               ? prototypeChanges.getRewrittenReturnInfo().getNewType()
               : null;
       Int2ReferenceMap<DexType> newParameterTypes = new Int2ReferenceOpenHashMap<>();
       IntSet removableParameterIndices = new IntOpenHashSet();
-      prototypeChanges
-          .getArgumentInfoCollection()
-          .forEach(
-              (argumentIndex, argumentInfo) -> {
-                if (argumentInfo.isRemovedArgumentInfo()) {
-                  removableParameterIndices.add(argumentIndex);
-                } else {
-                  assert argumentInfo.isRewrittenTypeInfo();
-                  RewrittenTypeInfo rewrittenTypeInfo = argumentInfo.asRewrittenTypeInfo();
-                  newParameterTypes.put(argumentIndex, rewrittenTypeInfo.getNewType());
-                }
-              });
+      argumentInfoCollection.forEach(
+          (argumentIndex, argumentInfo) -> {
+            if (argumentInfo.isRemovedReceiverInfo()) {
+              // Skip as the receiver is not in the proto.
+              assert canBeConvertedToStaticMethod;
+            } else {
+              int parameterIndex =
+                  method.getDefinition().getParameterIndexFromArgumentIndex(argumentIndex);
+              assert parameterIndex >= 0;
+              if (argumentInfo.isRemovedArgumentInfo()) {
+                removableParameterIndices.add(parameterIndex);
+              } else {
+                assert argumentInfo.isRewrittenTypeInfo();
+                RewrittenTypeInfo rewrittenTypeInfo = argumentInfo.asRewrittenTypeInfo();
+                newParameterTypes.put(parameterIndex, rewrittenTypeInfo.getNewType());
+              }
+            }
+          });
       return new AllowedPrototypeChanges(
-          newReturnType, newParameterTypes, removableParameterIndices);
+          canBeConvertedToStaticMethod,
+          newReturnType,
+          newParameterTypes,
+          removableParameterIndices);
     }
 
     public static AllowedPrototypeChanges empty() {
@@ -131,7 +150,11 @@
 
     @Override
     public int hashCode() {
-      return Objects.hash(newReturnType, newParameterTypes, removableParameterIndices);
+      return Objects.hash(
+          canBeConvertedToStaticMethod,
+          newReturnType,
+          newParameterTypes,
+          removableParameterIndices);
     }
 
     @Override
@@ -140,7 +163,8 @@
         return false;
       }
       AllowedPrototypeChanges other = (AllowedPrototypeChanges) obj;
-      return newReturnType == other.newReturnType
+      return canBeConvertedToStaticMethod == other.canBeConvertedToStaticMethod
+          && newReturnType == other.newReturnType
           && newParameterTypes.equals(other.newParameterTypes)
           && removableParameterIndices.equals(other.removableParameterIndices);
     }
@@ -359,10 +383,11 @@
 
             // Find the parameters that are either (i) the same constant, (ii) all unused, or (iii)
             // all possible to strengthen to the same stronger type, in all methods.
+            boolean canBeConvertedToStaticMethod = canRemoveReceiverFromVirtualMethods(methods);
             Int2ReferenceMap<DexType> newParameterTypes = new Int2ReferenceOpenHashMap<>();
             IntSet removableVirtualMethodParametersInAllMethods = new IntArraySet();
             for (int parameterIndex = 0;
-                parameterIndex < signature.getProto().getArity() + 1;
+                parameterIndex < signature.getParameters().size();
                 parameterIndex++) {
               if (canRemoveParameterFromVirtualMethods(methods, parameterIndex)) {
                 removableVirtualMethodParametersInAllMethods.add(parameterIndex);
@@ -380,12 +405,14 @@
                 getReturnValueForVirtualMethods(methods, signature);
             DexType newReturnType =
                 getNewReturnTypeForVirtualMethods(methods, returnValueForVirtualMethods);
-            if (newReturnType != null
+            if (canBeConvertedToStaticMethod
+                || newReturnType != null
                 || !newParameterTypes.isEmpty()
                 || !removableVirtualMethodParametersInAllMethods.isEmpty()) {
               allowedPrototypeChangesForVirtualMethods.put(
                   signature,
                   new AllowedPrototypeChanges(
+                      canBeConvertedToStaticMethod,
                       newReturnType,
                       newParameterTypes,
                       removableVirtualMethodParametersInAllMethods));
@@ -467,28 +494,30 @@
       return false;
     }
 
+    private boolean canRemoveReceiverFromVirtualMethods(ProgramMethodSet methods) {
+      if (methods.size() > 1) {
+        // Method staticizing would break dynamic dispatch.
+        return false;
+      }
+      ProgramMethod method = methods.getFirst();
+      return method.getOptimizationInfo().hasUnusedArguments()
+          && method.getOptimizationInfo().getUnusedArguments().get(0)
+          && ParameterRemovalUtils.canRemoveUnusedParametersFrom(appView, method)
+          && ParameterRemovalUtils.canRemoveUnusedParameter(appView, method, 0);
+    }
+
     private boolean canRemoveParameterFromVirtualMethods(
         ProgramMethodSet methods, int parameterIndex) {
-      if (parameterIndex == 0) {
-        if (methods.size() > 1) {
-          // Method staticizing would break dynamic dispatch.
-          return false;
-        }
-        ProgramMethod method = methods.getFirst();
-        return method.getOptimizationInfo().hasUnusedArguments()
-            && method.getOptimizationInfo().getUnusedArguments().get(parameterIndex)
-            && ParameterRemovalUtils.canRemoveUnusedParametersFrom(appView, method)
-            && ParameterRemovalUtils.canRemoveUnusedParameter(appView, method, parameterIndex);
-      }
+      int argumentIndex = parameterIndex + 1;
       for (ProgramMethod method : methods) {
         if (method.getDefinition().isAbstract()) {
           // OK, this parameter can be removed.
           continue;
         }
         if (method.getOptimizationInfo().hasUnusedArguments()
-            && method.getOptimizationInfo().getUnusedArguments().get(parameterIndex)
+            && method.getOptimizationInfo().getUnusedArguments().get(argumentIndex)
             && ParameterRemovalUtils.canRemoveUnusedParametersFrom(appView, method)
-            && ParameterRemovalUtils.canRemoveUnusedParameter(appView, method, parameterIndex)) {
+            && ParameterRemovalUtils.canRemoveUnusedParameter(appView, method, argumentIndex)) {
           // OK, this parameter is unused.
           continue;
         }
@@ -497,7 +526,7 @@
           ConcreteCallSiteOptimizationInfo concreteOptimizationInfo =
               optimizationInfo.asConcreteCallSiteOptimizationInfo();
           AbstractValue abstractValue =
-              concreteOptimizationInfo.getAbstractArgumentValue(parameterIndex);
+              concreteOptimizationInfo.getAbstractArgumentValue(argumentIndex);
           if (abstractValue.isSingleValue()
               && abstractValue.asSingleValue().isMaterializableInContext(appView, method)) {
             // OK, this parameter has a constant value and can be removed.
@@ -544,9 +573,6 @@
 
     private DexType getNewParameterTypeForVirtualMethods(
         ProgramMethodSet methods, int parameterIndex) {
-      if (parameterIndex == 0) {
-        return null;
-      }
       DexType newParameterType = null;
       for (ProgramMethod method : methods) {
         if (method.getAccessFlags().isAbstract()) {
@@ -561,7 +587,7 @@
         newParameterType = newParameterTypeForMethod;
       }
       assert newParameterType == null
-          || newParameterType != methods.getFirst().getArgumentType(parameterIndex);
+          || newParameterType != methods.getFirst().getParameter(parameterIndex);
       return newParameterType;
     }
 
@@ -734,7 +760,7 @@
         ProgramMethod method, RewrittenPrototypeDescription prototypeChanges) {
       DexMethodSignature methodSignatureWithoutPrototypeChanges = method.getMethodSignature();
       AllowedPrototypeChanges allowedPrototypeChanges =
-          AllowedPrototypeChanges.create(prototypeChanges);
+          AllowedPrototypeChanges.create(method, prototypeChanges);
 
       // Check if there is a reserved signature for this already.
       DexMethodSignature reservedSignature =
@@ -742,6 +768,9 @@
               .getOrDefault(methodSignatureWithoutPrototypeChanges, Collections.emptyMap())
               .get(allowedPrototypeChanges);
       if (reservedSignature != null) {
+        assert reservedSignature
+            .getProto()
+            .equals(prototypeChanges.rewriteMethod(method, dexItemFactory).getProto());
         return reservedSignature.withHolder(method.getHolderType(), dexItemFactory);
       }
 
@@ -882,14 +911,16 @@
             removableParameterIndices);
       }
 
+      boolean canBeConvertedToStaticMethod = allowedPrototypeChanges.canBeConvertedToStaticMethod;
       RewrittenPrototypeDescription prototypeChanges =
           computePrototypeChangesForMethod(
               method,
+              canBeConvertedToStaticMethod,
               allowedPrototypeChanges.newReturnType,
               newParameterTypes::get,
               removableParameterIndices::contains);
       assert prototypeChanges.getArgumentInfoCollection().numberOfRemovedArguments()
-          == removableParameterIndices.size();
+          == BooleanUtils.intValue(canBeConvertedToStaticMethod) + removableParameterIndices.size();
       return prototypeChanges;
     }
 
@@ -902,24 +933,26 @@
       ArgumentInfoCollection.Builder argumentInfoCollectionBuilder =
           ArgumentInfoCollection.builder()
               .setArgumentInfosSize(method.getDefinition().getNumberOfArguments());
-      for (int argumentIndex = 0;
-          argumentIndex < method.getDefinition().getNumberOfArguments();
-          argumentIndex++) {
-        if (removableParameterIndices.contains(argumentIndex)) {
-          argumentInfoCollectionBuilder.addArgumentInfo(
-              argumentIndex,
-              RemovedArgumentInfo.builder().setType(method.getArgumentType(argumentIndex)).build());
-        } else if (newParameterTypes.containsKey(argumentIndex)) {
-          DexType newParameterType = newParameterTypes.get(argumentIndex);
-          argumentInfoCollectionBuilder.addArgumentInfo(
-              argumentIndex,
-              RewrittenTypeInfo.builder()
-                  .setCastType(newParameterType)
-                  .setOldType(method.getArgumentType(argumentIndex))
-                  .setNewType(newParameterType)
-                  .build());
-        }
-      }
+      method
+          .getParameters()
+          .forEach(
+              (parameterIndex, parameter) -> {
+                int argumentIndex =
+                    method.getDefinition().getArgumentIndexFromParameterIndex(parameterIndex);
+                if (removableParameterIndices.contains(parameterIndex)) {
+                  argumentInfoCollectionBuilder.addArgumentInfo(
+                      argumentIndex, RemovedArgumentInfo.builder().setType(parameter).build());
+                } else if (newParameterTypes.containsKey(parameterIndex)) {
+                  DexType newParameterType = newParameterTypes.get(parameterIndex);
+                  argumentInfoCollectionBuilder.addArgumentInfo(
+                      argumentIndex,
+                      RewrittenTypeInfo.builder()
+                          .setCastType(newParameterType)
+                          .setOldType(parameter)
+                          .setNewType(newParameterType)
+                          .build());
+                }
+              });
       return RewrittenPrototypeDescription.create(
           Collections.emptyList(),
           computeReturnChangesForMethod(method, newReturnType),
@@ -932,7 +965,11 @@
               ? parameterIndex -> getNewParameterType(method, parameterIndex)
               : parameterIndex -> null;
       return computePrototypeChangesForMethod(
-          method, getNewReturnType(method), parameterIndexToParameterType, parameterIndex -> true);
+          method,
+          true,
+          getNewReturnType(method),
+          parameterIndexToParameterType,
+          parameterIndex -> true);
     }
 
     private DexType getNewReturnType(ProgramMethod method) {
@@ -996,7 +1033,6 @@
       } else {
         returnValue = method.getOptimizationInfo().getAbstractReturnValue();
       }
-
       return returnValue.isSingleValue()
               && returnValue.asSingleValue().isMaterializableInAllContexts(appView)
           ? returnValue.asSingleValue()
@@ -1007,12 +1043,13 @@
       if (!appView.getKeepInfo(method).isParameterTypeStrengtheningAllowed(options)) {
         return null;
       }
-      DexType staticType = method.getArgumentType(parameterIndex);
+      DexType staticType = method.getParameter(parameterIndex);
       if (!staticType.isClassType()) {
         return null;
       }
+      int argumentIndex = method.getDefinition().getArgumentIndexFromParameterIndex(parameterIndex);
       DynamicType dynamicType =
-          method.getOptimizationInfo().getArgumentInfos().getDynamicType(parameterIndex);
+          method.getOptimizationInfo().getArgumentInfos().getDynamicType(argumentIndex);
       if (dynamicType == null || dynamicType.isUnknown()) {
         return null;
       }
@@ -1046,24 +1083,27 @@
 
     private RewrittenPrototypeDescription computePrototypeChangesForMethod(
         ProgramMethod method,
+        boolean canBeConvertedToStaticMethod,
         DexType newReturnType,
         IntFunction<DexType> newParameterTypes,
         IntPredicate removableParameterIndices) {
       return RewrittenPrototypeDescription.create(
           Collections.emptyList(),
           computeReturnChangesForMethod(method, newReturnType),
-          computeParameterChangesForMethod(method, newParameterTypes, removableParameterIndices));
+          computeParameterChangesForMethod(
+              method, canBeConvertedToStaticMethod, newParameterTypes, removableParameterIndices));
     }
 
     private ArgumentInfoCollection computeParameterChangesForMethod(
         ProgramMethod method,
+        boolean canBeConvertedToStaticMethod,
         IntFunction<DexType> newParameterTypes,
         IntPredicate removableParameterIndices) {
       ArgumentInfoCollection.Builder parameterChangesBuilder =
           ArgumentInfoCollection.builder()
               .setArgumentInfosSize(method.getDefinition().getNumberOfArguments());
       if (method.getDefinition().isInstance()
-          && removableParameterIndices.test(0)
+          && canBeConvertedToStaticMethod
           && method.getOptimizationInfo().hasUnusedArguments()
           && method.getOptimizationInfo().getUnusedArguments().get(0)
           && ParameterRemovalUtils.canRemoveUnusedParametersFrom(appView, method)
@@ -1075,19 +1115,19 @@
       }
 
       CallSiteOptimizationInfo optimizationInfo = method.getOptimizationInfo().getArgumentInfos();
-      for (int argumentIndex = method.getDefinition().getFirstNonReceiverArgumentIndex();
-          argumentIndex < method.getDefinition().getNumberOfArguments();
-          argumentIndex++) {
-        if (removableParameterIndices.test(argumentIndex)) {
+      for (int parameterIndex = 0;
+          parameterIndex < method.getParameters().size();
+          parameterIndex++) {
+        int argumentIndex =
+            parameterIndex + method.getDefinition().getFirstNonReceiverArgumentIndex();
+        if (removableParameterIndices.test(parameterIndex)) {
           if (method.getOptimizationInfo().hasUnusedArguments()
               && method.getOptimizationInfo().getUnusedArguments().get(argumentIndex)
               && ParameterRemovalUtils.canRemoveUnusedParametersFrom(appView, method)
               && ParameterRemovalUtils.canRemoveUnusedParameter(appView, method, argumentIndex)) {
             parameterChangesBuilder.addArgumentInfo(
                 argumentIndex,
-                RemovedArgumentInfo.builder()
-                    .setType(method.getArgumentType(argumentIndex))
-                    .build());
+                RemovedArgumentInfo.builder().setType(method.getParameter(parameterIndex)).build());
             continue;
           }
 
@@ -1098,15 +1138,15 @@
                 argumentIndex,
                 RemovedArgumentInfo.builder()
                     .setSingleValue(abstractValue.asSingleValue())
-                    .setType(method.getArgumentType(argumentIndex))
+                    .setType(method.getParameter(parameterIndex))
                     .build());
             continue;
           }
         }
 
-        DexType dynamicType = newParameterTypes.apply(argumentIndex);
+        DexType dynamicType = newParameterTypes.apply(parameterIndex);
         if (dynamicType != null) {
-          DexType staticType = method.getArgumentType(argumentIndex);
+          DexType staticType = method.getParameter(parameterIndex);
           assert dynamicType != staticType;
           parameterChangesBuilder.addArgumentInfo(
               argumentIndex,
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 4d9598e..1008d80 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -4981,7 +4981,7 @@
     }
 
     DexType instantiatedType =
-        ConstantValueUtils.getDexTypeRepresentedByValue(
+        ConstantValueUtils.getDexTypeRepresentedByValueForTracing(
             invoke.asInvokeVirtual().getReceiver(), appView);
     if (instantiatedType == null || !instantiatedType.isClassType()) {
       // Give up, we can't tell which class is being instantiated, or the type is not a class type.
@@ -5027,7 +5027,7 @@
     }
 
     DexType instantiatedType =
-        ConstantValueUtils.getDexTypeRepresentedByValue(
+        ConstantValueUtils.getDexTypeRepresentedByValueForTracing(
             constructorDefinition.getReceiver(), appView);
     if (instantiatedType == null || !instantiatedType.isClassType()) {
       // Give up, we can't tell which constructor is being invoked, or the type is not a class type.
@@ -5076,7 +5076,8 @@
           }
 
           DexType type =
-              ConstantValueUtils.getDexTypeRepresentedByValue(arrayPutInstruction.value(), appView);
+              ConstantValueUtils.getDexTypeRepresentedByValueForTracing(
+                  arrayPutInstruction.value(), appView);
           if (type == null) {
             return;
           }
@@ -5131,7 +5132,8 @@
       }
 
       ArrayPut arrayPut = user.asArrayPut();
-      DexType type = ConstantValueUtils.getDexTypeRepresentedByValue(arrayPut.value(), appView);
+      DexType type =
+          ConstantValueUtils.getDexTypeRepresentedByValueForTracing(arrayPut.value(), appView);
       if (type == null || !type.isClassType()) {
         continue;
       }
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
index 6065527..0d767c6 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
@@ -676,7 +676,11 @@
     // actually equal.
     GraphLens graphLens = appView.graphLens();
     boolean includeContext =
-        intermediate || appView.options().getStartupOptions().isStartupInstrumentationEnabled();
+        intermediate
+            || appView
+                .options()
+                .getStartupInstrumentationOptions()
+                .isStartupInstrumentationEnabled();
     List<T> sortedPotentialMembers =
         ListUtils.sort(
             potentialEquivalence,
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java b/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java
index 6bec653..cbad6e6 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java
@@ -43,6 +43,7 @@
   S(31),
   Sv2(32),
   T(33),
+  MASTER(34), // API level for master is tentative.
   ANDROID_PLATFORM(10000);
 
   // When updating LATEST and a new version goes stable, add a new api-versions.xml to third_party
@@ -166,6 +167,8 @@
         return Sv2;
       case 33:
         return T;
+      case 34:
+        return MASTER;
       case 10000:
         return ANDROID_PLATFORM;
       default:
diff --git a/src/main/java/com/android/tools/r8/utils/DexVersion.java b/src/main/java/com/android/tools/r8/utils/DexVersion.java
index ac962fa..492c87b 100644
--- a/src/main/java/com/android/tools/r8/utils/DexVersion.java
+++ b/src/main/java/com/android/tools/r8/utils/DexVersion.java
@@ -41,6 +41,7 @@
         // ANDROID_PLATFORM is an unknown higher api version we therefore choose the highest known
         // version.
       case ANDROID_PLATFORM:
+      case MASTER:
       case T:
       case Sv2:
       case S:
diff --git a/src/main/java/com/android/tools/r8/utils/FileUtils.java b/src/main/java/com/android/tools/r8/utils/FileUtils.java
index ef26890..b8b8286 100644
--- a/src/main/java/com/android/tools/r8/utils/FileUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/FileUtils.java
@@ -216,6 +216,8 @@
     // own Input/OutputStream, but an external one which one can define on a different Charset than
     // default. We do not support this at the moment since R8 on dex is used only in tests, and
     // UTF_8 is the default charset used in tests.
-    throw new RuntimeException("R8 can run on dex only with UTF_8 as the default charset.");
+    throw new RuntimeException(
+        "R8 can run on dex only with UTF_8 as the default charset, but the charset used is "
+            + Charset.defaultCharset());
   }
 }
diff --git a/src/main/java/com/android/tools/r8/utils/InternalGlobalSyntheticsProgramProvider.java b/src/main/java/com/android/tools/r8/utils/InternalGlobalSyntheticsProgramProvider.java
index 6b2d828..33edbcb 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalGlobalSyntheticsProgramProvider.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalGlobalSyntheticsProgramProvider.java
@@ -9,7 +9,7 @@
 import com.android.tools.r8.ProgramResourceProvider;
 import com.android.tools.r8.ResourceException;
 import com.android.tools.r8.Version;
-import com.android.tools.r8.origin.ArchiveEntryOrigin;
+import com.android.tools.r8.origin.Origin;
 import com.google.common.io.ByteStreams;
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
@@ -25,6 +25,21 @@
 
 public class InternalGlobalSyntheticsProgramProvider implements ProgramResourceProvider {
 
+  public static class GlobalsEntryOrigin extends Origin {
+
+    private final String entryName;
+
+    public GlobalsEntryOrigin(String entryName, Origin parent) {
+      super(parent);
+      this.entryName = entryName;
+    }
+
+    @Override
+    public String part() {
+      return "global(" + entryName + ")";
+    }
+  }
+
   private final List<GlobalSyntheticsResourceProvider> providers;
   private List<ProgramResource> resources = null;
 
@@ -69,7 +84,7 @@
                       + Version.getVersionString());
             }
           } else if (name.endsWith(FileUtils.GLOBAL_SYNTHETIC_EXTENSION) && seen.add(name)) {
-            ArchiveEntryOrigin origin = new ArchiveEntryOrigin(name, provider.getOrigin());
+            GlobalsEntryOrigin origin = new GlobalsEntryOrigin(name, provider.getOrigin());
             String descriptor = guessTypeDescriptor(name);
             byte[] bytes = ByteStreams.toByteArray(stream);
             Set<String> descriptors = Collections.singleton(descriptor);
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 4653b96..d784613 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -39,6 +39,7 @@
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
 import com.android.tools.r8.experimental.startup.StartupOptions;
+import com.android.tools.r8.experimental.startup.instrumentation.StartupInstrumentationOptions;
 import com.android.tools.r8.features.FeatureSplitConfiguration;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
@@ -816,6 +817,8 @@
   private final ApiModelTestingOptions apiModelTestingOptions = new ApiModelTestingOptions();
   private final DesugarSpecificOptions desugarSpecificOptions = new DesugarSpecificOptions();
   private final StartupOptions startupOptions = new StartupOptions();
+  private final StartupInstrumentationOptions startupInstrumentationOptions =
+      new StartupInstrumentationOptions();
   public final TestingOptions testing = new TestingOptions();
 
   public List<ProguardConfigurationRule> mainDexKeepRules = ImmutableList.of();
@@ -879,6 +882,10 @@
     return startupOptions;
   }
 
+  public StartupInstrumentationOptions getStartupInstrumentationOptions() {
+    return startupInstrumentationOptions;
+  }
+
   public TestingOptions getTestingOptions() {
     return testing;
   }
@@ -1504,6 +1511,7 @@
     private boolean enableInterfaceMerging =
         System.getProperty("com.android.tools.r8.enableHorizontalInterfaceMerging") != null;
     private boolean enableInterfaceMergingInInitial = false;
+    private boolean enableSameFilePolicy = false;
     private boolean enableSyntheticMerging = true;
     private boolean ignoreRuntimeTypeChecksForTesting = false;
     private boolean restrictToSynthetics = false;
@@ -1559,6 +1567,10 @@
       return ignoreRuntimeTypeChecksForTesting;
     }
 
+    public boolean isSameFilePolicyEnabled() {
+      return enableSameFilePolicy;
+    }
+
     public boolean isSyntheticMergingEnabled() {
       return enableSyntheticMerging;
     }
@@ -1586,10 +1598,18 @@
       enableInterfaceMerging = true;
     }
 
+    public void setEnableInterfaceMerging(boolean enableInterfaceMerging) {
+      this.enableInterfaceMerging = enableInterfaceMerging;
+    }
+
     public void setEnableInterfaceMergingInInitial() {
       enableInterfaceMergingInInitial = true;
     }
 
+    public void setEnableSameFilePolicy(boolean enableSameFilePolicy) {
+      this.enableSameFilePolicy = enableSameFilePolicy;
+    }
+
     public void setIgnoreRuntimeTypeChecksForTesting() {
       ignoreRuntimeTypeChecksForTesting = true;
     }
@@ -1776,6 +1796,10 @@
     private boolean hasReadCheckDeterminism = false;
     private DeterminismChecker determinismChecker = null;
 
+    // Testing options to analyse locality of items in DEX files when they are generated.
+    public boolean calculateItemUseCountInDex = false;
+    public boolean calculateItemUseCountInDexDumpSingleUseStrings = false;
+
     private DeterminismChecker getDeterminismChecker() {
       // Lazily read the env-var so that it can be set after options init.
       if (determinismChecker == null && !hasReadCheckDeterminism) {
diff --git a/src/main/java/com/android/tools/r8/utils/ProgramClassCollection.java b/src/main/java/com/android/tools/r8/utils/ProgramClassCollection.java
index a5bbfcb..920b83c 100644
--- a/src/main/java/com/android/tools/r8/utils/ProgramClassCollection.java
+++ b/src/main/java/com/android/tools/r8/utils/ProgramClassCollection.java
@@ -9,6 +9,7 @@
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.InternalGlobalSyntheticsProgramProvider.GlobalsEntryOrigin;
 import com.google.common.collect.ImmutableList;
 import java.util.List;
 import java.util.concurrent.ConcurrentHashMap;
@@ -60,13 +61,7 @@
   public static ProgramClassConflictResolver defaultConflictResolver(Reporter reporter) {
     // The default conflict resolver only merges synthetic classes generated by D8 correctly.
     // All other conflicts are reported as a fatal error.
-    return (DexProgramClass a, DexProgramClass b) -> {
-      assert a.type == b.type;
-      if (a.accessFlags.isSynthetic() && b.accessFlags.isSynthetic()) {
-        return mergeClasses(reporter, a, b);
-      }
-      throw reportDuplicateTypes(reporter, a, b);
-    };
+    return (DexProgramClass a, DexProgramClass b) -> mergeClasses(reporter, a, b);
   }
 
   private static RuntimeException reportDuplicateTypes(
@@ -79,6 +74,31 @@
 
   private static DexProgramClass mergeClasses(
       Reporter reporter, DexProgramClass a, DexProgramClass b) {
+    assert a.type == b.type;
+    boolean syntheticA = a.accessFlags.isSynthetic();
+    boolean syntheticB = b.accessFlags.isSynthetic();
+    if (syntheticA && syntheticB) {
+      return mergeIfLegacySynthetics(reporter, a, b);
+    } else if (syntheticA) {
+      return mergeIfGlobalSynthetic(reporter, a, b);
+    } else if (syntheticB) {
+      return mergeIfGlobalSynthetic(reporter, b, a);
+    }
+    throw reportDuplicateTypes(reporter, a, b);
+  }
+
+  private static DexProgramClass mergeIfGlobalSynthetic(
+      Reporter reporter, DexProgramClass synthetic, DexProgramClass nonSynthetic) {
+    assert synthetic.accessFlags.isSynthetic();
+    assert !nonSynthetic.accessFlags.isSynthetic();
+    if (synthetic.getOrigin() instanceof GlobalsEntryOrigin) {
+      return nonSynthetic;
+    }
+    throw reportDuplicateTypes(reporter, nonSynthetic, synthetic);
+  }
+
+  private static DexProgramClass mergeIfLegacySynthetics(
+      Reporter reporter, DexProgramClass a, DexProgramClass b) {
     if (a.type.isLegacySynthesizedTypeAllowedDuplication()) {
       assert assertEqualClasses(a, b);
       return a;
diff --git a/src/main/java/com/android/tools/r8/utils/SegmentTree.java b/src/main/java/com/android/tools/r8/utils/SegmentTree.java
index 1fc95b0..9962b1c 100644
--- a/src/main/java/com/android/tools/r8/utils/SegmentTree.java
+++ b/src/main/java/com/android/tools/r8/utils/SegmentTree.java
@@ -6,9 +6,10 @@
 
 import java.util.Map;
 import java.util.TreeMap;
+import java.util.function.Consumer;
 
 /**
- * Implementation of a discrete segment tree where intervals are specified by their start end and
+ * Implementation of a discrete segment tree where intervals are specified by their start and end
  * point. Both points are considered part of the interval.
  */
 public class SegmentTree<V> {
@@ -79,4 +80,21 @@
   public int size() {
     return size;
   }
+
+  public void removeSegment(int start) {
+    if (internalTree.remove(start) != null) {
+      size = size - 1;
+    }
+  }
+
+  public void visitSegments(Consumer<V> consumer) {
+    internalTree
+        .values()
+        .forEach(
+            segment -> {
+              if (segment != null) {
+                consumer.accept(segment);
+              }
+            });
+  }
 }
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 7275934..f1e4e18 100644
--- a/src/main/java/com/android/tools/r8/utils/StringUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/StringUtils.java
@@ -155,9 +155,13 @@
   }
 
   public static String lines(List<String> lines) {
+    return lines(lines, LINE_SEPARATOR);
+  }
+
+  private static String lines(List<String> lines, String lineSeperator) {
     StringBuilder builder = new StringBuilder();
     for (String line : lines) {
-      builder.append(line).append(LINE_SEPARATOR);
+      builder.append(line).append(lineSeperator);
     }
     return builder.toString();
   }
@@ -166,6 +170,10 @@
     return lines(Arrays.asList(lines));
   }
 
+  public static String unixLines(String... lines) {
+    return lines(Arrays.asList(lines), "\n");
+  }
+
   public static String withNativeLineSeparator(String s) {
     s = s.replace("\r\n", "\n");
     if (LINE_SEPARATOR.equals("\r\n")) {
diff --git a/src/main/java/com/android/tools/r8/utils/SystemPropertyUtils.java b/src/main/java/com/android/tools/r8/utils/SystemPropertyUtils.java
index d8d4a38..a984560 100644
--- a/src/main/java/com/android/tools/r8/utils/SystemPropertyUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/SystemPropertyUtils.java
@@ -7,10 +7,21 @@
 import static com.google.common.base.Predicates.alwaysTrue;
 
 import com.android.tools.r8.Version;
+import java.util.function.Function;
 import java.util.function.Predicate;
+import java.util.function.Supplier;
 
 public class SystemPropertyUtils {
 
+  public static <T> T applySystemProperty(
+      String propertyName, Function<String, T> propertyFoundFn, Supplier<T> propertyNotFoundFn) {
+    if (isSystemPropertySet(propertyName)) {
+      return propertyFoundFn.apply(System.getProperty(propertyName));
+    } else {
+      return propertyNotFoundFn.get();
+    }
+  }
+
   public static String getSystemPropertyForDevelopment(String propertyName) {
     return Version.isDevelopmentVersion() ? System.getProperty(propertyName) : null;
   }
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 94288b4..b4519a9 100644
--- a/src/main/java/com/android/tools/r8/utils/Timing.java
+++ b/src/main/java/com/android/tools/r8/utils/Timing.java
@@ -299,6 +299,7 @@
               Node mergeTarget =
                   item.mergeTarget.children.computeIfAbsent(title, t -> new Node(t, trackMemory));
               mergeTarget.duration += child.duration;
+              mergeTarget.endMemory = child.endMemory;
               if (!child.children.isEmpty()) {
                 worklist.addLast(new Item(mergeTarget, child));
               }
@@ -308,6 +309,7 @@
 
     public void end() {
       assert !parent.children.containsKey(merged.title);
+      merged.end();
       parent.children.put(merged.title, merged);
     }
   }
diff --git a/src/main/java/com/android/tools/r8/utils/ZipUtils.java b/src/main/java/com/android/tools/r8/utils/ZipUtils.java
index 34714e4..fc9157d 100644
--- a/src/main/java/com/android/tools/r8/utils/ZipUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ZipUtils.java
@@ -35,6 +35,7 @@
 import java.util.Spliterator;
 import java.util.Spliterators;
 import java.util.function.BiFunction;
+import java.util.function.Function;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
 import java.util.stream.StreamSupport;
@@ -162,35 +163,39 @@
   }
 
   public static List<Path> unzip(Path zipFile, Path outDirectory) throws IOException {
-    return unzip(zipFile.toString(), outDirectory.toFile(), (entry) -> true).stream()
-        .map(File::toPath)
-        .collect(Collectors.toList());
+    return unzip(zipFile, outDirectory, (entry) -> true, Function.identity());
   }
 
   public static List<File> unzip(String zipFile, File outDirectory) throws IOException {
-    return unzip(zipFile, outDirectory, (entry) -> true);
+    return unzip(Paths.get(zipFile), outDirectory.toPath(), (entry) -> true, Path::toFile);
   }
 
-  public static List<File> unzip(String zipFile, File outDirectory, Predicate<ZipEntry> filter)
+  public static List<Path> unzip(Path zipFile, Path outDirectory, Predicate<ZipEntry> filter)
       throws IOException {
-    final Path outDirectoryPath = outDirectory.toPath();
-    final List<File> outFiles = new ArrayList<>();
-      iter(zipFile, (entry, input) -> {
-        String name = entry.getName();
-        if (!entry.isDirectory() && filter.test(entry)) {
-          if (name.contains("..")) {
-            // Protect against malicious archives.
-            throw new CompilationError("Invalid entry name \"" + name + "\"");
+    return unzip(zipFile, outDirectory, filter, Function.identity());
+  }
+
+  public static <T> List<T> unzip(
+      Path zipFile, Path outDirectory, Predicate<ZipEntry> filter, Function<Path, T> map)
+      throws IOException {
+    final List<T> outFiles = new ArrayList<>();
+    iter(
+        zipFile,
+        (entry, input) -> {
+          String name = entry.getName();
+          if (!entry.isDirectory() && filter.test(entry)) {
+            if (name.contains("..")) {
+              // Protect against malicious archives.
+              throw new CompilationError("Invalid entry name \"" + name + "\"");
+            }
+            Path outPath = outDirectory.resolve(name);
+            outPath.toFile().getParentFile().mkdirs();
+            try (OutputStream output = new FileOutputStream(outPath.toFile())) {
+              ByteStreams.copy(input, output);
+            }
+            outFiles.add(map.apply(outPath));
           }
-          Path outPath = outDirectoryPath.resolve(name);
-          File outFile = outPath.toFile();
-          outFile.getParentFile().mkdirs();
-          try (OutputStream output = new FileOutputStream(outFile)) {
-            ByteStreams.copy(input, output);
-          }
-          outFiles.add(outFile);
-        }
-      });
+        });
     return outFiles;
   }
 
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java
index 3bdfa4b..824203a 100644
--- a/src/test/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -346,6 +346,19 @@
     return self();
   }
 
+  public T allowUnusedDontWarnJavaLangClassValue() {
+    addOptionsModification(
+        options -> options.testing.allowedUnusedDontWarnPatterns.add("java.lang.ClassValue"));
+    return self();
+  }
+
+  public T allowUnusedDontWarnJavaLangClassValue(boolean condition) {
+    if (condition) {
+      allowUnusedDontWarnJavaLangClassValue();
+    }
+    return self();
+  }
+
   public T allowUnusedDontWarnPatterns() {
     return addOptionsModification(options -> options.testing.allowUnusedDontWarnRules = true);
   }
diff --git a/src/test/java/com/android/tools/r8/R8TestCompileResult.java b/src/test/java/com/android/tools/r8/R8TestCompileResult.java
index f343c0f..be01cef 100644
--- a/src/test/java/com/android/tools/r8/R8TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/R8TestCompileResult.java
@@ -112,18 +112,10 @@
   }
 
   @SafeVarargs
+  @Override
   public final <E extends Throwable> R8TestCompileResult inspectMultiDex(
       ThrowingConsumer<CodeInspector, E>... consumers) throws IOException, E {
-    Path out = state.getNewTempFolder();
-    getApp().writeToDirectory(out, OutputMode.DexIndexed);
-    consumers[0].accept(new CodeInspector(out.resolve("classes.dex"), getProguardMap()));
-    for (int i = 1; i < consumers.length; i++) {
-      Path dex = out.resolve("classes" + (i + 1) + ".dex");
-      CodeInspector inspector =
-          dex.toFile().exists() ? new CodeInspector(dex, getProguardMap()) : CodeInspector.empty();
-      consumers[i].accept(inspector);
-    }
-    return self();
+    return inspectMultiDex(writeProguardMap(), consumers);
   }
 
   public final <E extends Throwable> R8TestCompileResult inspectGraph(
diff --git a/src/test/java/com/android/tools/r8/SoftVerificationErrorJarRunner.java b/src/test/java/com/android/tools/r8/SoftVerificationErrorJarRunner.java
index 80a2879..b345c16 100644
--- a/src/test/java/com/android/tools/r8/SoftVerificationErrorJarRunner.java
+++ b/src/test/java/com/android/tools/r8/SoftVerificationErrorJarRunner.java
@@ -64,8 +64,8 @@
     File filteredProgramFolder = temp.newFolder();
     BooleanBox seenTestRunner = new BooleanBox();
     ZipUtils.unzip(
-        tempFolder.resolve("program.jar").toFile().toString(),
-        filteredProgramFolder,
+        tempFolder.resolve("program.jar"),
+        filteredProgramFolder.toPath(),
         zipEntry -> {
           if (zipEntry.getName().equals("com/example/softverificationsample/TestRunner.class")) {
             seenTestRunner.set();
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index 98f1627..4f8181d 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -2052,8 +2052,13 @@
           String[] lines1 = m1.getCode().toString().split("\n");
           String[] lines2 = m2.getCode().toString().split("\n");
           if (lines1.length != lines2.length) {
-            sb.append("Different number of lines.");
-            sb.append("\n");
+            sb.append("Different number of lines: ")
+                .append(lines1.length)
+                .append(" and ")
+                .append(lines2.length)
+                .append(".")
+                .append("\n");
+            printDiffForDifferentLineNumber(sb, lines1, lines2);
           } else {
             for (int i = 0; i < lines1.length; i++) {
               if (!lines1[i].equals(lines2[i])) {
@@ -2069,6 +2074,38 @@
     return sb.toString();
   }
 
+  // Identify the initial common lines and print the first 7 lines after the first difference.
+  private static void printDiffForDifferentLineNumber(
+      StringBuilder sb, String[] lines1, String[] lines2) {
+    int maxPrint = 7;
+    int i;
+    for (i = 0; i < lines1.length && i < lines2.length; i++) {
+      if (!lines1[i].equals(lines2[i])) {
+        break;
+      }
+    }
+    sb.append("Method 1:").append("\n");
+    printExtraLines(i, lines1, maxPrint, sb);
+    sb.append("Method 2:").append("\n");
+    printExtraLines(i, lines2, maxPrint, sb);
+  }
+
+  private static void printExtraLines(int i, String[] lines1, int maxPrint, StringBuilder sb) {
+    if (i < lines1.length) {
+      int j;
+      for (j = i; j < lines1.length && j < (i + maxPrint); j++) {
+        sb.append(lines1[j]);
+        sb.append("\n");
+      }
+      if (j != lines1.length) {
+        sb.append("...");
+        sb.append("\n");
+      }
+    } else {
+      sb.append("No difference in method.").append("\n");
+    }
+  }
+
   public static boolean filesAreEqual(Path file1, Path file2) throws IOException {
     long size = Files.size(file1);
     long sizeOther = Files.size(file2);
diff --git a/src/test/java/com/android/tools/r8/TestCompileResult.java b/src/test/java/com/android/tools/r8/TestCompileResult.java
index 4d557ea..b15e669 100644
--- a/src/test/java/com/android/tools/r8/TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/TestCompileResult.java
@@ -444,6 +444,27 @@
     return self();
   }
 
+  @SuppressWarnings("unchecked")
+  public <E extends Throwable> CR inspectMultiDex(ThrowingConsumer<CodeInspector, E>... consumers)
+      throws IOException, E {
+    return inspectMultiDex(null, consumers);
+  }
+
+  @SafeVarargs
+  public final <E extends Throwable> CR inspectMultiDex(
+      Path mappingFile, ThrowingConsumer<CodeInspector, E>... consumers) throws IOException, E {
+    Path out = state.getNewTempFolder();
+    getApp().writeToDirectory(out, OutputMode.DexIndexed);
+    consumers[0].accept(new CodeInspector(out.resolve("classes.dex"), mappingFile));
+    for (int i = 1; i < consumers.length; i++) {
+      Path dex = out.resolve("classes" + (i + 1) + ".dex");
+      CodeInspector inspector =
+          dex.toFile().exists() ? new CodeInspector(dex, mappingFile) : CodeInspector.empty();
+      consumers[i].accept(inspector);
+    }
+    return self();
+  }
+
   public <E extends Throwable> CR inspectWithOptions(
       ThrowingConsumer<CodeInspector, E> consumer, Consumer<InternalOptions> debugOptionsConsumer)
       throws IOException, E {
diff --git a/src/test/java/com/android/tools/r8/TestCondition.java b/src/test/java/com/android/tools/r8/TestCondition.java
index b1a6207..e4cec10 100644
--- a/src/test/java/com/android/tools/r8/TestCondition.java
+++ b/src/test/java/com/android/tools/r8/TestCondition.java
@@ -28,6 +28,7 @@
     ART_V12_0_0,
     ART_V13_0_0,
     ART_DEFAULT,
+    ART_MASTER,
     JAVA;
 
     static final Runtime LOWEST_ART_VERSION = ART_V4_0_4;
@@ -57,6 +58,8 @@
           return ART_V13_0_0;
         case DEFAULT:
           return ART_DEFAULT;
+        case MASTER:
+          return ART_MASTER;
         default:
           throw new Unreachable();
       }
diff --git a/src/test/java/com/android/tools/r8/TestParametersBuilder.java b/src/test/java/com/android/tools/r8/TestParametersBuilder.java
index cd34cd5..c4fc8c4 100644
--- a/src/test/java/com/android/tools/r8/TestParametersBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestParametersBuilder.java
@@ -128,11 +128,16 @@
     return withCfRuntimeFilter(vm -> vm.lessThan(endExclusive));
   }
 
-  /** Add all available DEX runtimes. */
-  public TestParametersBuilder withDexRuntimes() {
+  /** Add all available DEX runtimes including master. */
+  public TestParametersBuilder withDexRuntimesIncludingMaster() {
     return withDexRuntimeFilter(vm -> true);
   }
 
+  /** Add all available DEX runtimes except master. */
+  public TestParametersBuilder withDexRuntimes() {
+    return withDexRuntimeFilter(vm -> vm != DexVm.Version.MASTER);
+  }
+
   public TestParametersBuilder withDexRuntimesAndAllApiLevels() {
     return withDexRuntimes().withAllApiLevels();
   }
diff --git a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
index e1de2d3..d28d969 100644
--- a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
@@ -81,6 +81,7 @@
     return self();
   }
 
+  @Deprecated
   public T noMinification() {
     return minification(false);
   }
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 88b929a..d948ad2 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -259,7 +259,9 @@
     ART_12_0_0_TARGET(Version.V12_0_0, Kind.TARGET),
     ART_12_0_0_HOST(Version.V12_0_0, Kind.HOST),
     ART_13_0_0_TARGET(Version.V13_0_0, Kind.TARGET),
-    ART_13_0_0_HOST(Version.V13_0_0, Kind.HOST);
+    ART_13_0_0_HOST(Version.V13_0_0, Kind.HOST),
+    ART_MASTER_TARGET(Version.MASTER, Kind.TARGET),
+    ART_MASTER_HOST(Version.MASTER, Kind.HOST);
 
     private static final ImmutableMap<String, DexVm> SHORT_NAME_MAP =
         Arrays.stream(DexVm.values()).collect(ImmutableMap.toImmutableMap(
@@ -277,7 +279,8 @@
       V9_0_0("9.0.0"),
       V10_0_0("10.0.0"),
       V12_0_0("12.0.0"),
-      V13_0_0("13.0.0");
+      V13_0_0("13.0.0"),
+      MASTER("master");
 
       /** This should generally be the latest DEX VM fully supported. */
       // TODO(b/204855476): Rename to DEFAULT alias once the checked in VM is removed.
@@ -342,10 +345,15 @@
         return V13_0_0;
       }
 
+      public static Version master() {
+        return MASTER;
+      }
+
       static {
-        // Ensure first is always first and last is always last.
+        // Ensure first is always first and last is always last except for master.
         assert Arrays.stream(values()).allMatch(v -> v == first() || v.compareTo(first()) > 0);
-        assert Arrays.stream(values()).allMatch(v -> v == last() || v.compareTo(last()) < 0);
+        assert Arrays.stream(values())
+            .allMatch(v -> v == last() || v == master() || v.compareTo(last()) < 0);
       }
     }
 
@@ -610,6 +618,7 @@
   private static final Map<DexVm, String> ART_DIRS =
       ImmutableMap.<DexVm, String>builder()
           .put(DexVm.ART_DEFAULT, "art")
+          .put(DexVm.ART_MASTER_HOST, "host/art-master")
           .put(DexVm.ART_13_0_0_HOST, "host/art-13-dev")
           .put(DexVm.ART_12_0_0_HOST, "host/art-12.0.0-beta4")
           .put(DexVm.ART_10_0_0_HOST, "art-10.0.0")
@@ -624,6 +633,7 @@
   private static final Map<DexVm, String> ART_BINARY_VERSIONS =
       ImmutableMap.<DexVm, String>builder()
           .put(DexVm.ART_DEFAULT, "bin/art")
+          .put(DexVm.ART_MASTER_HOST, "bin/art")
           .put(DexVm.ART_13_0_0_HOST, "bin/art")
           .put(DexVm.ART_12_0_0_HOST, "bin/art")
           .put(DexVm.ART_10_0_0_HOST, "bin/art")
@@ -854,6 +864,9 @@
   }
 
   private static Path getAndroidJarPath(AndroidApiLevel apiLevel) {
+    if (apiLevel == AndroidApiLevel.MASTER) {
+      return Paths.get("third_party/android_jar/lib-master/android.jar");
+    }
     String jar = String.format(
         ANDROID_JAR_PATTERN,
         (apiLevel == AndroidApiLevel.getDefault() ? DEFAULT_MIN_SDK : apiLevel).getLevel());
@@ -1021,6 +1034,8 @@
 
   public static AndroidApiLevel getMinApiLevelForDexVm(DexVm dexVm) {
     switch (dexVm.version) {
+      case MASTER:
+        return AndroidApiLevel.MASTER;
       case V13_0_0:
         return AndroidApiLevel.T;
       case V12_0_0:
diff --git a/src/test/java/com/android/tools/r8/accessrelaxation/ConstructorRelaxationTest.java b/src/test/java/com/android/tools/r8/accessrelaxation/ConstructorRelaxationTest.java
index 4f91077..f7afbc4 100644
--- a/src/test/java/com/android/tools/r8/accessrelaxation/ConstructorRelaxationTest.java
+++ b/src/test/java/com/android/tools/r8/accessrelaxation/ConstructorRelaxationTest.java
@@ -189,7 +189,7 @@
                   o.inlinerOptions().enableInlining = false;
                   o.enableVerticalClassMerging = false;
                 })
-            .noMinification()
+            .addDontObfuscate()
             .addKeepMainRule(mainClass)
             .allowAccessModification()
             .setMinApi(parameters.getRuntime())
diff --git a/src/test/java/com/android/tools/r8/accessrelaxation/NonConstructorRelaxationTest.java b/src/test/java/com/android/tools/r8/accessrelaxation/NonConstructorRelaxationTest.java
index 29e44c0..aff91c6 100644
--- a/src/test/java/com/android/tools/r8/accessrelaxation/NonConstructorRelaxationTest.java
+++ b/src/test/java/com/android/tools/r8/accessrelaxation/NonConstructorRelaxationTest.java
@@ -86,7 +86,7 @@
             .enableMemberValuePropagationAnnotations()
             .enableUnusedArgumentAnnotations(!enableUnusedArgumentRemoval)
             .addKeepMainRule(mainClass)
-            .noMinification()
+            .addDontObfuscate()
             .addKeepRules(
                 // Note: we use '-checkdiscard' to indirectly check that the access relaxation is
                 // done which leads to inlining of all pB*** methods so they are removed. Without
@@ -172,7 +172,7 @@
             .enableInliningAnnotations()
             .enableMemberValuePropagationAnnotations()
             .enableNoHorizontalClassMergingAnnotations()
-            .noMinification()
+            .addDontObfuscate()
             .addKeepRules(
                 "-checkdiscard class " + Base.class.getCanonicalName() + "{",
                 "  *** p*();",
diff --git a/src/test/java/com/android/tools/r8/androidapi/AndroidJarMasterTest.java b/src/test/java/com/android/tools/r8/androidapi/AndroidJarMasterTest.java
new file mode 100644
index 0000000..6f2a237
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/androidapi/AndroidJarMasterTest.java
@@ -0,0 +1,58 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.androidapi;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+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 AndroidJarMasterTest extends TestBase {
+
+  @Parameter() public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build());
+  }
+
+  private static final String EXPECTED_OUTPUT = StringUtils.lines("Hello, world!");
+
+  @Test
+  public void testD8() throws Exception {
+    testForD8(parameters.getBackend())
+        .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.MASTER))
+        .addInnerClasses(getClass())
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.MASTER))
+        .addInnerClasses(getClass())
+        .addKeepMainRule(TestClass.class)
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  static class TestClass {
+
+    public static void main(String[] args) {
+      System.out.println("Hello, world!");
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/annotations/AnnotationWithInjectedMethodsTest.java b/src/test/java/com/android/tools/r8/annotations/AnnotationWithInjectedMethodsTest.java
index be3ee34..da34ea7 100644
--- a/src/test/java/com/android/tools/r8/annotations/AnnotationWithInjectedMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/annotations/AnnotationWithInjectedMethodsTest.java
@@ -54,7 +54,7 @@
         .addKeepClassAndMembersRules(AnnotationWithInjectedMethod.class)
         .addKeepRuntimeVisibleAnnotations()
         .addOptionsModification(options -> options.testing.allowInjectedAnnotationMethods = true)
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .run(parameters.getRuntime(), Main.class)
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockMergeProgramDefinedDuplicateTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockMergeProgramDefinedDuplicateTest.java
new file mode 100644
index 0000000..0a72589
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockMergeProgramDefinedDuplicateTest.java
@@ -0,0 +1,178 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.apimodel;
+
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.OutputMode;
+import com.android.tools.r8.SingleTestRunResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestCompilerBuilder;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.synthesis.globals.GlobalSyntheticsTestingConsumer;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.nio.file.Path;
+import java.util.ArrayList;
+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 ApiModelMockMergeProgramDefinedDuplicateTest extends TestBase {
+
+  private final AndroidApiLevel mockLevel = AndroidApiLevel.M;
+
+  @Parameter() public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withDexRuntimes().withAllApiLevels().build();
+  }
+
+  private boolean isGreaterOrEqualToMockLevel() {
+    return parameters.isDexRuntime() && parameters.getApiLevel().isGreaterThanOrEqualTo(mockLevel);
+  }
+
+  private void setupTestCompileBuilder(
+      TestCompilerBuilder<?, ?, ?, ?, ?> testBuilder, Class<?> programClass)
+      throws NoSuchMethodException {
+    testBuilder
+        .addProgramClasses(programClass)
+        .addLibraryClasses(LibraryClass.class)
+        .addDefaultRuntimeLibrary(parameters)
+        .setMinApi(parameters.getApiLevel())
+        .apply(ApiModelingTestHelper::enableApiCallerIdentification)
+        .apply(ApiModelingTestHelper::enableStubbingOfClasses)
+        .apply(setMockApiLevelForClass(LibraryClass.class, mockLevel))
+        .apply(setMockApiLevelForDefaultInstanceInitializer(LibraryClass.class, mockLevel))
+        .apply(setMockApiLevelForMethod(LibraryClass.class.getDeclaredMethod("foo"), mockLevel))
+        .apply(setMockApiLevelForMethod(LibraryClass.class.getDeclaredMethod("bar"), mockLevel));
+  }
+
+  private boolean addToBootClasspath() {
+    return parameters.isDexRuntime()
+        && parameters.getRuntime().maxSupportedApiLevel().isGreaterThanOrEqualTo(mockLevel);
+  }
+
+  @Test
+  public void testD8DebugDexFilePerClassFile() throws Exception {
+    testD8Merge(CompilationMode.DEBUG);
+  }
+
+  @Test
+  public void testD8ReleaseDexFilePerClassFile() throws Exception {
+    testD8Merge(CompilationMode.RELEASE);
+  }
+
+  private Path runD8ForClass(
+      Class<?> clazz, GlobalSyntheticsTestingConsumer global, CompilationMode mode)
+      throws Exception {
+    return testForD8()
+        .setMode(mode)
+        .setOutputMode(OutputMode.DexFilePerClassFile)
+        .apply(b -> b.getBuilder().setGlobalSyntheticsConsumer(global))
+        .apply(builder -> setupTestCompileBuilder(builder, clazz))
+        .compile()
+        .writeToZip();
+  }
+
+  public void testD8Merge(CompilationMode mode) throws Exception {
+    List<Path> paths = new ArrayList<>();
+    GlobalSyntheticsTestingConsumer mainGlobals = new GlobalSyntheticsTestingConsumer();
+    GlobalSyntheticsTestingConsumer testCallingFooGlobals = new GlobalSyntheticsTestingConsumer();
+    GlobalSyntheticsTestingConsumer testCallingBarGlobals = new GlobalSyntheticsTestingConsumer();
+    paths.add(runD8ForClass(Main.class, mainGlobals, mode));
+    paths.add(runD8ForClass(TestCallingFoo.class, testCallingFooGlobals, mode));
+    paths.add(runD8ForClass(TestCallingBar.class, testCallingBarGlobals, mode));
+    assertFalse(mainGlobals.hasGlobals());
+    if (isGreaterOrEqualToMockLevel()) {
+      assertFalse(testCallingFooGlobals.hasGlobals());
+      assertFalse(testCallingBarGlobals.hasGlobals());
+    } else {
+      // The TestCallingX does reference the mock and should have globals.
+      assertNotNull(
+          testCallingFooGlobals.getProvider(Reference.classFromClass(TestCallingFoo.class)));
+      assertNotNull(
+          testCallingBarGlobals.getProvider(Reference.classFromClass(TestCallingBar.class)));
+    }
+
+    testForD8()
+        .setMode(mode)
+        .addProgramFiles(paths)
+        // Add the actual LibraryClass definition to the code, thus the globals will need to merge
+        // the synthetic stubs but also disregard them due to a program definition being present.
+        .addProgramClasses(LibraryClass.class)
+        .setMinApi(parameters.getApiLevel())
+        .apply(
+            b ->
+                b.getBuilder()
+                    .addGlobalSyntheticsResourceProviders(testCallingFooGlobals.getProviders())
+                    .addGlobalSyntheticsResourceProviders(testCallingBarGlobals.getProviders()))
+        .compile()
+        .inspect(this::inspect)
+        .applyIf(addToBootClasspath(), b -> b.addBootClasspathClasses(LibraryClass.class))
+        .run(parameters.getRuntime(), Main.class)
+        .apply(this::checkOutput);
+  }
+
+  private void inspect(CodeInspector inspector) {
+    ClassSubject libraryClassSubject = inspector.clazz(LibraryClass.class);
+    assertThat(libraryClassSubject, isPresent());
+    assertThat(libraryClassSubject.uniqueMethodWithName("foo"), isPresent());
+    assertThat(libraryClassSubject.uniqueMethodWithName("bar"), isPresent());
+  }
+
+  private void checkOutput(SingleTestRunResult<?> runResult) {
+    runResult.assertSuccessWithOutputLines("LibraryClass::foo", "LibraryClass::bar");
+  }
+
+  // Only present form api level 23, but also included in program.
+  public static class LibraryClass {
+
+    public void foo() {
+      System.out.println("LibraryClass::foo");
+    }
+
+    public void bar() {
+      System.out.println("LibraryClass::bar");
+    }
+  }
+
+  public static class TestCallingFoo {
+
+    public static void callFoo() {
+      new LibraryClass().foo();
+    }
+  }
+
+  public static class TestCallingBar {
+
+    public static void callBar() {
+      new LibraryClass().bar();
+    }
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) {
+      TestCallingFoo.callFoo();
+      TestCallingBar.callBar();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfDefaultInterfaceMethodsTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfDefaultInterfaceMethodsTest.java
index 420bf8d..9a243d3 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfDefaultInterfaceMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfDefaultInterfaceMethodsTest.java
@@ -52,7 +52,7 @@
         // We are testing that we do not inline/merge higher api-levels
         .apply(ApiModelingTestHelper::disableOutliningAndStubbing)
         .enableNoMethodStaticizingAnnotations()
-        .noMinification()
+        .addDontObfuscate()
         .compile()
         .inspect(
             inspector -> {
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelStaticTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelStaticMethodTest.java
similarity index 94%
rename from src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelStaticTest.java
rename to src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelStaticMethodTest.java
index 1b7713d..72ca50d 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelStaticTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelStaticMethodTest.java
@@ -20,7 +20,7 @@
 import org.junit.runners.Parameterized.Parameters;
 
 @RunWith(Parameterized.class)
-public class ApiModelNoInliningOfHigherApiLevelStaticTest extends TestBase {
+public class ApiModelNoInliningOfHigherApiLevelStaticMethodTest extends TestBase {
 
   private final TestParameters parameters;
 
@@ -29,7 +29,7 @@
     return getTestParameters().withAllRuntimesAndApiLevels().build();
   }
 
-  public ApiModelNoInliningOfHigherApiLevelStaticTest(TestParameters parameters) {
+  public ApiModelNoInliningOfHigherApiLevelStaticMethodTest(TestParameters parameters) {
     this.parameters = parameters;
   }
 
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfStaticInterfaceMethodsTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfStaticInterfaceMethodsTest.java
index 22714a3..9411c7d 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfStaticInterfaceMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfStaticInterfaceMethodsTest.java
@@ -52,7 +52,7 @@
         .apply(ApiModelingTestHelper::enableApiCallerIdentification)
         // We are testing that we do not inline/merge higher api-levels
         .apply(ApiModelingTestHelper::disableOutliningAndStubbing)
-        .noMinification()
+        .addDontObfuscate()
         .enableInliningAnnotations()
         .compile()
         .inspect(
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingSubReferenceApiTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingSubReferenceApiTest.java
index d105c17..f49536b 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingSubReferenceApiTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingSubReferenceApiTest.java
@@ -65,7 +65,7 @@
                 inspector.assertNoClassesMerged();
               }
             })
-        .noMinification()
+        .addDontObfuscate()
         .compile()
         .inspect(
             inspector -> {
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineInstanceFieldTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineInstanceFieldTest.java
new file mode 100644
index 0000000..2d9de46
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineInstanceFieldTest.java
@@ -0,0 +1,145 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.apimodel;
+
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForField;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.SingleTestRunResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestCompilerBuilder;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.testing.AndroidBuildVersion;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.lang.reflect.Field;
+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 ApiModelOutlineInstanceFieldTest extends TestBase {
+
+  private static final AndroidApiLevel classApiLevel = AndroidApiLevel.M;
+  private static final AndroidApiLevel fieldApiLevel = AndroidApiLevel.P;
+
+  private static final String[] EXPECTED =
+      new String[] {"LibraryClass::getField", "LibraryClass::putField"};
+
+  @Parameter public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  private void setupTestBuilder(TestCompilerBuilder<?, ?, ?, ?, ?> testBuilder) throws Exception {
+    testBuilder
+        .addLibraryClasses(LibraryClass.class)
+        .addDefaultRuntimeLibrary(parameters)
+        .addProgramClasses(Main.class)
+        .setMinApi(parameters.getApiLevel())
+        .addAndroidBuildVersion()
+        .apply(setMockApiLevelForClass(LibraryClass.class, classApiLevel))
+        .apply(setMockApiLevelForDefaultInstanceInitializer(LibraryClass.class, classApiLevel))
+        .apply(setMockApiLevelForField(LibraryClass.class.getField("getField"), fieldApiLevel))
+        .apply(setMockApiLevelForField(LibraryClass.class.getField("putField"), fieldApiLevel))
+        .apply(ApiModelingTestHelper::enableApiCallerIdentification)
+        .apply(ApiModelingTestHelper::enableOutliningOfMethods)
+        .apply(ApiModelingTestHelper::disableStubbingOfClasses);
+  }
+
+  public boolean addToBootClasspath() {
+    return parameters.isDexRuntime()
+        && parameters.getApiLevel().isGreaterThanOrEqualTo(classApiLevel);
+  }
+
+  @Test
+  public void testD8Debug() throws Exception {
+    assumeTrue(parameters.isDexRuntime());
+    testForD8()
+        .setMode(CompilationMode.DEBUG)
+        .apply(this::setupTestBuilder)
+        .compile()
+        .inspect(this::inspect)
+        .applyIf(addToBootClasspath(), b -> b.addBootClasspathClasses(LibraryClass.class))
+        .run(parameters.getRuntime(), Main.class)
+        .apply(this::checkOutput);
+  }
+
+  @Test
+  public void testD8Release() throws Exception {
+    assumeTrue(parameters.isDexRuntime());
+    testForD8()
+        .setMode(CompilationMode.RELEASE)
+        .apply(this::setupTestBuilder)
+        .compile()
+        .inspect(this::inspect)
+        .applyIf(addToBootClasspath(), b -> b.addBootClasspathClasses(LibraryClass.class))
+        .run(parameters.getRuntime(), Main.class)
+        .apply(this::checkOutput);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .apply(this::setupTestBuilder)
+        .addKeepMainRule(Main.class)
+        .compile()
+        .inspect(this::inspect)
+        .applyIf(addToBootClasspath(), b -> b.addBootClasspathClasses(LibraryClass.class))
+        .run(parameters.getRuntime(), Main.class)
+        .apply(this::checkOutput);
+  }
+
+  private void inspect(CodeInspector inspector) throws Exception {
+    Field[] fields =
+        new Field[] {
+          LibraryClass.class.getField("getField"), LibraryClass.class.getField("putField")
+        };
+    for (Field field : fields) {
+      verifyThat(inspector, parameters, field)
+          .isOutlinedFromUntil(Main.class.getMethod("main", String[].class), fieldApiLevel);
+    }
+  }
+
+  private void checkOutput(SingleTestRunResult<?> runResult) {
+    if (parameters.isDexRuntime()
+        && parameters.getApiLevel().isGreaterThanOrEqualTo(classApiLevel)) {
+      runResult.assertSuccessWithOutputLines(EXPECTED);
+    } else {
+      runResult.assertSuccessWithOutputLines("Not calling API");
+    }
+  }
+
+  // Only present from api level 23.
+  public static class LibraryClass {
+
+    public String getField = "LibraryClass::getField";
+
+    public String putField;
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) {
+      if (AndroidBuildVersion.VERSION >= 23) {
+        LibraryClass libraryClass = new LibraryClass();
+        System.out.println(libraryClass.getField);
+        libraryClass.putField = "LibraryClass::putField";
+        System.out.println(libraryClass.putField);
+      } else {
+        System.out.println("Not calling API");
+      }
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineStaticFieldTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineStaticFieldTest.java
new file mode 100644
index 0000000..79b2cb8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineStaticFieldTest.java
@@ -0,0 +1,142 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.apimodel;
+
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForField;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.SingleTestRunResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestCompilerBuilder;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.testing.AndroidBuildVersion;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.lang.reflect.Field;
+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 ApiModelOutlineStaticFieldTest extends TestBase {
+
+  private static final AndroidApiLevel classApiLevel = AndroidApiLevel.M;
+  private static final AndroidApiLevel fieldApiLevel = AndroidApiLevel.P;
+
+  private static final String[] EXPECTED =
+      new String[] {"LibraryClass::getField", "LibraryClass::putField"};
+
+  @Parameter public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  private void setupTestBuilder(TestCompilerBuilder<?, ?, ?, ?, ?> testBuilder) throws Exception {
+    testBuilder
+        .addLibraryClasses(LibraryClass.class)
+        .addDefaultRuntimeLibrary(parameters)
+        .addProgramClasses(Main.class)
+        .setMinApi(parameters.getApiLevel())
+        .addAndroidBuildVersion()
+        .apply(setMockApiLevelForClass(LibraryClass.class, classApiLevel))
+        .apply(setMockApiLevelForField(LibraryClass.class.getField("getField"), fieldApiLevel))
+        .apply(setMockApiLevelForField(LibraryClass.class.getField("putField"), fieldApiLevel))
+        .apply(ApiModelingTestHelper::enableApiCallerIdentification)
+        .apply(ApiModelingTestHelper::enableOutliningOfMethods)
+        .apply(ApiModelingTestHelper::disableStubbingOfClasses);
+  }
+
+  public boolean addToBootClasspath() {
+    return parameters.isDexRuntime()
+        && parameters.getApiLevel().isGreaterThanOrEqualTo(classApiLevel);
+  }
+
+  @Test
+  public void testD8Debug() throws Exception {
+    assumeTrue(parameters.isDexRuntime());
+    testForD8()
+        .setMode(CompilationMode.DEBUG)
+        .apply(this::setupTestBuilder)
+        .compile()
+        .inspect(this::inspect)
+        .applyIf(addToBootClasspath(), b -> b.addBootClasspathClasses(LibraryClass.class))
+        .run(parameters.getRuntime(), Main.class)
+        .apply(this::checkOutput);
+  }
+
+  @Test
+  public void testD8Release() throws Exception {
+    assumeTrue(parameters.isDexRuntime());
+    testForD8()
+        .setMode(CompilationMode.RELEASE)
+        .apply(this::setupTestBuilder)
+        .compile()
+        .inspect(this::inspect)
+        .applyIf(addToBootClasspath(), b -> b.addBootClasspathClasses(LibraryClass.class))
+        .run(parameters.getRuntime(), Main.class)
+        .apply(this::checkOutput);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .apply(this::setupTestBuilder)
+        .addKeepMainRule(Main.class)
+        .compile()
+        .inspect(this::inspect)
+        .applyIf(addToBootClasspath(), b -> b.addBootClasspathClasses(LibraryClass.class))
+        .run(parameters.getRuntime(), Main.class)
+        .apply(this::checkOutput);
+  }
+
+  private void inspect(CodeInspector inspector) throws Exception {
+    Field[] fields =
+        new Field[] {
+          LibraryClass.class.getField("getField"), LibraryClass.class.getField("putField")
+        };
+    for (Field field : fields) {
+      verifyThat(inspector, parameters, field)
+          .isOutlinedFromUntil(Main.class.getMethod("main", String[].class), fieldApiLevel);
+    }
+  }
+
+  private void checkOutput(SingleTestRunResult<?> runResult) {
+    if (parameters.isDexRuntime()
+        && parameters.getApiLevel().isGreaterThanOrEqualTo(classApiLevel)) {
+      runResult.assertSuccessWithOutputLines(EXPECTED);
+    } else {
+      runResult.assertSuccessWithOutputLines("Not calling API");
+    }
+  }
+
+  // Only present from api level 23.
+  public static class LibraryClass {
+
+    public static String getField = "LibraryClass::getField";
+
+    public static String putField;
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) {
+      if (AndroidBuildVersion.VERSION >= 23) {
+        System.out.println(LibraryClass.getField);
+        LibraryClass.putField = "LibraryClass::putField";
+        System.out.println(LibraryClass.putField);
+      } else {
+        System.out.println("Not calling API");
+      }
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
index ed2343a..4166e5e 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.apimodel;
 
+import static com.android.tools.r8.utils.codeinspector.CodeMatchers.accessesField;
 import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethod;
 import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethodWithName;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
@@ -11,10 +12,12 @@
 import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 
 import com.android.tools.r8.TestCompilerBuilder;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ThrowableConsumer;
+import com.android.tools.r8.references.FieldReference;
 import com.android.tools.r8.references.MethodReference;
 import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.utils.AndroidApiLevel;
@@ -193,6 +196,12 @@
         inspector, parameters, Reference.methodFromMethod(method));
   }
 
+  static ApiModelingFieldVerificationHelper verifyThat(
+      CodeInspector inspector, TestParameters parameters, Field field) {
+    return new ApiModelingFieldVerificationHelper(
+        inspector, parameters, Reference.fieldFromField(field));
+  }
+
   public static void assertNoSynthesizedClasses(CodeInspector inspector) {
     assertEquals(
         Collections.emptySet(),
@@ -224,6 +233,51 @@
     }
   }
 
+  public static class ApiModelingFieldVerificationHelper {
+
+    private final CodeInspector inspector;
+    private final FieldReference fieldOfInterest;
+    private final TestParameters parameters;
+
+    private ApiModelingFieldVerificationHelper(
+        CodeInspector inspector, TestParameters parameters, FieldReference fieldOfInterest) {
+      this.inspector = inspector;
+      this.fieldOfInterest = fieldOfInterest;
+      this.parameters = parameters;
+    }
+
+    void isOutlinedFromUntil(Method method, AndroidApiLevel apiLevel) {
+      if (parameters.isDexRuntime() && parameters.getApiLevel().isLessThan(apiLevel)) {
+        isOutlinedFrom(method);
+      } else {
+        isNotOutlinedFrom(method);
+      }
+    }
+
+    void isOutlinedFrom(Method method) {
+      // Check that the call is in a synthetic class.
+      List<FoundMethodSubject> outlinedMethod =
+          inspector.allClasses().stream()
+              .flatMap(clazz -> clazz.allMethods().stream())
+              .filter(
+                  methodSubject ->
+                      methodSubject.isSynthetic()
+                          && accessesField(fieldOfInterest).matches(methodSubject))
+              .collect(Collectors.toList());
+      assertFalse(outlinedMethod.isEmpty());
+      // Assert that method invokes the outline
+      MethodSubject caller = inspector.method(method);
+      assertThat(caller, isPresent());
+      assertThat(caller, invokesMethod(outlinedMethod.get(0)));
+    }
+
+    void isNotOutlinedFrom(Method method) {
+      MethodSubject caller = inspector.method(method);
+      assertThat(caller, isPresent());
+      assertThat(caller, accessesField(fieldOfInterest));
+    }
+  }
+
   public static class ApiModelingMethodVerificationHelper {
 
     private final CodeInspector inspector;
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/B77836766.java b/src/test/java/com/android/tools/r8/bridgeremoval/B77836766.java
index 57c41a8..d08bb2f 100644
--- a/src/test/java/com/android/tools/r8/bridgeremoval/B77836766.java
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/B77836766.java
@@ -147,7 +147,7 @@
         .addOptionsModification(this::configure)
         .enableConstantArgumentAnnotations()
         .noHorizontalClassMerging(cls2Class.name)
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(
@@ -280,7 +280,7 @@
         .addOptionsModification(this::configure)
         .enableConstantArgumentAnnotations()
         .noHorizontalClassMerging(derivedIntegerClass.name)
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(
@@ -392,7 +392,7 @@
         .addKeepMainRule(mainClass.name)
         .addOptionsModification(this::configure)
         .enableConstantArgumentAnnotations()
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(
@@ -481,7 +481,7 @@
         .addKeepMainRule(mainClass.name)
         .addOptionsModification(this::configure)
         .enableConstantArgumentAnnotations()
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/BridgeHoistingAccessibilityTest.java b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/BridgeHoistingAccessibilityTest.java
index 1eb5247..eac0142 100644
--- a/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/BridgeHoistingAccessibilityTest.java
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/BridgeHoistingAccessibilityTest.java
@@ -80,7 +80,7 @@
         .enableNoVerticalClassMergingAnnotations()
         .enableNeverClassInliningAnnotations()
         // TODO(b/173398086): uniqueMethodWithName() does not work with argument changes.
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(this::inspect)
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/KeptBridgeHoistingTest.java b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/KeptBridgeHoistingTest.java
index e45bdcc..458a8d2 100644
--- a/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/KeptBridgeHoistingTest.java
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/KeptBridgeHoistingTest.java
@@ -46,7 +46,7 @@
         .enableNoVerticalClassMergingAnnotations()
         .enableNeverClassInliningAnnotations()
         // TODO(b/173398086): uniqueMethodWithName() does not work with signature changes.
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(this::inspect)
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/NonSuperclassBridgeHoistingTest.java b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/NonSuperclassBridgeHoistingTest.java
index 2819311..ff05bfd 100644
--- a/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/NonSuperclassBridgeHoistingTest.java
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/NonSuperclassBridgeHoistingTest.java
@@ -48,7 +48,7 @@
         .enableNoVerticalClassMergingAnnotations()
         .enableNeverClassInliningAnnotations()
         // TODO(b/173398086): uniqueMethodWithName() does not work with signature changes.
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(this::inspect)
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/PositiveBridgeHoistingTest.java b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/PositiveBridgeHoistingTest.java
index 120d7b4..2ac940a 100644
--- a/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/PositiveBridgeHoistingTest.java
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/PositiveBridgeHoistingTest.java
@@ -59,7 +59,7 @@
         .enableNoMethodStaticizingAnnotations()
         .enableNoHorizontalClassMergingAnnotations()
         // TODO(b/173398086): uniqueMethodWithName() does not work with argument changes.
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(this::inspect)
diff --git a/src/test/java/com/android/tools/r8/cf/AlwaysNullGetItemTestRunner.java b/src/test/java/com/android/tools/r8/cf/AlwaysNullGetItemTestRunner.java
index f99896f..a459923 100644
--- a/src/test/java/com/android/tools/r8/cf/AlwaysNullGetItemTestRunner.java
+++ b/src/test/java/com/android/tools/r8/cf/AlwaysNullGetItemTestRunner.java
@@ -39,7 +39,7 @@
     testForR8(parameters.getBackend())
         .addProgramClassesAndInnerClasses(CLASS)
         .debug()
-        .noMinification()
+        .addDontObfuscate()
         .noTreeShaking()
         .setMinApi(parameters.getApiLevel())
         .compile()
diff --git a/src/test/java/com/android/tools/r8/cf/BasicBlockMuncherQuadraticTest.java b/src/test/java/com/android/tools/r8/cf/BasicBlockMuncherQuadraticTest.java
index 7ab71ea..6cb424f 100644
--- a/src/test/java/com/android/tools/r8/cf/BasicBlockMuncherQuadraticTest.java
+++ b/src/test/java/com/android/tools/r8/cf/BasicBlockMuncherQuadraticTest.java
@@ -16,7 +16,7 @@
     testForR8(Backend.CF)
         .addKeepMainRule(MethodHolder.class)
         .addInnerClasses(BasicBlockMuncherQuadraticTest.class)
-        .noMinification()
+        .addDontObfuscate()
         .addOptionsModification(options -> options.testing.basicBlockMuncherIterationLimit = 50000)
         .compile();
   }
diff --git a/src/test/java/com/android/tools/r8/cf/CfDebugLocalStackMapVerificationTest.java b/src/test/java/com/android/tools/r8/cf/CfDebugLocalStackMapVerificationTest.java
index 5108c8d..0f1668f 100644
--- a/src/test/java/com/android/tools/r8/cf/CfDebugLocalStackMapVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/cf/CfDebugLocalStackMapVerificationTest.java
@@ -5,10 +5,7 @@
 package com.android.tools.r8.cf;
 
 import static org.hamcrest.CoreMatchers.containsString;
-import static org.junit.Assert.assertThrows;
 
-import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
@@ -18,12 +15,6 @@
 import org.junit.runners.Parameterized;
 import org.junit.runners.Parameterized.Parameter;
 import org.junit.runners.Parameterized.Parameters;
-import org.objectweb.asm.AnnotationVisitor;
-import org.objectweb.asm.ClassWriter;
-import org.objectweb.asm.FieldVisitor;
-import org.objectweb.asm.Label;
-import org.objectweb.asm.MethodVisitor;
-import org.objectweb.asm.Opcodes;
 
 /** Regression tests for b/237567012 */
 @RunWith(Parameterized.class)
@@ -59,7 +50,7 @@
   }
 
   @Test
-  public void testSmallReproD8() throws Exception {
+  public void testD8() throws Exception {
     testForD8(parameters.getBackend())
         .addProgramClasses(SmallRepro.class)
         .setMinApi(AndroidApiLevel.B)
@@ -69,967 +60,25 @@
               options.testing.neverReuseCfLocalRegisters = true;
             })
         .run(parameters.getRuntime(), SmallRepro.class)
-        // TODO(b/237567012): Run should succeed with printing of FOO.
-        .assertFailureWithErrorThatThrows(VerifyError.class);
+        .assertSuccessWithOutputThatMatches(containsString("FOO"));
   }
 
   @Test
   public void testR8() throws Exception {
-    // TODO(b/237567012): We should not fail compilation.
-    assertThrows(
-        CompilationFailedException.class,
-        () ->
-            testForR8(parameters.getBackend())
-                .addProgramClassFileData(CfDebugLocalStackMapVerificationTest$MainDump.dump())
-                .addKeepMainRule(Main.class)
-                .setMode(CompilationMode.DEBUG)
-                .addDontWarn("*")
-                // TODO(b/237567012): Remove option when resolved.
-                .addOptionsModification(
-                    options -> options.enableCheckAllInstructionsDuringStackMapVerification = true)
-                .compile()
-                .run(parameters.getRuntime(), Main.class));
-  }
-
-  public static class Main {
-
-    // InvokeSuspend is taken from an input program.jar and copied verbatim in the dump below.
-    public Object invokeSuspend(Object o) {
-      return o;
-    }
-
-    public static void main(String[] args) {
-      new Main().invokeSuspend(null);
-    }
-  }
-
-  public static class CfDebugLocalStackMapVerificationTest$MainDump implements Opcodes {
-
-    public static byte[] dump() throws Exception {
-
-      ClassWriter classWriter = new ClassWriter(0);
-      FieldVisitor fieldVisitor;
-      MethodVisitor methodVisitor;
-      AnnotationVisitor annotationVisitor0;
-
-      classWriter.visit(
-          V1_8,
-          ACC_FINAL | ACC_SUPER,
-          binaryName(Main.class),
-          null,
-          "kotlin/coroutines/jvm/internal/SuspendLambda",
-          new String[] {"kotlin/jvm/functions/Function2"});
-
-      {
-        fieldVisitor = classWriter.visitField(0, "L$1", "Ljava/lang/Object;", null, null);
-        fieldVisitor.visitEnd();
-      }
-      {
-        fieldVisitor = classWriter.visitField(0, "L$2", "Ljava/lang/Object;", null, null);
-        fieldVisitor.visitEnd();
-      }
-      {
-        fieldVisitor = classWriter.visitField(0, "label", "I", null, null);
-        fieldVisitor.visitEnd();
-      }
-      {
-        fieldVisitor =
-            classWriter.visitField(
-                ACC_PRIVATE | ACC_SYNTHETIC, "L$0", "Ljava/lang/Object;", null, null);
-        fieldVisitor.visitEnd();
-      }
-      {
-        fieldVisitor =
-            classWriter.visitField(
-                ACC_FINAL | ACC_SYNTHETIC,
-                "this$0",
-                "Lio/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber;",
-                null,
-                null);
-        fieldVisitor.visitEnd();
-      }
-      {
-        methodVisitor =
-            classWriter.visitMethod(
-                0,
-                "<init>",
-                "(Lio/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber;Lkotlin/coroutines/Continuation;)V",
-                "(Lio/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber;Lkotlin/coroutines/Continuation<-Lio/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1;>;)V",
-                null);
-        methodVisitor.visitCode();
-        Label label0 = new Label();
-        methodVisitor.visitLabel(label0);
-        methodVisitor.visitVarInsn(ALOAD, 0);
-        methodVisitor.visitVarInsn(ALOAD, 1);
-        methodVisitor.visitFieldInsn(
-            PUTFIELD,
-            "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
-            "this$0",
-            "Lio/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber;");
-        methodVisitor.visitVarInsn(ALOAD, 0);
-        methodVisitor.visitInsn(ICONST_2);
-        methodVisitor.visitVarInsn(ALOAD, 2);
-        methodVisitor.visitMethodInsn(
-            INVOKESPECIAL,
-            "kotlin/coroutines/jvm/internal/SuspendLambda",
-            "<init>",
-            "(ILkotlin/coroutines/Continuation;)V",
-            false);
-        methodVisitor.visitInsn(RETURN);
-        Label label1 = new Label();
-        methodVisitor.visitLabel(label1);
-        methodVisitor.visitLocalVariable(
-            "this",
-            "Lio/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1;",
-            null,
-            label0,
-            label1,
-            0);
-        methodVisitor.visitLocalVariable(
-            "$receiver",
-            "Lio/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber;",
-            null,
-            label0,
-            label1,
-            1);
-        methodVisitor.visitLocalVariable(
-            "$completion", "Lkotlin/coroutines/Continuation;", null, label0, label1, 2);
-        methodVisitor.visitMaxs(3, 3);
-        methodVisitor.visitEnd();
-      }
-      {
-        methodVisitor =
-            classWriter.visitMethod(
-                ACC_PUBLIC | ACC_FINAL,
-                "invokeSuspend",
-                "(Ljava/lang/Object;)Ljava/lang/Object;",
-                null,
-                null);
-        {
-          annotationVisitor0 =
-              methodVisitor.visitAnnotation("Lorg/jetbrains/annotations/Nullable;", false);
-          annotationVisitor0.visitEnd();
-        }
-        methodVisitor.visitAnnotableParameterCount(1, false);
-        {
-          annotationVisitor0 =
-              methodVisitor.visitParameterAnnotation(
-                  0, "Lorg/jetbrains/annotations/NotNull;", false);
-          annotationVisitor0.visitEnd();
-        }
-        methodVisitor.visitCode();
-        Label label0 = new Label();
-        Label label1 = new Label();
-        Label label2 = new Label();
-        methodVisitor.visitTryCatchBlock(label0, label1, label2, "java/lang/Throwable");
-        Label label3 = new Label();
-        Label label4 = new Label();
-        methodVisitor.visitTryCatchBlock(label3, label4, label2, "java/lang/Throwable");
-        Label label5 = new Label();
-        Label label6 = new Label();
-        methodVisitor.visitTryCatchBlock(label5, label6, label2, "java/lang/Throwable");
-        Label label7 = new Label();
-        methodVisitor.visitTryCatchBlock(label0, label1, label7, null);
-        methodVisitor.visitTryCatchBlock(label3, label4, label7, null);
-        methodVisitor.visitTryCatchBlock(label5, label6, label7, null);
-        methodVisitor.visitTryCatchBlock(label2, label7, label7, null);
-        Label label8 = new Label();
-        methodVisitor.visitTryCatchBlock(label7, label8, label7, null);
-        Label label9 = new Label();
-        Label label10 = new Label();
-        methodVisitor.visitTryCatchBlock(
-            label9, label1, label10, "kotlinx/coroutines/channels/ClosedReceiveChannelException");
-        methodVisitor.visitTryCatchBlock(
-            label3, label4, label10, "kotlinx/coroutines/channels/ClosedReceiveChannelException");
-        methodVisitor.visitTryCatchBlock(
-            label5, label10, label10, "kotlinx/coroutines/channels/ClosedReceiveChannelException");
-        methodVisitor.visitMethodInsn(
-            INVOKESTATIC,
-            "kotlin/coroutines/intrinsics/IntrinsicsKt",
-            "getCOROUTINE_SUSPENDED",
-            "()Ljava/lang/Object;",
-            false);
-        Label label11 = new Label();
-        methodVisitor.visitLabel(label11);
-        methodVisitor.visitLineNumber(60, label11);
-        methodVisitor.visitVarInsn(ASTORE, 10);
-        methodVisitor.visitVarInsn(ALOAD, 0);
-        methodVisitor.visitFieldInsn(
-            GETFIELD,
-            "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
-            "label",
-            "I");
-        Label label12 = new Label();
-        Label label13 = new Label();
-        Label label14 = new Label();
-        Label label15 = new Label();
-        methodVisitor.visitTableSwitchInsn(0, 2, label15, new Label[] {label12, label13, label14});
-        methodVisitor.visitLabel(label12);
-        methodVisitor.visitFrame(
-            Opcodes.F_FULL,
-            11,
-            new Object[] {
-              "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
-              "java/lang/Object",
-              Opcodes.TOP,
-              Opcodes.TOP,
-              Opcodes.TOP,
-              Opcodes.TOP,
-              Opcodes.TOP,
-              Opcodes.TOP,
-              Opcodes.TOP,
-              Opcodes.TOP,
-              "java/lang/Object"
-            },
-            0,
-            new Object[] {});
-        methodVisitor.visitVarInsn(ALOAD, 1);
-        methodVisitor.visitMethodInsn(
-            INVOKESTATIC, "kotlin/ResultKt", "throwOnFailure", "(Ljava/lang/Object;)V", false);
-        Label label16 = new Label();
-        methodVisitor.visitLabel(label16);
-        methodVisitor.visitVarInsn(ALOAD, 0);
-        methodVisitor.visitFieldInsn(
-            GETFIELD,
-            "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
-            "L$0",
-            "Ljava/lang/Object;");
-        methodVisitor.visitTypeInsn(CHECKCAST, "kotlinx/coroutines/CoroutineScope");
-        methodVisitor.visitVarInsn(ASTORE, 2);
-        methodVisitor.visitLabel(label9);
-        methodVisitor.visitLineNumber(61, label9);
-        methodVisitor.visitInsn(NOP);
-        Label label17 = new Label();
-        methodVisitor.visitLabel(label17);
-        methodVisitor.visitLineNumber(62, label17);
-        methodVisitor.visitVarInsn(ALOAD, 0);
-        methodVisitor.visitFieldInsn(
-            GETFIELD,
-            "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
-            "this$0",
-            "Lio/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber;");
-        methodVisitor.visitMethodInsn(
-            INVOKESTATIC,
-            "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber",
-            "access$getQueue$p",
-            "(Lio/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber;)Lkotlinx/coroutines/channels/Channel;",
-            false);
-        methodVisitor.visitTypeInsn(CHECKCAST, "kotlinx/coroutines/channels/ReceiveChannel");
-        methodVisitor.visitVarInsn(ASTORE, 3);
-        methodVisitor.visitVarInsn(ALOAD, 0);
-        methodVisitor.visitFieldInsn(
-            GETFIELD,
-            "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
-            "this$0",
-            "Lio/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber;");
-        methodVisitor.visitVarInsn(ASTORE, 4);
-        Label label18 = new Label();
-        methodVisitor.visitLabel(label18);
-        methodVisitor.visitInsn(ICONST_0);
-        methodVisitor.visitVarInsn(ISTORE, 5);
-        Label label19 = new Label();
-        methodVisitor.visitLabel(label19);
-        methodVisitor.visitLineNumber(146, label19);
-        methodVisitor.visitInsn(NOP);
-        Label label20 = new Label();
-        methodVisitor.visitLabel(label20);
-        methodVisitor.visitLineNumber(149, label20);
-        methodVisitor.visitInsn(ACONST_NULL);
-        methodVisitor.visitVarInsn(ASTORE, 6);
-        methodVisitor.visitLabel(label0);
-        methodVisitor.visitLineNumber(150, label0);
-        methodVisitor.visitInsn(NOP);
-        Label label21 = new Label();
-        methodVisitor.visitLabel(label21);
-        methodVisitor.visitLineNumber(151, label21);
-        methodVisitor.visitInsn(ICONST_0);
-        methodVisitor.visitVarInsn(ISTORE, 7);
-        Label label22 = new Label();
-        methodVisitor.visitLabel(label22);
-        methodVisitor.visitLineNumber(63, label22);
-        methodVisitor.visitFrame(
-            Opcodes.F_FULL,
-            11,
-            new Object[] {
-              "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
-              "java/lang/Object",
-              "kotlinx/coroutines/CoroutineScope",
-              "kotlinx/coroutines/channels/ReceiveChannel",
-              "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber",
-              Opcodes.INTEGER,
-              Opcodes.NULL,
-              Opcodes.INTEGER,
-              Opcodes.TOP,
-              Opcodes.TOP,
-              "java/lang/Object"
-            },
-            0,
-            new Object[] {});
-        methodVisitor.visitVarInsn(ALOAD, 2);
-        methodVisitor.visitMethodInsn(
-            INVOKESTATIC,
-            "kotlinx/coroutines/CoroutineScopeKt",
-            "isActive",
-            "(Lkotlinx/coroutines/CoroutineScope;)Z",
-            false);
-        Label label23 = new Label();
-        methodVisitor.visitJumpInsn(IFEQ, label23);
-        Label label24 = new Label();
-        methodVisitor.visitLabel(label24);
-        methodVisitor.visitLineNumber(64, label24);
-        methodVisitor.visitVarInsn(ALOAD, 4);
-        methodVisitor.visitMethodInsn(
-            INVOKESTATIC,
-            "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber",
-            "access$getQueue$p",
-            "(Lio/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber;)Lkotlinx/coroutines/channels/Channel;",
-            false);
-        methodVisitor.visitMethodInsn(
-            INVOKEINTERFACE,
-            "kotlinx/coroutines/channels/Channel",
-            "tryReceive-PtdJZtk",
-            "()Ljava/lang/Object;",
-            true);
-        methodVisitor.visitMethodInsn(
-            INVOKESTATIC,
-            "kotlinx/coroutines/channels/ChannelResult",
-            "getOrNull-impl",
-            "(Ljava/lang/Object;)Ljava/lang/Object;",
-            false);
-        methodVisitor.visitTypeInsn(CHECKCAST, "java/nio/ByteBuffer");
-        methodVisitor.visitVarInsn(ASTORE, 8);
-        Label label25 = new Label();
-        methodVisitor.visitLabel(label25);
-        methodVisitor.visitLineNumber(65, label25);
-        methodVisitor.visitVarInsn(ALOAD, 8);
-        Label label26 = new Label();
-        methodVisitor.visitJumpInsn(IFNONNULL, label26);
-        Label label27 = new Label();
-        methodVisitor.visitLabel(label27);
-        methodVisitor.visitLineNumber(66, label27);
-        methodVisitor.visitVarInsn(ALOAD, 4);
-        methodVisitor.visitFieldInsn(
-            GETFIELD,
-            "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber",
-            "subscription",
-            "Ljava/lang/Object;");
-        methodVisitor.visitTypeInsn(CHECKCAST, "java/util/concurrent/Flow$Subscription");
-        methodVisitor.visitInsn(DUP);
-        Label label28 = new Label();
-        methodVisitor.visitJumpInsn(IFNULL, label28);
-        methodVisitor.visitInsn(LCONST_1);
-        methodVisitor.visitMethodInsn(
-            INVOKEINTERFACE, "java/util/concurrent/Flow$Subscription", "request", "(J)V", true);
-        Label label29 = new Label();
-        methodVisitor.visitJumpInsn(GOTO, label29);
-        methodVisitor.visitLabel(label28);
-        methodVisitor.visitFrame(
-            Opcodes.F_FULL,
-            11,
-            new Object[] {
-              "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
-              "java/lang/Object",
-              "kotlinx/coroutines/CoroutineScope",
-              "kotlinx/coroutines/channels/ReceiveChannel",
-              "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber",
-              Opcodes.INTEGER,
-              Opcodes.NULL,
-              Opcodes.INTEGER,
-              "java/nio/ByteBuffer",
-              Opcodes.TOP,
-              "java/lang/Object"
-            },
-            1,
-            new Object[] {"java/util/concurrent/Flow$Subscription"});
-        methodVisitor.visitInsn(POP);
-        methodVisitor.visitLabel(label29);
-        methodVisitor.visitLineNumber(67, label29);
-        methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
-        methodVisitor.visitVarInsn(ALOAD, 4);
-        methodVisitor.visitMethodInsn(
-            INVOKESTATIC,
-            "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber",
-            "access$getQueue$p",
-            "(Lio/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber;)Lkotlinx/coroutines/channels/Channel;",
-            false);
-        methodVisitor.visitVarInsn(ALOAD, 0);
-        methodVisitor.visitVarInsn(ALOAD, 0);
-        methodVisitor.visitVarInsn(ALOAD, 2);
-        methodVisitor.visitFieldInsn(
-            PUTFIELD,
-            "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
-            "L$0",
-            "Ljava/lang/Object;");
-        methodVisitor.visitVarInsn(ALOAD, 0);
-        methodVisitor.visitVarInsn(ALOAD, 3);
-        methodVisitor.visitFieldInsn(
-            PUTFIELD,
-            "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
-            "L$1",
-            "Ljava/lang/Object;");
-        methodVisitor.visitVarInsn(ALOAD, 0);
-        methodVisitor.visitVarInsn(ALOAD, 4);
-        methodVisitor.visitFieldInsn(
-            PUTFIELD,
-            "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
-            "L$2",
-            "Ljava/lang/Object;");
-        methodVisitor.visitVarInsn(ALOAD, 0);
-        methodVisitor.visitInsn(ICONST_1);
-        methodVisitor.visitFieldInsn(
-            PUTFIELD,
-            "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
-            "label",
-            "I");
-        methodVisitor.visitMethodInsn(
-            INVOKEINTERFACE,
-            "kotlinx/coroutines/channels/Channel",
-            "receive",
-            "(Lkotlin/coroutines/Continuation;)Ljava/lang/Object;",
-            true);
-        methodVisitor.visitLabel(label1);
-        methodVisitor.visitInsn(DUP);
-        methodVisitor.visitVarInsn(ALOAD, 10);
-        Label label30 = new Label();
-        methodVisitor.visitJumpInsn(IF_ACMPNE, label30);
-        Label label31 = new Label();
-        methodVisitor.visitLabel(label31);
-        methodVisitor.visitLineNumber(60, label31);
-        methodVisitor.visitVarInsn(ALOAD, 10);
-        methodVisitor.visitInsn(ARETURN);
-        methodVisitor.visitLabel(label13);
-        methodVisitor.visitFrame(
-            Opcodes.F_FULL,
-            11,
-            new Object[] {
-              "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
-              "java/lang/Object",
-              Opcodes.TOP,
-              Opcodes.TOP,
-              Opcodes.TOP,
-              Opcodes.TOP,
-              Opcodes.TOP,
-              Opcodes.TOP,
-              Opcodes.TOP,
-              Opcodes.TOP,
-              "java/lang/Object"
-            },
-            0,
-            new Object[] {});
-        methodVisitor.visitInsn(ICONST_0);
-        methodVisitor.visitVarInsn(ISTORE, 5);
-        Label label32 = new Label();
-        methodVisitor.visitLabel(label32);
-        methodVisitor.visitInsn(ICONST_0);
-        methodVisitor.visitVarInsn(ISTORE, 7);
-        Label label33 = new Label();
-        methodVisitor.visitLabel(label33);
-        methodVisitor.visitInsn(ACONST_NULL);
-        methodVisitor.visitVarInsn(ASTORE, 6);
-        methodVisitor.visitVarInsn(ALOAD, 0);
-        methodVisitor.visitFieldInsn(
-            GETFIELD,
-            "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
-            "L$2",
-            "Ljava/lang/Object;");
-        methodVisitor.visitTypeInsn(
-            CHECKCAST,
-            "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber");
-        methodVisitor.visitVarInsn(ASTORE, 4);
-        methodVisitor.visitVarInsn(ALOAD, 0);
-        methodVisitor.visitFieldInsn(
-            GETFIELD,
-            "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
-            "L$1",
-            "Ljava/lang/Object;");
-        methodVisitor.visitTypeInsn(CHECKCAST, "kotlinx/coroutines/channels/ReceiveChannel");
-        methodVisitor.visitVarInsn(ASTORE, 3);
-        Label label34 = new Label();
-        methodVisitor.visitLabel(label34);
-        methodVisitor.visitVarInsn(ALOAD, 0);
-        methodVisitor.visitFieldInsn(
-            GETFIELD,
-            "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
-            "L$0",
-            "Ljava/lang/Object;");
-        methodVisitor.visitTypeInsn(CHECKCAST, "kotlinx/coroutines/CoroutineScope");
-        methodVisitor.visitVarInsn(ASTORE, 2);
-        methodVisitor.visitLabel(label3);
-        methodVisitor.visitInsn(NOP);
-        methodVisitor.visitVarInsn(ALOAD, 1);
-        methodVisitor.visitMethodInsn(
-            INVOKESTATIC, "kotlin/ResultKt", "throwOnFailure", "(Ljava/lang/Object;)V", false);
-        methodVisitor.visitVarInsn(ALOAD, 1);
-        methodVisitor.visitLabel(label30);
-        methodVisitor.visitFrame(
-            Opcodes.F_FULL,
-            11,
-            new Object[] {
-              "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
-              "java/lang/Object",
-              "kotlinx/coroutines/CoroutineScope",
-              "kotlinx/coroutines/channels/ReceiveChannel",
-              "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber",
-              Opcodes.INTEGER,
-              Opcodes.NULL,
-              Opcodes.INTEGER,
-              Opcodes.TOP,
-              Opcodes.TOP,
-              "java/lang/Object"
-            },
-            1,
-            new Object[] {"java/lang/Object"});
-        methodVisitor.visitTypeInsn(CHECKCAST, "java/nio/ByteBuffer");
-        methodVisitor.visitVarInsn(ASTORE, 8);
-        methodVisitor.visitLabel(label26);
-        methodVisitor.visitLineNumber(70, label26);
-        methodVisitor.visitFrame(
-            Opcodes.F_FULL,
-            11,
-            new Object[] {
-              "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
-              "java/lang/Object",
-              "kotlinx/coroutines/CoroutineScope",
-              "kotlinx/coroutines/channels/ReceiveChannel",
-              "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber",
-              Opcodes.INTEGER,
-              Opcodes.NULL,
-              Opcodes.INTEGER,
-              "java/nio/ByteBuffer",
-              Opcodes.TOP,
-              "java/lang/Object"
-            },
-            0,
-            new Object[] {});
-        methodVisitor.visitVarInsn(ALOAD, 4);
-        methodVisitor.visitMethodInsn(
-            INVOKESTATIC,
-            "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber",
-            "access$getResponseChannel$p",
-            "(Lio/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber;)Lio/ktor/utils/io/ByteChannel;",
-            false);
-        methodVisitor.visitVarInsn(ALOAD, 8);
-        methodVisitor.visitVarInsn(ALOAD, 0);
-        methodVisitor.visitVarInsn(ALOAD, 0);
-        methodVisitor.visitVarInsn(ALOAD, 2);
-        methodVisitor.visitFieldInsn(
-            PUTFIELD,
-            "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
-            "L$0",
-            "Ljava/lang/Object;");
-        methodVisitor.visitVarInsn(ALOAD, 0);
-        methodVisitor.visitVarInsn(ALOAD, 3);
-        methodVisitor.visitFieldInsn(
-            PUTFIELD,
-            "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
-            "L$1",
-            "Ljava/lang/Object;");
-        methodVisitor.visitVarInsn(ALOAD, 0);
-        methodVisitor.visitVarInsn(ALOAD, 4);
-        methodVisitor.visitFieldInsn(
-            PUTFIELD,
-            "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
-            "L$2",
-            "Ljava/lang/Object;");
-        methodVisitor.visitVarInsn(ALOAD, 0);
-        methodVisitor.visitInsn(ICONST_2);
-        methodVisitor.visitFieldInsn(
-            PUTFIELD,
-            "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
-            "label",
-            "I");
-        methodVisitor.visitMethodInsn(
-            INVOKEINTERFACE,
-            "io/ktor/utils/io/ByteChannel",
-            "writeFully",
-            "(Ljava/nio/ByteBuffer;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;",
-            true);
-        methodVisitor.visitLabel(label4);
-        methodVisitor.visitInsn(DUP);
-        methodVisitor.visitVarInsn(ALOAD, 10);
-        Label label35 = new Label();
-        methodVisitor.visitJumpInsn(IF_ACMPNE, label35);
-        Label label36 = new Label();
-        methodVisitor.visitLabel(label36);
-        methodVisitor.visitLineNumber(60, label36);
-        methodVisitor.visitVarInsn(ALOAD, 10);
-        methodVisitor.visitInsn(ARETURN);
-        methodVisitor.visitLabel(label14);
-        methodVisitor.visitFrame(
-            Opcodes.F_FULL,
-            11,
-            new Object[] {
-              "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
-              "java/lang/Object",
-              Opcodes.TOP,
-              Opcodes.TOP,
-              Opcodes.TOP,
-              Opcodes.TOP,
-              Opcodes.TOP,
-              Opcodes.TOP,
-              Opcodes.TOP,
-              Opcodes.TOP,
-              "java/lang/Object"
-            },
-            0,
-            new Object[] {});
-        methodVisitor.visitInsn(ICONST_0);
-        methodVisitor.visitVarInsn(ISTORE, 5);
-        Label label37 = new Label();
-        methodVisitor.visitLabel(label37);
-        methodVisitor.visitInsn(ICONST_0);
-        methodVisitor.visitVarInsn(ISTORE, 7);
-        Label label38 = new Label();
-        methodVisitor.visitLabel(label38);
-        methodVisitor.visitInsn(ACONST_NULL);
-        methodVisitor.visitVarInsn(ASTORE, 6);
-        methodVisitor.visitVarInsn(ALOAD, 0);
-        methodVisitor.visitFieldInsn(
-            GETFIELD,
-            "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
-            "L$2",
-            "Ljava/lang/Object;");
-        methodVisitor.visitTypeInsn(
-            CHECKCAST,
-            "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber");
-        methodVisitor.visitVarInsn(ASTORE, 4);
-        methodVisitor.visitVarInsn(ALOAD, 0);
-        methodVisitor.visitFieldInsn(
-            GETFIELD,
-            "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
-            "L$1",
-            "Ljava/lang/Object;");
-        methodVisitor.visitTypeInsn(CHECKCAST, "kotlinx/coroutines/channels/ReceiveChannel");
-        methodVisitor.visitVarInsn(ASTORE, 3);
-        Label label39 = new Label();
-        methodVisitor.visitLabel(label39);
-        methodVisitor.visitVarInsn(ALOAD, 0);
-        methodVisitor.visitFieldInsn(
-            GETFIELD,
-            "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
-            "L$0",
-            "Ljava/lang/Object;");
-        methodVisitor.visitTypeInsn(CHECKCAST, "kotlinx/coroutines/CoroutineScope");
-        methodVisitor.visitVarInsn(ASTORE, 2);
-        methodVisitor.visitLabel(label5);
-        methodVisitor.visitInsn(NOP);
-        methodVisitor.visitVarInsn(ALOAD, 1);
-        methodVisitor.visitMethodInsn(
-            INVOKESTATIC, "kotlin/ResultKt", "throwOnFailure", "(Ljava/lang/Object;)V", false);
-        methodVisitor.visitVarInsn(ALOAD, 1);
-        methodVisitor.visitLabel(label35);
-        methodVisitor.visitLineNumber(70, label35);
-        methodVisitor.visitFrame(
-            Opcodes.F_FULL,
-            11,
-            new Object[] {
-              "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
-              "java/lang/Object",
-              "kotlinx/coroutines/CoroutineScope",
-              "kotlinx/coroutines/channels/ReceiveChannel",
-              "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber",
-              Opcodes.INTEGER,
-              Opcodes.NULL,
-              Opcodes.INTEGER,
-              Opcodes.TOP,
-              Opcodes.TOP,
-              "java/lang/Object"
-            },
-            1,
-            new Object[] {"java/lang/Object"});
-        methodVisitor.visitInsn(POP);
-        methodVisitor.visitJumpInsn(GOTO, label22);
-        methodVisitor.visitLabel(label23);
-        methodVisitor.visitLineNumber(72, label23);
-        methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
-        methodVisitor.visitInsn(NOP);
-        Label label40 = new Label();
-        methodVisitor.visitLabel(label40);
-        methodVisitor.visitFieldInsn(GETSTATIC, "kotlin/Unit", "INSTANCE", "Lkotlin/Unit;");
-        methodVisitor.visitVarInsn(ASTORE, 9);
-        methodVisitor.visitLabel(label6);
-        methodVisitor.visitLineNumber(156, label6);
-        methodVisitor.visitVarInsn(ALOAD, 3);
-        methodVisitor.visitVarInsn(ALOAD, 6);
-        methodVisitor.visitMethodInsn(
-            INVOKESTATIC,
-            "kotlinx/coroutines/channels/ChannelsKt",
-            "cancelConsumed",
-            "(Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/lang/Throwable;)V",
-            false);
-        Label label41 = new Label();
-        methodVisitor.visitLabel(label41);
-        methodVisitor.visitLineNumber(151, label41);
-        Label label42 = new Label();
-        methodVisitor.visitJumpInsn(GOTO, label42);
-        methodVisitor.visitLabel(label2);
-        methodVisitor.visitLineNumber(152, label2);
-        methodVisitor.visitFrame(
-            Opcodes.F_FULL,
-            11,
-            new Object[] {
-              "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
-              "java/lang/Object",
-              "kotlinx/coroutines/CoroutineScope",
-              "kotlinx/coroutines/channels/ReceiveChannel",
-              "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber",
-              Opcodes.INTEGER,
-              Opcodes.NULL,
-              Opcodes.TOP,
-              Opcodes.TOP,
-              Opcodes.TOP,
-              "java/lang/Object"
-            },
-            1,
-            new Object[] {"java/lang/Throwable"});
-        methodVisitor.visitVarInsn(ASTORE, 9);
-        Label label43 = new Label();
-        methodVisitor.visitLabel(label43);
-        methodVisitor.visitLineNumber(153, label43);
-        methodVisitor.visitVarInsn(ALOAD, 9);
-        methodVisitor.visitVarInsn(ASTORE, 6);
-        Label label44 = new Label();
-        methodVisitor.visitLabel(label44);
-        methodVisitor.visitLineNumber(154, label44);
-        methodVisitor.visitVarInsn(ALOAD, 9);
-        methodVisitor.visitInsn(ATHROW);
-        methodVisitor.visitLabel(label7);
-        methodVisitor.visitLineNumber(155, label7);
-        methodVisitor.visitFrame(
-            Opcodes.F_FULL,
-            11,
-            new Object[] {
-              "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
-              "java/lang/Object",
-              "kotlinx/coroutines/CoroutineScope",
-              "kotlinx/coroutines/channels/ReceiveChannel",
-              "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber",
-              Opcodes.INTEGER,
-              "java/lang/Throwable",
-              Opcodes.TOP,
-              Opcodes.TOP,
-              Opcodes.TOP,
-              "java/lang/Object"
-            },
-            1,
-            new Object[] {"java/lang/Throwable"});
-        methodVisitor.visitVarInsn(ASTORE, 9);
-        methodVisitor.visitLabel(label8);
-        methodVisitor.visitLineNumber(156, label8);
-        methodVisitor.visitVarInsn(ALOAD, 3);
-        methodVisitor.visitVarInsn(ALOAD, 6);
-        methodVisitor.visitMethodInsn(
-            INVOKESTATIC,
-            "kotlinx/coroutines/channels/ChannelsKt",
-            "cancelConsumed",
-            "(Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/lang/Throwable;)V",
-            false);
-        methodVisitor.visitVarInsn(ALOAD, 9);
-        methodVisitor.visitInsn(ATHROW);
-        methodVisitor.visitLabel(label10);
-        methodVisitor.visitLineNumber(73, label10);
-        methodVisitor.visitFrame(
-            Opcodes.F_FULL,
-            11,
-            new Object[] {
-              "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
-              "java/lang/Object",
-              "kotlinx/coroutines/CoroutineScope",
-              Opcodes.TOP,
-              Opcodes.TOP,
-              Opcodes.TOP,
-              Opcodes.TOP,
-              Opcodes.TOP,
-              Opcodes.TOP,
-              Opcodes.TOP,
-              "java/lang/Object"
-            },
-            1,
-            new Object[] {"kotlinx/coroutines/channels/ClosedReceiveChannelException"});
-        methodVisitor.visitVarInsn(ASTORE, 3);
-        methodVisitor.visitLabel(label42);
-        methodVisitor.visitLineNumber(75, label42);
-        methodVisitor.visitFrame(
-            Opcodes.F_FULL,
-            11,
-            new Object[] {
-              "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
-              "java/lang/Object",
-              "kotlinx/coroutines/CoroutineScope",
-              "java/lang/Object",
-              Opcodes.TOP,
-              Opcodes.TOP,
-              Opcodes.TOP,
-              Opcodes.TOP,
-              Opcodes.TOP,
-              Opcodes.TOP,
-              "java/lang/Object"
-            },
-            0,
-            new Object[] {});
-        methodVisitor.visitFieldInsn(GETSTATIC, "kotlin/Unit", "INSTANCE", "Lkotlin/Unit;");
-        methodVisitor.visitInsn(ARETURN);
-        methodVisitor.visitLabel(label15);
-        methodVisitor.visitFrame(
-            Opcodes.F_FULL,
-            11,
-            new Object[] {
-              "io/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1",
-              "java/lang/Object",
-              Opcodes.TOP,
-              Opcodes.TOP,
-              Opcodes.TOP,
-              Opcodes.TOP,
-              Opcodes.TOP,
-              Opcodes.TOP,
-              Opcodes.TOP,
-              Opcodes.TOP,
-              "java/lang/Object"
-            },
-            0,
-            new Object[] {});
-        methodVisitor.visitTypeInsn(NEW, "java/lang/IllegalStateException");
-        methodVisitor.visitInsn(DUP);
-        methodVisitor.visitLdcInsn("call to 'resume' before 'invoke' with coroutine");
-        methodVisitor.visitMethodInsn(
-            INVOKESPECIAL,
-            "java/lang/IllegalStateException",
-            "<init>",
-            "(Ljava/lang/String;)V",
-            false);
-        methodVisitor.visitInsn(ATHROW);
-        methodVisitor.visitLocalVariable(
-            "$this$launch", "Lkotlinx/coroutines/CoroutineScope;", null, label9, label13, 2);
-        methodVisitor.visitLocalVariable(
-            "$this$launch", "Lkotlinx/coroutines/CoroutineScope;", null, label3, label14, 2);
-        methodVisitor.visitLocalVariable(
-            "$this$launch", "Lkotlinx/coroutines/CoroutineScope;", null, label5, label23, 2);
-        methodVisitor.visitLocalVariable(
-            "$this$consume$iv",
-            "Lkotlinx/coroutines/channels/ReceiveChannel;",
-            null,
-            label18,
-            label13,
-            3);
-        methodVisitor.visitLocalVariable(
-            "$this$consume$iv",
-            "Lkotlinx/coroutines/channels/ReceiveChannel;",
-            null,
-            label34,
-            label14,
-            3);
-        methodVisitor.visitLocalVariable(
-            "$this$consume$iv",
-            "Lkotlinx/coroutines/channels/ReceiveChannel;",
-            null,
-            label39,
-            label40,
-            3);
-        methodVisitor.visitLocalVariable(
-            "$this$consume$iv",
-            "Lkotlinx/coroutines/channels/ReceiveChannel;",
-            null,
-            label40,
-            label41,
-            3);
-        methodVisitor.visitLocalVariable(
-            "$this$consume$iv",
-            "Lkotlinx/coroutines/channels/ReceiveChannel;",
-            null,
-            label2,
-            label10,
-            3);
-        methodVisitor.visitLocalVariable(
-            "cause$iv", "Ljava/lang/Throwable;", null, label0, label13, 6);
-        methodVisitor.visitLocalVariable(
-            "cause$iv", "Ljava/lang/Throwable;", null, label34, label14, 6);
-        methodVisitor.visitLocalVariable(
-            "cause$iv", "Ljava/lang/Throwable;", null, label39, label40, 6);
-        methodVisitor.visitLocalVariable(
-            "cause$iv", "Ljava/lang/Throwable;", null, label40, label41, 6);
-        methodVisitor.visitLocalVariable(
-            "cause$iv", "Ljava/lang/Throwable;", null, label2, label44, 6);
-        methodVisitor.visitLocalVariable(
-            "cause$iv", "Ljava/lang/Throwable;", null, label44, label10, 6);
-        methodVisitor.visitLocalVariable(
-            "buffer", "Ljava/nio/ByteBuffer;", null, label25, label27, 8);
-        methodVisitor.visitLocalVariable(
-            "buffer", "Ljava/nio/ByteBuffer;", null, label26, label4, 8);
-        methodVisitor.visitLocalVariable("e$iv", "Ljava/lang/Throwable;", null, label43, label7, 9);
-        methodVisitor.visitLocalVariable(
-            "$i$a$-consume-JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1$1",
-            "I",
-            null,
-            label22,
-            label13,
-            7);
-        methodVisitor.visitLocalVariable("$i$f$consume", "I", null, label19, label13, 5);
-        methodVisitor.visitLocalVariable(
-            "this",
-            "Lio/ktor/client/engine/java/JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1;",
-            null,
-            label16,
-            label15,
-            0);
-        methodVisitor.visitLocalVariable(
-            "$result", "Ljava/lang/Object;", null, label16, label15, 1);
-        methodVisitor.visitLocalVariable(
-            "$i$a$-consume-JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1$1",
-            "I",
-            null,
-            label33,
-            label14,
-            7);
-        methodVisitor.visitLocalVariable("$i$f$consume", "I", null, label32, label14, 5);
-        methodVisitor.visitLocalVariable(
-            "$i$a$-consume-JavaHttpResponseBodyHandler$JavaHttpResponseBodySubscriber$1$1",
-            "I",
-            null,
-            label38,
-            label40,
-            7);
-        methodVisitor.visitLocalVariable("$i$f$consume", "I", null, label37, label10, 5);
-        methodVisitor.visitMaxs(5, 11);
-        methodVisitor.visitEnd();
-      }
-      {
-        methodVisitor =
-            classWriter.visitMethod(
-                ACC_PUBLIC | ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
-        methodVisitor.visitCode();
-        Label label0 = new Label();
-        methodVisitor.visitLabel(label0);
-        methodVisitor.visitLineNumber(70, label0);
-        methodVisitor.visitTypeInsn(
-            NEW, "com/android/tools/r8/cf/CfDebugLocalStackMapVerificationTest$Main");
-        methodVisitor.visitInsn(DUP);
-        methodVisitor.visitMethodInsn(
-            INVOKESPECIAL,
-            "com/android/tools/r8/cf/CfDebugLocalStackMapVerificationTest$Main",
-            "<init>",
-            "()V",
-            false);
-        methodVisitor.visitInsn(ACONST_NULL);
-        methodVisitor.visitMethodInsn(
-            INVOKEVIRTUAL,
-            "com/android/tools/r8/cf/CfDebugLocalStackMapVerificationTest$Main",
-            "invokeSuspend",
-            "(Ljava/lang/Object;)Ljava/lang/Object;",
-            false);
-        methodVisitor.visitInsn(POP);
-        Label label1 = new Label();
-        methodVisitor.visitLabel(label1);
-        methodVisitor.visitLineNumber(71, label1);
-        methodVisitor.visitInsn(RETURN);
-        Label label2 = new Label();
-        methodVisitor.visitLabel(label2);
-        methodVisitor.visitLocalVariable("args", "[Ljava/lang/String;", null, label0, label2, 0);
-        methodVisitor.visitMaxs(2, 1);
-        methodVisitor.visitEnd();
-      }
-      classWriter.visitEnd();
-
-      return classWriter.toByteArray();
-    }
+    testForR8(parameters.getBackend())
+        .debug()
+        .addProgramClasses(SmallRepro.class)
+        .addKeepAllAttributes()
+        .addDontShrink()
+        .addDontObfuscate()
+        .addDontOptimize()
+        .addOptionsModification(
+            options -> {
+              options.testing.neverReuseCfLocalRegisters = true;
+              // TODO(b/237567012): Remove option when resolved.
+              options.enableCheckAllInstructionsDuringStackMapVerification = true;
+            })
+        .run(parameters.getRuntime(), SmallRepro.class)
+        .assertSuccessWithOutputThatMatches(containsString("FOO"));
   }
 }
diff --git a/src/test/java/com/android/tools/r8/cf/CloserTestRunner.java b/src/test/java/com/android/tools/r8/cf/CloserTestRunner.java
index bce6ae0..dea6642 100644
--- a/src/test/java/com/android/tools/r8/cf/CloserTestRunner.java
+++ b/src/test/java/com/android/tools/r8/cf/CloserTestRunner.java
@@ -44,7 +44,7 @@
         .addProgramClasses(CloserTest.class)
         .addKeepMainRule(CloserTest.class)
         .setMode(CompilationMode.RELEASE)
-        .noMinification()
+        .addDontObfuscate()
         .noTreeShaking()
         .enableInliningAnnotations()
         .compile()
diff --git a/src/test/java/com/android/tools/r8/cf/DebugInfoTestRunner.java b/src/test/java/com/android/tools/r8/cf/DebugInfoTestRunner.java
index 75a8b55..0d62e66 100644
--- a/src/test/java/com/android/tools/r8/cf/DebugInfoTestRunner.java
+++ b/src/test/java/com/android/tools/r8/cf/DebugInfoTestRunner.java
@@ -53,7 +53,7 @@
     return testForR8(backend)
         .debug()
         .noTreeShaking()
-        .noMinification()
+        .addDontObfuscate()
         .addOptionsModification(o -> o.invalidDebugInfoFatal = true);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/cf/GetClassLdcClassTest.java b/src/test/java/com/android/tools/r8/cf/GetClassLdcClassTest.java
index c5284c0..07d16af 100644
--- a/src/test/java/com/android/tools/r8/cf/GetClassLdcClassTest.java
+++ b/src/test/java/com/android/tools/r8/cf/GetClassLdcClassTest.java
@@ -65,7 +65,7 @@
         .addKeepMainRule(TestClass.class)
         // We cannot keep class Runner, as that prohibits getClass optimization.
         // Instead disable minification and inlining of the Runner class and method.
-        .noMinification()
+        .addDontObfuscate()
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .run(parameters.getRuntime(), TestClass.class)
@@ -90,7 +90,7 @@
             .addKeepMainRule(TestClass.class)
             // We cannot keep class Runner, as that prohibits getClass optimization.
             // Instead disable minification and inlining of the Runner class and method.
-            .noMinification()
+            .addDontObfuscate()
             .enableInliningAnnotations()
             .enableNeverClassInliningAnnotations()
             .run(parameters.getRuntime(), TestClass.class);
diff --git a/src/test/java/com/android/tools/r8/cf/KeepDeserializeLambdaMethodTestRunner.java b/src/test/java/com/android/tools/r8/cf/KeepDeserializeLambdaMethodTestRunner.java
index 7b52abd..0bdb4f8 100644
--- a/src/test/java/com/android/tools/r8/cf/KeepDeserializeLambdaMethodTestRunner.java
+++ b/src/test/java/com/android/tools/r8/cf/KeepDeserializeLambdaMethodTestRunner.java
@@ -91,9 +91,7 @@
           // TODO(b/148836254): Support deserialized lambdas without the need of additional rules.
           "-keep class * { private static synthetic void lambda$*(); }");
     } else {
-      builder
-          .noMinification()
-          .noTreeShaking();
+      builder.addDontObfuscate().noTreeShaking();
     }
     builder
         .run(parameters.getRuntime(), getMainClass())
diff --git a/src/test/java/com/android/tools/r8/cf/PrintSeedsWithDeserializeLambdaMethodTest.java b/src/test/java/com/android/tools/r8/cf/PrintSeedsWithDeserializeLambdaMethodTest.java
index 33b13a9..255e2be 100644
--- a/src/test/java/com/android/tools/r8/cf/PrintSeedsWithDeserializeLambdaMethodTest.java
+++ b/src/test/java/com/android/tools/r8/cf/PrintSeedsWithDeserializeLambdaMethodTest.java
@@ -58,7 +58,7 @@
             options -> options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces())
         .addPrintSeeds()
         .allowStdoutMessages()
-        .noMinification()
+        .addDontObfuscate()
         .noTreeShaking()
         .run(parameters.getRuntime(), getMainClass())
         .assertSuccessWithOutput(EXPECTED)
diff --git a/src/test/java/com/android/tools/r8/cf/TryRangeTestRunner.java b/src/test/java/com/android/tools/r8/cf/TryRangeTestRunner.java
index 8f5dc82..7c9d4ac 100644
--- a/src/test/java/com/android/tools/r8/cf/TryRangeTestRunner.java
+++ b/src/test/java/com/android/tools/r8/cf/TryRangeTestRunner.java
@@ -39,7 +39,7 @@
         .addProgramClasses(TryRangeTest.class)
         .addKeepMainRule(TryRangeTest.class)
         .setMode(CompilationMode.RELEASE)
-        .noMinification()
+        .addDontObfuscate()
         .noTreeShaking()
         .enableInliningAnnotations()
         .addOptionsModification(o -> o.enableLoadStoreOptimization = false)
@@ -54,7 +54,7 @@
             .addProgramClasses(TryRangeTestLimitRange.class)
             .addKeepMainRule(TryRangeTestLimitRange.class)
             .setMode(CompilationMode.RELEASE)
-            .noMinification()
+            .addDontObfuscate()
             .noTreeShaking()
             .enableInliningAnnotations()
             .addOptionsModification(
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingWithStartupClassesTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingWithStartupClassesTest.java
index 30b9700..417a097 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingWithStartupClassesTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingWithStartupClassesTest.java
@@ -5,10 +5,11 @@
 package com.android.tools.r8.classmerging.horizontal;
 
 import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.StartupProfileProvider;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.experimental.startup.StartupClass;
-import com.android.tools.r8.experimental.startup.StartupConfiguration;
+import com.android.tools.r8.experimental.startup.StartupProfile;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.google.common.collect.ImmutableList;
@@ -49,21 +50,21 @@
         .addOptionsModification(
             options -> {
               DexItemFactory dexItemFactory = options.dexItemFactory();
-              options
-                  .getStartupOptions()
-                  .setStartupConfiguration(
-                      StartupConfiguration.builder()
-                          .apply(
-                              builder ->
-                                  getStartupClasses()
-                                      .forEach(
-                                          startupClass ->
-                                              builder.addStartupClass(
-                                                  StartupClass.dexBuilder()
-                                                      .setClassReference(
-                                                          toDexType(startupClass, dexItemFactory))
-                                                      .build())))
-                          .build());
+              StartupProfile startupProfile =
+                  StartupProfile.builder()
+                      .apply(
+                          builder ->
+                              getStartupClasses()
+                                  .forEach(
+                                      startupClass ->
+                                          builder.addStartupClass(
+                                              StartupClass.dexBuilder()
+                                                  .setClassReference(
+                                                      toDexType(startupClass, dexItemFactory))
+                                                  .build())))
+                      .build();
+              StartupProfileProvider startupProfileProvider = startupProfile::serializeToString;
+              options.getStartupOptions().setStartupProfileProvider(startupProfileProvider);
             })
         .addHorizontallyMergedClassesInspector(
             inspector ->
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/PinnedClassMemberReferenceTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/PinnedClassMemberReferenceTest.java
index 41694f6..db6cc21 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/PinnedClassMemberReferenceTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/PinnedClassMemberReferenceTest.java
@@ -26,7 +26,7 @@
     return testForR8(parameters.getBackend())
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
-        .noMinification()
+        .addDontObfuscate()
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .setMinApi(parameters.getApiLevel());
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerInterfaceCollisionTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerInterfaceCollisionTest.java
index fb7e05e..07f3fce 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerInterfaceCollisionTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerInterfaceCollisionTest.java
@@ -31,7 +31,7 @@
     testForR8(parameters.getBackend())
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
-        .noMinification()
+        .addDontObfuscate()
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .enableNoVerticalClassMergingAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerInterfaceFixedCollisionTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerInterfaceFixedCollisionTest.java
index 4dfb6df..43bbf33 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerInterfaceFixedCollisionTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerInterfaceFixedCollisionTest.java
@@ -31,7 +31,7 @@
     testForR8(parameters.getBackend())
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
-        .noMinification()
+        .addDontObfuscate()
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .enableNoVerticalClassMergingAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerInterfaceImplementedByParentTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerInterfaceImplementedByParentTest.java
index 65b57aa..5904824 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerInterfaceImplementedByParentTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerInterfaceImplementedByParentTest.java
@@ -31,7 +31,7 @@
     testForR8(parameters.getBackend())
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
-        .noMinification()
+        .addDontObfuscate()
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .enableNoVerticalClassMergingAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerSubClassCollisionTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerSubClassCollisionTest.java
index 5e8b88d..b00bc63 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerSubClassCollisionTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerSubClassCollisionTest.java
@@ -26,7 +26,7 @@
     testForR8(parameters.getBackend())
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
-        .noMinification()
+        .addDontObfuscate()
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .enableNoVerticalClassMergingAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontalstatic/InliningAfterStaticClassMergerTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontalstatic/InliningAfterStaticClassMergerTest.java
index 75e26f1..18017a1 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontalstatic/InliningAfterStaticClassMergerTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontalstatic/InliningAfterStaticClassMergerTest.java
@@ -86,7 +86,7 @@
             .addOptionsModification(
                 options -> options.libraryInterfacesMayHaveStaticInitialization = true)
             .enableMemberValuePropagationAnnotations()
-            .noMinification()
+            .addDontObfuscate()
             .setMinApi(parameters.getApiLevel())
             .run(parameters.getRuntime(), TestClass.class)
             .assertSuccessWithOutput(expected)
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontalstatic/StaticClassMergerVisibilityTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontalstatic/StaticClassMergerVisibilityTest.java
index 189ac21..7555576 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontalstatic/StaticClassMergerVisibilityTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontalstatic/StaticClassMergerVisibilityTest.java
@@ -46,7 +46,7 @@
                     .assertMergedInto(C.class, D.class))
         .enableInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
-        .noMinification()
+        .addDontObfuscate()
         .run(parameters.getRuntime(), Main.class)
         .assertSuccessWithOutputLines("A.print()", "B.print()", "C.print()", "D.print()")
         .inspect(
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/B141942381.java b/src/test/java/com/android/tools/r8/classmerging/vertical/B141942381.java
index d840b52..5ab767d 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/B141942381.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/B141942381.java
@@ -52,7 +52,7 @@
         .setMinApi(parameters.getApiLevel())
         .addKeepAttributes("Signatures")
         .enableNeverClassInliningAnnotations()
-        .noMinification()
+        .addDontObfuscate()
         .compile()
         .inspect(this::inspect)
         .run(parameters.getRuntime(), TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/IncorrectRewritingOfInvokeSuperTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/IncorrectRewritingOfInvokeSuperTest.java
index 505aa07..2ecdb10 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/IncorrectRewritingOfInvokeSuperTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/IncorrectRewritingOfInvokeSuperTest.java
@@ -35,7 +35,7 @@
         .addKeepMainRule(TestClass.class)
         .addOptionsModification(options -> options.enableUnusedInterfaceRemoval = false)
         .enableInliningAnnotations()
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getRuntime())
         .run(parameters.getRuntime(), TestClass.class)
         .assertSuccess();
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
index cbbb43e..ae53238 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
@@ -101,7 +101,7 @@
             .addProgramFiles(EXAMPLE_JAR)
             .addKeepRuleFiles(proguardConfig)
             .enableProguardTestOptions()
-            .noMinification()
+            .addDontObfuscate()
             .addOptionsModification(optionsConsumer)
             .setMinApi(parameters.getApiLevel())
             .compile()
@@ -1183,7 +1183,7 @@
                     appBuilder.addProgramResourceProvider(provider);
                   }
                 })
-            .noMinification()
+            .addDontObfuscate()
             .enableProguardTestOptions()
             .setMinApi(parameters.getApiLevel())
             .compile();
diff --git a/src/test/java/com/android/tools/r8/compatproguard/CompatKeepClassMemberNamesTestRunner.java b/src/test/java/com/android/tools/r8/compatproguard/CompatKeepClassMemberNamesTestRunner.java
index 68e882e..dba64d3 100644
--- a/src/test/java/com/android/tools/r8/compatproguard/CompatKeepClassMemberNamesTestRunner.java
+++ b/src/test/java/com/android/tools/r8/compatproguard/CompatKeepClassMemberNamesTestRunner.java
@@ -114,7 +114,7 @@
             .addProgramClasses(CLASSES)
             .addKeepMainRule(MAIN_CLASS)
             .addInliningAnnotations()
-            .noMinification()
+            .addDontObfuscate()
             .compile());
   }
 
@@ -143,7 +143,7 @@
           RR extends TestRunResult<RR>,
           T extends TestShrinkerBuilder<C, B, CR, RR, T>>
       T buildWithMembersRule(TestShrinkerBuilder<C, B, CR, RR, T> builder) {
-    return buildWithMembersRuleEnableMinification(builder).noMinification();
+    return buildWithMembersRuleEnableMinification(builder).addDontObfuscate();
   }
 
   private <CR extends TestCompileResult<CR, RR>, RR extends TestRunResult<RR>>
@@ -211,7 +211,7 @@
         .addKeepMainRule(MAIN_CLASS)
         .addKeepRules("-keepclassmembers " + KEEP_RULE_NON_STATIC)
         .addInliningAnnotations()
-        .noMinification();
+        .addDontObfuscate();
   }
 
   @Test
@@ -315,7 +315,7 @@
             .addProgramClasses(CLASSES)
             .addKeepMainRule(MAIN_CLASS)
             .addInliningAnnotations()
-            .noMinification()
+            .addDontObfuscate()
             .addKeepRules("-keepclassmembers class " + Bar.class.getTypeName())
             .compile());
   }
@@ -344,7 +344,7 @@
           RR extends TestRunResult<RR>,
           T extends TestShrinkerBuilder<C, B, CR, RR, T>>
       T buildWithMemberNamesRule(TestShrinkerBuilder<C, B, CR, RR, T> builder) {
-    return buildWithMemberNamesRuleEnableMinification(builder).noMinification();
+    return buildWithMemberNamesRuleEnableMinification(builder).addDontObfuscate();
   }
 
   private <CR extends TestCompileResult<CR, RR>, RR extends TestRunResult<RR>>
diff --git a/src/test/java/com/android/tools/r8/compatproguard/ForNameTest.java b/src/test/java/com/android/tools/r8/compatproguard/ForNameTest.java
index a5a9f0a1..97cb7c9 100644
--- a/src/test/java/com/android/tools/r8/compatproguard/ForNameTest.java
+++ b/src/test/java/com/android/tools/r8/compatproguard/ForNameTest.java
@@ -78,7 +78,7 @@
                     // Add main dex rule to disable Class.forName() optimization.
                     .addMainDexRules("-keep class " + CLASS_NAME)
                     .addDontOptimize()
-                    .noMinification()
+                    .addDontObfuscate()
                     .noTreeShaking()
                     .setMinApi(AndroidApiLevel.B));
 
diff --git a/src/test/java/com/android/tools/r8/debug/KotlinStdLibCompilationTest.java b/src/test/java/com/android/tools/r8/debug/KotlinStdLibCompilationTest.java
index 2c3a478..0f0a782 100644
--- a/src/test/java/com/android/tools/r8/debug/KotlinStdLibCompilationTest.java
+++ b/src/test/java/com/android/tools/r8/debug/KotlinStdLibCompilationTest.java
@@ -57,7 +57,7 @@
         .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.LATEST))
         .addKeepAllAttributes()
         .allowDiagnosticWarningMessages()
-        .noMinification()
+        .addDontObfuscate()
         .noTreeShaking()
         .setMode(CompilationMode.DEBUG)
         .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/debug/LambdaOuterContextTestRunner.java b/src/test/java/com/android/tools/r8/debug/LambdaOuterContextTestRunner.java
index 99a19f5..0e77b85 100644
--- a/src/test/java/com/android/tools/r8/debug/LambdaOuterContextTestRunner.java
+++ b/src/test/java/com/android/tools/r8/debug/LambdaOuterContextTestRunner.java
@@ -11,7 +11,6 @@
 import com.android.tools.r8.debug.LambdaOuterContextTest.Converter;
 import com.android.tools.r8.utils.StringUtils;
 import org.apache.harmony.jpda.tests.framework.jdwp.Value;
-import org.junit.Ignore;
 import org.junit.Test;
 
 public class LambdaOuterContextTestRunner extends DebugTestBase {
@@ -40,7 +39,7 @@
         testForR8(Backend.CF)
             .addProgramClassesAndInnerClasses(CLASS)
             .debug()
-            .noMinification()
+            .addDontObfuscate()
             .noTreeShaking()
             .compile();
     compileResult.run(CLASS).assertSuccessWithOutput(EXPECTED);
diff --git a/src/test/java/com/android/tools/r8/debug/LineNumberOptimizationTest.java b/src/test/java/com/android/tools/r8/debug/LineNumberOptimizationTest.java
index af193de..c161a96 100644
--- a/src/test/java/com/android/tools/r8/debug/LineNumberOptimizationTest.java
+++ b/src/test/java/com/android/tools/r8/debug/LineNumberOptimizationTest.java
@@ -52,7 +52,7 @@
             .setMinApi(parameters.getApiLevel())
             .setMode(dontOptimizeByEnablingDebug ? CompilationMode.DEBUG : CompilationMode.RELEASE)
             .noTreeShaking()
-            .noMinification()
+            .addDontObfuscate()
             .addKeepAttributeSourceFile()
             .addKeepAttributeLineNumberTable()
             .addOptionsModification(
diff --git a/src/test/java/com/android/tools/r8/debug/LoadInvokeLoadOptimizationTestRunner.java b/src/test/java/com/android/tools/r8/debug/LoadInvokeLoadOptimizationTestRunner.java
index 9242d61..e382d39 100644
--- a/src/test/java/com/android/tools/r8/debug/LoadInvokeLoadOptimizationTestRunner.java
+++ b/src/test/java/com/android/tools/r8/debug/LoadInvokeLoadOptimizationTestRunner.java
@@ -40,7 +40,7 @@
           temp ->
               testForR8(temp, backend)
                   .noTreeShaking()
-                  .noMinification()
+                  .addDontObfuscate()
                   .addKeepRules("-keepattributes SourceFile,LineNumberTable")
                   .addProgramClasses(CLASS)
                   .setMinApi(minApi)
diff --git a/src/test/java/com/android/tools/r8/debuginfo/KotlinDebugInfoTestRunner.java b/src/test/java/com/android/tools/r8/debuginfo/KotlinDebugInfoTestRunner.java
index 80a51c8..cb0ee5c 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/KotlinDebugInfoTestRunner.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/KotlinDebugInfoTestRunner.java
@@ -85,7 +85,7 @@
         .addOptionsModification(options -> options.invalidDebugInfoFatal = true)
         .apply(configuration)
         .debug()
-        .noMinification()
+        .addDontObfuscate()
         .noTreeShaking()
         .compile()
         .run(parameters.getRuntime(), className)
diff --git a/src/test/java/com/android/tools/r8/desugar/DefaultLambdaWithInvokeInterfaceTestRunner.java b/src/test/java/com/android/tools/r8/desugar/DefaultLambdaWithInvokeInterfaceTestRunner.java
index c04e3eb..0136d59 100644
--- a/src/test/java/com/android/tools/r8/desugar/DefaultLambdaWithInvokeInterfaceTestRunner.java
+++ b/src/test/java/com/android/tools/r8/desugar/DefaultLambdaWithInvokeInterfaceTestRunner.java
@@ -57,12 +57,13 @@
 
   @Test
   public void testR8Cf() throws Throwable {
-    R8TestCompileResult compileResult = testForR8(Backend.CF)
-        .addProgramClassesAndInnerClasses(CLASS)
-        .noMinification()
-        .noTreeShaking()
-        .debug()
-        .compile();
+    R8TestCompileResult compileResult =
+        testForR8(Backend.CF)
+            .addProgramClassesAndInnerClasses(CLASS)
+            .addDontObfuscate()
+            .noTreeShaking()
+            .debug()
+            .compile();
     compileResult
         // TODO(b/123506120): Add .assertNoMessages()
         .run(CLASS)
diff --git a/src/test/java/com/android/tools/r8/desugar/DefaultLambdaWithSelfReferenceTestRunner.java b/src/test/java/com/android/tools/r8/desugar/DefaultLambdaWithSelfReferenceTestRunner.java
index 409d776..499527f 100644
--- a/src/test/java/com/android/tools/r8/desugar/DefaultLambdaWithSelfReferenceTestRunner.java
+++ b/src/test/java/com/android/tools/r8/desugar/DefaultLambdaWithSelfReferenceTestRunner.java
@@ -103,7 +103,7 @@
         testForR8(parameters.getBackend())
             .addProgramClassesAndInnerClasses(CLASS)
             .setMinApi(parameters.getApiLevel())
-            .noMinification()
+            .addDontObfuscate()
             .noTreeShaking()
             .addKeepAllAttributes()
             .debug()
diff --git a/src/test/java/com/android/tools/r8/desugar/DefaultLambdaWithUnderscoreThisTestRunner.java b/src/test/java/com/android/tools/r8/desugar/DefaultLambdaWithUnderscoreThisTestRunner.java
index 9860a15..263a87e 100644
--- a/src/test/java/com/android/tools/r8/desugar/DefaultLambdaWithUnderscoreThisTestRunner.java
+++ b/src/test/java/com/android/tools/r8/desugar/DefaultLambdaWithUnderscoreThisTestRunner.java
@@ -114,7 +114,7 @@
         testForR8(parameters.getBackend())
             .addProgramClassesAndInnerClasses(CLASS)
             .addKeepAllClassesRule()
-            .noMinification()
+            .addDontObfuscate()
             .setMode(CompilationMode.DEBUG)
             .addOptionsModification(
                 internalOptions -> {
diff --git a/src/test/java/com/android/tools/r8/desugar/DesugarLambdaContextDuplicateInLibraryTest.java b/src/test/java/com/android/tools/r8/desugar/DesugarLambdaContextDuplicateInLibraryTest.java
index 98839fa..c712d37 100644
--- a/src/test/java/com/android/tools/r8/desugar/DesugarLambdaContextDuplicateInLibraryTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/DesugarLambdaContextDuplicateInLibraryTest.java
@@ -48,7 +48,7 @@
   @Test
   public void testOnlyProgram() throws Exception {
     testForR8(parameters.getBackend())
-        .noMinification()
+        .addDontObfuscate()
         .addProgramClasses(PROGRAM)
         .addProgramClasses(LIBRARY)
         .addKeepMainRule(MAIN)
@@ -73,7 +73,7 @@
   @Test
   public void testDuplicate() throws Exception {
     testForR8(parameters.getBackend())
-        .noMinification() // Don't minify so the name collision will happen.
+        .addDontObfuscate() // Don't minify so the name collision will happen.
         .addProgramClasses(PROGRAM)
         .addProgramClasses(LIBRARY)
         .addLibraryClasses(LIBRARY)
diff --git a/src/test/java/com/android/tools/r8/desugar/DesugaredLambdaImplementationInliningTest.java b/src/test/java/com/android/tools/r8/desugar/DesugaredLambdaImplementationInliningTest.java
index cb1b96e..6a0f410 100644
--- a/src/test/java/com/android/tools/r8/desugar/DesugaredLambdaImplementationInliningTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/DesugaredLambdaImplementationInliningTest.java
@@ -45,7 +45,7 @@
     testForR8Compat(parameters.getBackend())
         .addProgramClasses(TestClass.class)
         .addKeepMainRule(TestClass.class)
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), TestClass.class)
         .assertSuccessWithOutput(EXPECTED)
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/BackportMainDexTest.java b/src/test/java/com/android/tools/r8/desugar/backports/BackportMainDexTest.java
index 81f0674..a3bec4a 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/BackportMainDexTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/BackportMainDexTest.java
@@ -238,7 +238,7 @@
     MainDexConsumer mainDexConsumer = parameters.isDexRuntime() ? new MainDexConsumer() : null;
     testForR8(parameters.getBackend())
         .debug() // Use debug mode to force a minimal main dex.
-        .noMinification() // Disable minification so we can inspect the synthetic names.
+        .addDontObfuscate() // Disable minification so we can inspect the synthetic names.
         .applyIf(mainDexConsumer != null, b -> b.setProgramConsumer(mainDexConsumer))
         .addProgramClasses(CLASSES)
         .addKeepMainRule(TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/TestBackportedNotPresentInAndroidJar.java b/src/test/java/com/android/tools/r8/desugar/backports/TestBackportedNotPresentInAndroidJar.java
index 195dfd2..1ca4309 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/TestBackportedNotPresentInAndroidJar.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/TestBackportedNotPresentInAndroidJar.java
@@ -79,6 +79,9 @@
   @Test
   public void testBackportedMethodsPerAPILevel() throws Exception {
     for (AndroidApiLevel apiLevel : AndroidApiLevel.values()) {
+      if (apiLevel == AndroidApiLevel.MASTER) {
+        continue;
+      }
       if (apiLevel == AndroidApiLevel.T) {
         continue;
       }
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ConcurrentHashMapSubclassV2Test.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ConcurrentHashMapSubclassV2Test.java
index 20f26fc..102902a 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ConcurrentHashMapSubclassV2Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ConcurrentHashMapSubclassV2Test.java
@@ -12,8 +12,8 @@
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification;
 import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.StringUtils;
-import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.ConcurrentHashMap;
 import org.junit.Assume;
@@ -52,34 +52,39 @@
     this.compilationMode = compilationMode;
   }
 
+  private boolean isDefinedOnBootClasspath() {
+    return parameters
+        .getRuntime()
+        .asDex()
+        .getMinApiLevel()
+        .isGreaterThanOrEqualTo(AndroidApiLevel.N);
+  }
+
   @Test
-  public void test() throws Exception {
-    Assume.assumeFalse(
-        "b/237701688", compilationMode.isDebug() && compilationSpecification == R8_L8SHRINK);
+  public void test() throws Throwable {
+    Assume.assumeTrue("b/237701688", compilationSpecification == R8_L8SHRINK);
     testForDesugaredLibrary(parameters, libraryDesugaringSpecification, compilationSpecification)
         .addInnerClasses(getClass())
-        .setMode(compilationMode)
         .addKeepMainRule(Executor.class)
+        .setMode(compilationMode)
+        .addOptionsModification(
+            options -> {
+              // Devirtualizing is correcting the invalid member-rebinding.
+              if (compilationMode.isRelease()) {
+                options.enableDevirtualization = false;
+              }
+            })
         .run(parameters.getRuntime(), Executor.class)
         .assertSuccessWithOutput(EXPECTED_RESULT);
   }
 
-  @SuppressWarnings("unchecked")
   static class Executor {
     public static void main(String[] args) {
-      StringListConcurrentHashMap<String> map = new StringListConcurrentHashMap<>();
-      map.putKeyAndArray("foo");
+      StringListConcurrentHashMap map = new StringListConcurrentHashMap();
+      map.putIfAbsent("foo", "bar");
       System.out.println(map.keys().nextElement());
     }
   }
 
-  static class StringListConcurrentHashMap<K> extends ConcurrentHashMap<K, List<String>> {
-    StringListConcurrentHashMap() {
-      super();
-    }
-
-    void putKeyAndArray(K key) {
-      this.putIfAbsent(key, new ArrayList<>());
-    }
-  }
+  static class StringListConcurrentHashMap extends ConcurrentHashMap<String, String> {}
 }
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LegacyDesugaredLibraryConfigurationParsingTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LegacyDesugaredLibraryConfigurationParsingTest.java
index 8d06a2f..c08abd3 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LegacyDesugaredLibraryConfigurationParsingTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LegacyDesugaredLibraryConfigurationParsingTest.java
@@ -5,6 +5,7 @@
 
 import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
 import static com.android.tools.r8.DiagnosticsMatcher.diagnosticOrigin;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
 import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.RELEASED_1_1_5;
 import static org.hamcrest.CoreMatchers.allOf;
 import static org.hamcrest.CoreMatchers.containsString;
@@ -17,6 +18,7 @@
 import com.android.tools.r8.TestDiagnosticMessagesImpl;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
+import com.android.tools.r8.errors.UnsupportedDesugaredLibraryConfigurationVersionDiagnostic;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
@@ -190,7 +192,14 @@
         diagnostics ->
             diagnostics.assertErrorsMatch(
                 allOf(
+                    diagnosticType(UnsupportedDesugaredLibraryConfigurationVersionDiagnostic.class),
                     diagnosticMessage(containsString("upgrade the D8/R8 compiler")),
+                    diagnosticMessage(
+                        containsString(
+                            "https://developer.android.com/studio/build/library-desugaring-versions")),
+                    diagnosticMessage(
+                        containsString(
+                            "https://developer.android.com/studio/build/library-desugaring")),
                     diagnosticOrigin(origin))));
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LocalDateEpochTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LocalDateEpochTest.java
index 3d1a75b..a1a428b 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LocalDateEpochTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LocalDateEpochTest.java
@@ -89,7 +89,7 @@
     DexField src = options.dexItemFactory().createField(date, date, epoch);
     HumanRewritingFlags rewritingFlags =
         HumanRewritingFlags.builder(options.reporter, Origin.unknown())
-            .retargetStaticField(src, desugarDate)
+            .retargetStaticField(src, src.withHolder(desugarDate, options.dexItemFactory()))
             .amendLibraryField(
                 src,
                 FieldAccessFlags.fromSharedAccessFlags(
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/WrapperEqualityTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/WrapperEqualityTest.java
index 77b7738..2956c19 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/WrapperEqualityTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/WrapperEqualityTest.java
@@ -177,6 +177,7 @@
       appConsumer = consumer;
     }
 
+    @SuppressWarnings("unchecked")
     public static void setSupplier(Supplier supplier) {
       appSupplier = supplier;
     }
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/StandardCharsetTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/StandardCharsetTest.java
index eb887cc..181cd69 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/StandardCharsetTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/StandardCharsetTest.java
@@ -17,7 +17,10 @@
 import java.io.IOException;
 import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -33,7 +36,7 @@
   private final CompilationSpecification compilationSpecification;
 
   private static final String EXPECTED_RESULT =
-      StringUtils.lines("%E3%81%8B", "%82%A0%82%A9%97%43%24%E3%81%8B", "true");
+      StringUtils.lines("%E3%81%8B", "%82%A0%82%A9%97%43%24%E3%81%8B", "true", "true", "written");
 
   @Parameters(name = "{0}, spec: {1}, {2}")
   public static List<Object[]> data() {
@@ -53,11 +56,11 @@
   }
 
   @Test
-  public void test() throws Exception {
+  public void test() throws Throwable {
     testForDesugaredLibrary(parameters, libraryDesugaringSpecification, compilationSpecification)
         .addProgramClassFileData(getProgramClassFileData())
         .addKeepMainRule(TestClass.class)
-        .run(parameters.getRuntime(), TestClass.class)
+        .run(parameters.getRuntime(), TestClass.class, temp.newFile().toString())
         .assertSuccessWithOutput(EXPECTED_RESULT);
   }
 
@@ -95,6 +98,17 @@
               + encode("$", "Shift_JIS")
               + encode("か", StandardCharsets.UTF_8));
       System.out.println(Character.isBmpCodePoint('か'));
+
+      System.out.println(Charset.defaultCharset() == StandardCharsets.UTF_8);
+
+      // The following Files methods internally uses UTF_8 references.
+      String path = args[0];
+      try {
+        Files.write(Paths.get(path), Collections.singleton("written"));
+        System.out.println(Files.readAllLines(Paths.get(path)).get(0));
+      } catch (IOException e) {
+        System.out.println("IOException");
+      }
     }
 
     // Replaced in the transformer by JDK 11 URLEncoder#encode.
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/legacy/L8TestWithLegacySpecification.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/legacy/L8TestWithLegacySpecification.java
index 3a6fd81..c0e4cf6 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/legacy/L8TestWithLegacySpecification.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/legacy/L8TestWithLegacySpecification.java
@@ -47,6 +47,7 @@
             .sorted()
             .filter(apiLevel -> apiLevel.isGreaterThanOrEqualTo(AndroidApiLevel.L))
             .filter(apiLevel -> apiLevel.isLessThan(AndroidApiLevel.ANDROID_PLATFORM))
+            .filter(apiLevel -> apiLevel != AndroidApiLevel.MASTER)
             .collect(Collectors.toList()),
         CompilationMode.values(),
         ImmutableList.of(
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/HelloWorldCompiledOnArtTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/HelloWorldCompiledOnArtTest.java
index dd5acbd..b7a1a0b 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/HelloWorldCompiledOnArtTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/HelloWorldCompiledOnArtTest.java
@@ -9,12 +9,10 @@
 import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
 import static junit.framework.TestCase.assertEquals;
 import static junit.framework.TestCase.assertTrue;
-import static org.junit.Assume.assumeTrue;
 
 import com.android.tools.r8.D8;
 import com.android.tools.r8.R8;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestRuntime;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.DexVm.Version;
 import com.android.tools.r8.ToolHelper.ProcessResult;
@@ -29,7 +27,6 @@
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.List;
-import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -38,25 +35,6 @@
 @RunWith(Parameterized.class)
 public class HelloWorldCompiledOnArtTest extends DesugaredLibraryTestBase {
 
-  // TODO(b/142621961): Create an abstraction to easily run tests on External DexR8.
-  // Manage pathMock in the abstraction.
-  private static Path pathMock;
-
-  @BeforeClass
-  public static void compilePathBackport() throws Exception {
-    assumeTrue("JDK8 is not checked-in on Windows", !ToolHelper.isWindows());
-    pathMock = getStaticTemp().newFolder("PathMock").toPath();
-    javac(TestRuntime.getCheckedInJdk8(), getStaticTemp())
-        .setOutputPath(pathMock)
-        .addSourceFiles(
-            getAllFilesWithSuffixInDirectory(Paths.get("src/test/r8OnArtBackport"), "java"))
-        .compile();
-  }
-
-  public static Path[] getPathBackport() throws Exception {
-    return getAllFilesWithSuffixInDirectory(pathMock, "class");
-  }
-
   private final TestParameters parameters;
   private final CompilationSpecification compilationSpecification;
   private final LibraryDesugaringSpecification libraryDesugaringSpecification;
@@ -65,7 +43,7 @@
   public static List<Object[]> data() {
     return buildParameters(
         getTestParameters()
-            .withDexRuntimesStartingFromIncluding(Version.V7_0_0)
+            .withDexRuntimesStartingFromIncluding(Version.V5_1_1)
             .withApiLevelsStartingAtIncluding(AndroidApiLevel.L)
             .build(),
         ImmutableList.of(JDK11_PATH),
@@ -132,14 +110,11 @@
   }
 
   private DesugaredLibraryTestCompileResult<?> compileR8ToDexWithD8() throws Exception {
-    Path[] pathBackport = getPathBackport();
+    assert parameters.getApiLevel().getLevel() >= AndroidApiLevel.O.getLevel()
+        || libraryDesugaringSpecification.hasNioFileDesugaring(parameters);
     return testForDesugaredLibrary(
             parameters, libraryDesugaringSpecification, compilationSpecification)
         .addProgramFiles(ToolHelper.R8_WITH_RELOCATED_DEPS_JAR)
-        .applyIf(
-            parameters.getApiLevel().getLevel() < AndroidApiLevel.O.getLevel()
-                && !libraryDesugaringSpecification.hasNioFileDesugaring(parameters),
-            b -> b.addProgramFiles(pathBackport))
         .addOptionsModification(
             options -> {
               options.testing.enableD8ResourcesPassThrough = true;
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/R8CompiledThroughDexTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/R8CompiledThroughDexTest.java
index af9ac89..44eb8fa 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/R8CompiledThroughDexTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/R8CompiledThroughDexTest.java
@@ -4,6 +4,8 @@
 
 package com.android.tools.r8.desugar.desugaredlibrary.r8ondex;
 
+import static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.D8_L8DEBUG;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11_PATH;
 import static junit.framework.TestCase.assertEquals;
 import static org.junit.Assert.assertTrue;
 
@@ -13,14 +15,16 @@
 import com.android.tools.r8.R8;
 import com.android.tools.r8.R8Command;
 import com.android.tools.r8.R8Command.Builder;
+import com.android.tools.r8.StringResource;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.TestRuntime;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.DexVm.Version;
 import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
+import com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification;
+import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.DeterminismChecker;
 import com.android.tools.r8.utils.Pair;
@@ -38,25 +42,34 @@
 import org.junit.runners.Parameterized;
 import org.junit.runners.Parameterized.Parameters;
 
-// TODO(b/142621961): Parametrize at least L and P instead of just P.
 @RunWith(Parameterized.class)
 public class R8CompiledThroughDexTest extends DesugaredLibraryTestBase {
 
-  private static final boolean minify = false;
-
   private final TestParameters parameters;
+  private final CompilationSpecification compilationSpecification;
+  private final LibraryDesugaringSpecification libraryDesugaringSpecification;
 
-  @Parameters(name = "{0}")
-  public static TestParametersCollection data() {
+  @Parameters(name = "{0}, spec: {1}, {2}")
+  public static List<Object[]> data() {
     // We only run this test with ART 8 and with full desugaring to avoid the large runtime on ART.
-    return getTestParameters()
-        .withDexRuntime(Version.V8_1_0)
-        .withApiLevel(AndroidApiLevel.B)
-        .build();
+    return buildParameters(
+        getTestParameters()
+            // Android 5 and 6 do not seem to work with more than 512 Mb of RAM.
+            .withDexRuntime(Version.V8_1_0)
+            // TODO(b/241748003): Broken strictly below 19 due to charset issue.
+            .withApiLevel(AndroidApiLevel.N)
+            .build(),
+        ImmutableList.of(JDK11_PATH),
+        ImmutableList.of(D8_L8DEBUG));
   }
 
-  public R8CompiledThroughDexTest(TestParameters parameters) {
+  public R8CompiledThroughDexTest(
+      TestParameters parameters,
+      LibraryDesugaringSpecification libraryDesugaringSpecification,
+      CompilationSpecification compilationSpecification) {
     this.parameters = parameters;
+    this.compilationSpecification = compilationSpecification;
+    this.libraryDesugaringSpecification = libraryDesugaringSpecification;
   }
 
   private static String commandLinePathFor(String string) {
@@ -84,13 +97,21 @@
     arguments.add("--min-api").add(Integer.toString(parameters.getApiLevel().getLevel()));
     buildup.add(b -> b.setMinApiLevel(parameters.getApiLevel().getLevel()));
 
-    arguments.add("--lib").add(commandLinePathFor(ToolHelper.JAVA_8_RUNTIME));
-    buildup.add(b -> b.addLibraryFiles(ToolHelper.getJava8RuntimeJar()));
+    arguments.add("--lib").add(commandLinePathFor(ToolHelper.getAndroidJar(AndroidApiLevel.R)));
+    buildup.add(b -> b.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.R)));
 
     arguments.add("--pg-conf").add(commandLinePathFor(R8_KEEP));
     buildup.add(b -> b.addProguardConfigurationFiles(Paths.get(R8_KEEP)));
 
-    if (!minify) {
+    arguments
+        .add("--desugared-lib")
+        .add(commandLinePathFor(libraryDesugaringSpecification.getSpecification()));
+    buildup.add(
+        b ->
+            b.addDesugaredLibraryConfiguration(
+                StringResource.fromFile(libraryDesugaringSpecification.getSpecification())));
+
+    if (!compilationSpecification.isProgramShrink()) {
       arguments.add("--no-minification");
       buildup.add(b -> b.setDisableMinification(true));
     }
@@ -167,7 +188,10 @@
       Path outputThroughDex = outputFolder.resolve("outThroughDex.zip");
       ProcessResult artProcessResult =
           ToolHelper.runArtRaw(
-              Collections.singletonList(commandLinePathFor(outputThroughCf)),
+              ImmutableList.of(
+                  commandLinePathFor(outputThroughCf),
+                  commandLinePathFor(
+                      getNonShrunkDesugaredLib(parameters, libraryDesugaringSpecification))),
               R8.class.getTypeName(),
               builder -> builder.appendArtOption("--64").appendArtOption("-Xmx512m"),
               parameters.getRuntime().asDex().getVm(),
@@ -183,9 +207,10 @@
         System.out.println(artProcessResult);
       }
       assertEquals(0, artProcessResult.exitCode);
-      assertTrue(
+      assertProgramsEqual(
           "The output of R8/JVM in-process and R8/ART external differ.",
-          TestBase.filesAreEqual(outputThroughCf, outputThroughDex));
+          outputThroughCf,
+          outputThroughDex);
     }
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/DesugaredLibraryTestBuilder.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/DesugaredLibraryTestBuilder.java
index 28d4db2..46d0b73 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/DesugaredLibraryTestBuilder.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/DesugaredLibraryTestBuilder.java
@@ -282,7 +282,7 @@
   }
 
   public DesugaredLibraryTestBuilder<T> noMinification() {
-    withR8TestBuilder(R8TestBuilder::noMinification);
+    withR8TestBuilder(R8TestBuilder::addDontObfuscate);
     return this;
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/FullNestOnProgramPathTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/FullNestOnProgramPathTest.java
index 832e72d..6fb309f 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/FullNestOnProgramPathTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/FullNestOnProgramPathTest.java
@@ -106,7 +106,7 @@
     for (String nestID : NEST_IDS) {
       testForR8(parameters.getBackend())
           .noTreeShaking()
-          .noMinification()
+          .addDontObfuscate()
           .addKeepAllAttributes()
           .setMinApi(parameters.getApiLevel())
           .addProgramFiles(classesOfNest(nestID))
@@ -149,7 +149,7 @@
     return testForR8(getStaticTemp(), backend)
         .apply(configuration)
         .noTreeShaking()
-        .noMinification()
+        .addDontObfuscate()
         .addKeepAllAttributes()
         .addOptionsModification(options -> options.enableNestReduction = false)
         .addProgramFiles(JAR)
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestCompilationExceptionTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestCompilationExceptionTest.java
index 2533da9..c7c4b21 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestCompilationExceptionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestCompilationExceptionTest.java
@@ -92,7 +92,7 @@
     } else {
       return testForR8(parameters.getBackend())
           .noTreeShaking()
-          .noMinification()
+          .addDontObfuscate()
           .addKeepAllAttributes()
           .setMinApi(parameters.getApiLevel())
           .addProgramFiles(matchingClasses)
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestConstructorRemovedArgTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestConstructorRemovedArgTest.java
index 04eac5e..adfc202 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestConstructorRemovedArgTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestConstructorRemovedArgTest.java
@@ -44,7 +44,7 @@
     String nestID = "constructors";
     testForR8(parameters.getBackend())
         .addKeepMainRule(getMainClass(nestID))
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .addOptionsModification(options -> options.enableClassInlining = false)
         .addProgramFiles(classesOfNest(nestID))
@@ -60,7 +60,7 @@
     testForR8(parameters.getBackend())
         .noTreeShaking()
         .addKeepMainRule(getMainClass(nestID))
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .addOptionsModification(options -> options.enableClassInlining = false)
         .addProgramFiles(classesOfNest(nestID))
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestMemberPropagatedTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestMemberPropagatedTest.java
index 8107d71..3e86e4f 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestMemberPropagatedTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestMemberPropagatedTest.java
@@ -41,7 +41,7 @@
     List<Path> toCompile = classesMatching("NestPvtFieldPropagated");
     testForR8(parameters.getBackend())
         .addKeepMainRule(getMainClass("memberPropagated"))
-        .noMinification()
+        .addDontObfuscate()
         .addOptionsModification(
             options -> {
               options.enableClassInlining = false;
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestMethodInlinedTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestMethodInlinedTest.java
index 8eb08f4..a235beb 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestMethodInlinedTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestMethodInlinedTest.java
@@ -55,7 +55,7 @@
     List<Path> toCompile = classesMatching("NestPvtMethodCallInlined");
     testForR8(parameters.getBackend())
         .addKeepMainRule(getMainClass("pvtCallInlined"))
-        .noMinification()
+        .addDontObfuscate()
         .addOptionsModification(
             options -> {
               options.enableClassInlining = false;
diff --git a/src/test/java/com/android/tools/r8/desugar/records/SimpleRecordTest.java b/src/test/java/com/android/tools/r8/desugar/records/SimpleRecordTest.java
index 9a74bb5..96af73c 100644
--- a/src/test/java/com/android/tools/r8/desugar/records/SimpleRecordTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/records/SimpleRecordTest.java
@@ -152,7 +152,7 @@
     R8FullTestBuilder builder =
         testForR8(parameters.getBackend())
             .addProgramClassFileData(PROGRAM_DATA)
-            .noMinification()
+            .addDontObfuscate()
             .setMinApi(parameters.getApiLevel())
             .addKeepMainRule(MAIN_TYPE);
     if (parameters.isCfRuntime()) {
diff --git a/src/test/java/com/android/tools/r8/desugar/twr/TwrCloseResourceDuplicationTest.java b/src/test/java/com/android/tools/r8/desugar/twr/TwrCloseResourceDuplicationTest.java
index dd43eb9..f774b57 100644
--- a/src/test/java/com/android/tools/r8/desugar/twr/TwrCloseResourceDuplicationTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/twr/TwrCloseResourceDuplicationTest.java
@@ -124,7 +124,7 @@
             parameters.getApiLevel().isLessThan(apiLevelWithTwrCloseResourceSupport()),
             builder -> builder.addDontWarn("java.lang.AutoCloseable"))
         .setMinApi(parameters.getApiLevel())
-        .noMinification()
+        .addDontObfuscate()
         .run(parameters.getRuntime(), MAIN.typeName(), getZipFile())
         .assertSuccessWithOutput(EXPECTED)
         .inspect(
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterInlineRegression.java b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterInlineRegression.java
index dfc4327..186346f 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterInlineRegression.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterInlineRegression.java
@@ -48,7 +48,7 @@
                 // Link against android.jar that contains ReflectiveOperationException.
                 .addLibraryFiles(parameters.getDefaultAndroidJarAbove(AndroidApiLevel.K))
                 .enableNoVerticalClassMergingAnnotations()
-                .noMinification();
+                .addDontObfuscate();
     ProcessResult processResult =
         testR8Splitter(
             parameters,
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMergeRegression.java b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMergeRegression.java
index c83e290..efd1f73 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMergeRegression.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMergeRegression.java
@@ -50,7 +50,7 @@
                 // Link against android.jar that contains ReflectiveOperationException.
                 .addLibraryFiles(parameters.getDefaultAndroidJarAbove(AndroidApiLevel.K))
                 .enableNoVerticalClassMergingAnnotations()
-                .noMinification();
+                .addDontObfuscate();
     ProcessResult processResult =
         testR8Splitter(
             parameters,
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/R8SplitterInlineToFeature.java b/src/test/java/com/android/tools/r8/dexsplitter/R8SplitterInlineToFeature.java
index 6576720..a7fef57 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/R8SplitterInlineToFeature.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/R8SplitterInlineToFeature.java
@@ -55,7 +55,7 @@
                 // Link against android.jar that contains ReflectiveOperationException.
                 .addLibraryFiles(parameters.getDefaultAndroidJarAbove(AndroidApiLevel.K))
                 .enableNoVerticalClassMergingAnnotations()
-                .noMinification();
+                .addDontObfuscate();
     ThrowableConsumer<R8TestCompileResult> ensureInlined =
         r8TestCompileResult -> {
           // Ensure that isEarly from BaseUtilClass is inlined into the feature
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/SyntheticDistributionTest.java b/src/test/java/com/android/tools/r8/dexsplitter/SyntheticDistributionTest.java
index 3dd0858..d0ea744 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/SyntheticDistributionTest.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/SyntheticDistributionTest.java
@@ -101,7 +101,7 @@
                 BaseSuperClass.class.getDeclaredMethod(
                     "keptApplyLambda", MyFunction.class, String.class)))
         .enableInliningAnnotations()
-        .noMinification()
+        .addDontObfuscate()
         // BaseDexClassLoader was introduced at api level 14.
         .apply(ApiModelingTestHelper::disableOutliningAndStubbing)
         .setMinApi(parameters.getApiLevel());
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingClassStaticizerTest.java b/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingClassStaticizerTest.java
index d510c33..12dfd1f 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingClassStaticizerTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingClassStaticizerTest.java
@@ -51,7 +51,7 @@
                     .assertNoOtherClassesMerged())
         .enableNeverClassInliningAnnotations()
         .enableInliningAnnotations()
-        .noMinification() // For assertions.
+        .addDontObfuscate() // For assertions.
         .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
         .setMinApi(parameters.getApiLevel())
         .compile()
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingTestBase.java b/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingTestBase.java
index c36733b..4690144 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingTestBase.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingTestBase.java
@@ -73,4 +73,8 @@
   protected static EnumKeepRules[] getAllEnumKeepRules() {
     return EnumKeepRules.values();
   }
+
+  protected static EnumKeepRules[] getStudioEnumKeepRules() {
+    return new EnumKeepRules[] {EnumKeepRules.STUDIO};
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingVerticalClassMergeTest.java b/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingVerticalClassMergeTest.java
index f240835..6f53904 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingVerticalClassMergeTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingVerticalClassMergeTest.java
@@ -43,7 +43,7 @@
         .addEnumUnboxingInspector(inspector -> inspector.assertUnboxed(UnboxableEnum.class))
         .enableNeverClassInliningAnnotations()
         .enableInliningAnnotations()
-        .noMinification() // For assertions.
+        .addDontObfuscate() // For assertions.
         .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
         .setMinApi(parameters.getApiLevel())
         .compile()
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/FailingMethodEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/FailingMethodEnumUnboxingTest.java
index 0a00994..19aa6fe 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/FailingMethodEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/FailingMethodEnumUnboxingTest.java
@@ -65,7 +65,7 @@
             .enableInliningAnnotations()
             .enableNeverClassInliningAnnotations()
             // TODO(b/173398086): uniqueMethodWithName() does not work with signature changes.
-            .noMinification()
+            .addDontObfuscate()
             .setMinApi(parameters.getApiLevel())
             .compile()
             .inspect(this::assertEnumsAsExpected);
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/FieldPutEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/FieldPutEnumUnboxingTest.java
index d7627a6..fce366e 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/FieldPutEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/FieldPutEnumUnboxingTest.java
@@ -53,7 +53,7 @@
             .enableInliningAnnotations()
             .enableNeverClassInliningAnnotations()
             .setMinApi(parameters.getApiLevel())
-            .noMinification()
+            .addDontObfuscate()
             .compile()
             .inspect(
                 i -> {
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/InstanceFieldsEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/InstanceFieldsEnumUnboxingTest.java
index e359c36..a493981 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/InstanceFieldsEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/InstanceFieldsEnumUnboxingTest.java
@@ -71,7 +71,7 @@
                             FailureBoxedInnerEnumField.EnumField.class,
                             FailureUnboxedEnumField.EnumField.class,
                             FailureTooManyUsedFields.EnumField.class))
-            .noMinification()
+            .addDontObfuscate()
             .enableInliningAnnotations()
             .enableNeverClassInliningAnnotations()
             .addKeepRules(enumKeepRules.getKeepRules())
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/InterfaceEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/InterfaceEnumUnboxingTest.java
index 3bc5c02..bcf2ec5 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/InterfaceEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/InterfaceEnumUnboxingTest.java
@@ -65,7 +65,7 @@
                         .assertUnboxedIf(
                             !parameters.canUseDefaultAndStaticInterfaceMethods(),
                             FailureDefaultMethodUsed.EnumInterface.class))
-            .noMinification()
+            .addDontObfuscate()
             .enableNoVerticalClassMergingAnnotations()
             .enableInliningAnnotations()
             .enableNeverClassInliningAnnotations()
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/LambdaEnumUnboxingEmptyEnumObjectTest.java b/src/test/java/com/android/tools/r8/enumunboxing/LambdaEnumUnboxingEmptyEnumObjectTest.java
index 284f8ac..5f24fbe 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/LambdaEnumUnboxingEmptyEnumObjectTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/LambdaEnumUnboxingEmptyEnumObjectTest.java
@@ -40,7 +40,7 @@
         .addKeepMainRule(Main.class)
         .addKeepRules(enumKeepRules.getKeepRules())
         .enableNeverClassInliningAnnotations()
-        .noMinification()
+        .addDontObfuscate()
         .enableNoVerticalClassMergingAnnotations()
         .enableInliningAnnotations()
         .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/LambdaEnumUnboxingEmptyEnumTest.java b/src/test/java/com/android/tools/r8/enumunboxing/LambdaEnumUnboxingEmptyEnumTest.java
index 288ace4..14eba8a 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/LambdaEnumUnboxingEmptyEnumTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/LambdaEnumUnboxingEmptyEnumTest.java
@@ -40,7 +40,7 @@
         .addKeepMainRule(Main.class)
         .addKeepRules(enumKeepRules.getKeepRules())
         .enableNeverClassInliningAnnotations()
-        .noMinification()
+        .addDontObfuscate()
         .enableNoVerticalClassMergingAnnotations()
         .enableInliningAnnotations()
         .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/SwitchEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/SwitchEnumUnboxingTest.java
index 43dec5d..670e3b3 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/SwitchEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/SwitchEnumUnboxingTest.java
@@ -49,7 +49,7 @@
         .addEnumUnboxingInspector(inspector -> inspector.assertUnboxed(ENUM_CLASS))
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
-        .noMinification() // For assertions.
+        .addDontObfuscate() // For assertions.
         .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
         .setMinApi(parameters.getApiLevel())
         .compile()
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/ValueOfWithoutCastEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/ValueOfWithoutCastEnumUnboxingTest.java
new file mode 100644
index 0000000..e213b3b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/enumunboxing/ValueOfWithoutCastEnumUnboxingTest.java
@@ -0,0 +1,92 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.enumunboxing;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.BooleanUtils;
+import java.util.List;
+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 ValueOfWithoutCastEnumUnboxingTest extends EnumUnboxingTestBase {
+
+  private final TestParameters parameters;
+  private final boolean enumValueOptimization;
+  private final EnumKeepRules enumKeepRules;
+
+  @Parameters(name = "{0} valueOpt: {1} keep: {2}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withAllRuntimesAndApiLevels().build(),
+        BooleanUtils.values(),
+        getStudioEnumKeepRules());
+  }
+
+  public ValueOfWithoutCastEnumUnboxingTest(
+      TestParameters parameters, boolean enumValueOptimization, EnumKeepRules enumKeepRules) {
+    this.parameters = parameters;
+    this.enumValueOptimization = enumValueOptimization;
+    this.enumKeepRules = enumKeepRules;
+  }
+
+  @Test
+  public void testEnumUnboxing() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepClassAndMembersRules(Main.class)
+        .addKeepRules(enumKeepRules.getKeepRules())
+        .addEnumUnboxingInspector(inspector -> inspector.assertNotUnboxed(MyEnum.class))
+        .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
+        .enableInliningAnnotations()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("A");
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      MyEnum e = System.currentTimeMillis() > 0 ? MyEnum.A : MyEnum.B;
+      // When the library method optimizer runs, the class argument to Enum.valueOf is still not a
+      // const-class. Therefore, the library method optimizer cannot insert an assume-dynamic-type
+      // instruction for the out-value of the call to Enum.valueOf. The argument is optimized into a
+      // const-class before the enum unboxing analysis runs. The enum unboxer must conclude that the
+      // enum is not subject to unboxing.
+      Object o = Enum.valueOf(new ClassInlineCandidate().set(MyEnum.class).get(), e.name());
+      escape(o);
+    }
+
+    // @Keep
+    static void escape(Object o) {
+      System.out.println(o);
+    }
+  }
+
+  static class ClassInlineCandidate {
+
+    Class<MyEnum> clazz;
+
+    @NeverInline
+    ClassInlineCandidate set(Class<MyEnum> clazz) {
+      this.clazz = clazz;
+      return this;
+    }
+
+    @NeverInline
+    Class<MyEnum> get() {
+      return clazz;
+    }
+  }
+
+  enum MyEnum {
+    A,
+    B
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/VirtualMethodsAccessibilityErrorEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/VirtualMethodsAccessibilityErrorEnumUnboxingTest.java
index 03e6de9..8cb9018 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/VirtualMethodsAccessibilityErrorEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/VirtualMethodsAccessibilityErrorEnumUnboxingTest.java
@@ -59,7 +59,7 @@
                 })
             .transform();
     testForR8(parameters.getBackend())
-        .noMinification()
+        .addDontObfuscate()
         .addProgramClasses(MyEnum.class)
         .addProgramClassFileData(testClassData)
         .addKeepMainRule(TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/graph/MissingClassThrowingTest.java b/src/test/java/com/android/tools/r8/graph/MissingClassThrowingTest.java
index 14cf693..333ab92 100644
--- a/src/test/java/com/android/tools/r8/graph/MissingClassThrowingTest.java
+++ b/src/test/java/com/android/tools/r8/graph/MissingClassThrowingTest.java
@@ -68,7 +68,7 @@
                 .addProgramClasses(Program.class)
                 .addKeepAllClassesRule()
                 .addKeepAllAttributes()
-                .noMinification()
+                .addDontObfuscate()
                 .noTreeShaking()
                 .enableInliningAnnotations()
                 .enableNoHorizontalClassMergingAnnotations()
diff --git a/src/test/java/com/android/tools/r8/graph/invokestatic/InvokeStaticOnInterfaceTest.java b/src/test/java/com/android/tools/r8/graph/invokestatic/InvokeStaticOnInterfaceTest.java
index 61d3d32..aaadb51 100644
--- a/src/test/java/com/android/tools/r8/graph/invokestatic/InvokeStaticOnInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/graph/invokestatic/InvokeStaticOnInterfaceTest.java
@@ -81,7 +81,7 @@
             .enableInliningAnnotations()
             .enableNoVerticalClassMergingAnnotations()
             .addOptionsModification(o -> o.testing.allowInvokeErrors = true)
-            .noMinification()
+            .addDontObfuscate()
             .addKeepMainRule(Main.class)
             .run(parameters.getRuntime(), Main.class);
     if (parameters.getRuntime().asCf().isNewerThan(CfVm.JDK8)) {
diff --git a/src/test/java/com/android/tools/r8/internal/Regression127524985.java b/src/test/java/com/android/tools/r8/internal/Regression127524985.java
index 009c587..9b88d56 100644
--- a/src/test/java/com/android/tools/r8/internal/Regression127524985.java
+++ b/src/test/java/com/android/tools/r8/internal/Regression127524985.java
@@ -68,7 +68,7 @@
     testForR8(parameters.getBackend())
         .debug()
         .noTreeShaking()
-        .noMinification()
+        .addDontObfuscate()
         .addKeepAllAttributes()
         .addKeepRules("-dontwarn")
         .allowDiagnosticWarningMessages()
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeCompilationTestBase.java b/src/test/java/com/android/tools/r8/internal/YouTubeCompilationTestBase.java
index f280e80..8a15c91 100644
--- a/src/test/java/com/android/tools/r8/internal/YouTubeCompilationTestBase.java
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeCompilationTestBase.java
@@ -29,6 +29,7 @@
   static final String DEPLOY_JAR = "YouTubeRelease_deploy.jar";
   static final String PG_MAP = "YouTubeRelease_proguard.map";
   static final String PG_CONF = "YouTubeRelease_proguard.config";
+  static final String PG_CONF_EXTRA = "YouTubeRelease_proguard_extra.config";
   static final String PG_PROTO_CONF = "YouTubeRelease_proto_safety.pgconf";
   static final String PG_MISSING_CLASSES_CONF = "YouTubeRelease_proguard_missing_classes.config";
 
@@ -61,17 +62,24 @@
     return path;
   }
 
+  protected Path getD8DesugaredLibraryJDKLibs() {
+    Path path = Paths.get(base, "desugar_jdk_libs/d8_desugared_jdk_libs.jar");
+    assertTrue(path.toFile().exists());
+    return path;
+  }
+
   protected Path getDesugaredLibraryJDKLibsConfiguration() {
     Path path = Paths.get(base, "desugar_jdk_libs/desugar_jdk_libs_configuration.jar");
     assertTrue(path.toFile().exists());
     return path;
   }
 
-  protected List<Path> getDesugaredLibraryKeepRuleFiles() {
+  protected List<Path> getDesugaredLibraryKeepRuleFiles(Path generatedKeepRules) {
     ImmutableList<Path> keepRuleFiles =
         ImmutableList.of(
             Paths.get(base, "desugar_jdk_libs/base.pgcfg"),
-            Paths.get(base, "desugar_jdk_libs/minify_desugar_jdk_libs.pgcfg"));
+            Paths.get(base, "desugar_jdk_libs/minify_desugar_jdk_libs.pgcfg"),
+            generatedKeepRules);
     assertTrue(keepRuleFiles.stream().allMatch(keepRuleFile -> keepRuleFile.toFile().exists()));
     return keepRuleFiles;
   }
@@ -80,7 +88,7 @@
     ImmutableList.Builder<Path> builder = ImmutableList.builder();
     builder.add(Paths.get(base).resolve(PG_CONF));
     builder.add(Paths.get(ToolHelper.PROGUARD_SETTINGS_FOR_INTERNAL_APPS).resolve(PG_CONF));
-    for (String name : new String[] {PG_PROTO_CONF, PG_MISSING_CLASSES_CONF}) {
+    for (String name : new String[] {PG_CONF_EXTRA, PG_PROTO_CONF, PG_MISSING_CLASSES_CONF}) {
       Path config = Paths.get(base).resolve(name);
       if (config.toFile().exists()) {
         builder.add(config);
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeV1620Test.java b/src/test/java/com/android/tools/r8/internal/YouTubeV1620Test.java
deleted file mode 100644
index 56aed18..0000000
--- a/src/test/java/com/android/tools/r8/internal/YouTubeV1620Test.java
+++ /dev/null
@@ -1,190 +0,0 @@
-// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.internal;
-
-import static com.android.tools.r8.ToolHelper.isLocalDevelopment;
-import static com.android.tools.r8.ToolHelper.shouldRunSlowTests;
-import static com.android.tools.r8.internal.proto.ProtoShrinkingTestBase.assertRewrittenProtoSchemasMatch;
-import static com.android.tools.r8.internal.proto.ProtoShrinkingTestBase.keepAllProtosRule;
-import static com.android.tools.r8.internal.proto.ProtoShrinkingTestBase.keepDynamicMethodSignatureRule;
-import static com.android.tools.r8.internal.proto.ProtoShrinkingTestBase.keepNewMessageInfoSignatureRule;
-import static org.hamcrest.CoreMatchers.anyOf;
-import static org.hamcrest.CoreMatchers.containsString;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assume.assumeTrue;
-
-import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.L8TestCompileResult;
-import com.android.tools.r8.LibraryDesugaringTestConfiguration;
-import com.android.tools.r8.LibraryDesugaringTestConfiguration.PresentKeepRuleConsumer;
-import com.android.tools.r8.R8FullTestBuilder;
-import com.android.tools.r8.R8TestCompileResult;
-import com.android.tools.r8.ResourceException;
-import com.android.tools.r8.StringResource;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.ThrowableConsumer;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.Reporter;
-import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.concurrent.ExecutionException;
-import org.junit.After;
-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 YouTubeV1620Test extends YouTubeCompilationTestBase {
-
-  private static final int MAX_APPLICATION_SIZE = 29750000;
-  private static final int MAX_DESUGARED_LIBRARY_SIZE = 425000;
-
-  private final TestParameters parameters;
-
-  private final Path dumpDirectory = Paths.get("YouTubeV1620-" + System.currentTimeMillis());
-  private final Reporter reporter = new Reporter();
-
-  @Parameters(name = "{0}")
-  public static TestParametersCollection data() {
-    return getTestParameters().withDefaultDexRuntime().withApiLevel(AndroidApiLevel.L).build();
-  }
-
-  public YouTubeV1620Test(TestParameters parameters) {
-    super(16, 20, parameters.getApiLevel());
-    this.parameters = parameters;
-  }
-
-  @Test
-  public void test() throws Exception {
-    assumeTrue(isLocalDevelopment());
-    assumeTrue(shouldRunSlowTests());
-
-    KeepRuleConsumer keepRuleConsumer = new PresentKeepRuleConsumer();
-    R8TestCompileResult r8CompileResult = compileApplicationWithR8(keepRuleConsumer);
-    L8TestCompileResult l8CompileResult = compileDesugaredLibraryWithL8(keepRuleConsumer);
-
-    inspect(r8CompileResult, l8CompileResult);
-
-    if (isLocalDevelopment()) {
-      dump(r8CompileResult, l8CompileResult);
-    }
-  }
-
-  @Test
-  public void testProtoRewriting() throws Exception {
-    assumeTrue(shouldRunSlowTests());
-
-    KeepRuleConsumer keepRuleConsumer = KeepRuleConsumer.emptyConsumer();
-    R8TestCompileResult r8CompileResult =
-        compileApplicationWithR8(
-            keepRuleConsumer,
-            builder ->
-                builder
-                    .addKeepRules(
-                        keepAllProtosRule(),
-                        keepDynamicMethodSignatureRule(),
-                        keepNewMessageInfoSignatureRule())
-                    .allowCheckDiscardedErrors(true));
-    assertRewrittenProtoSchemasMatch(
-        new CodeInspector(getProgramFiles()), r8CompileResult.inspector());
-  }
-
-  @After
-  public void teardown() {
-    reporter.failIfPendingErrors();
-  }
-
-  private R8TestCompileResult compileApplicationWithR8(KeepRuleConsumer keepRuleConsumer)
-      throws IOException, CompilationFailedException {
-    return compileApplicationWithR8(keepRuleConsumer, ThrowableConsumer.empty());
-  }
-
-  private R8TestCompileResult compileApplicationWithR8(
-      KeepRuleConsumer keepRuleConsumer, ThrowableConsumer<R8FullTestBuilder> configuration)
-      throws IOException, CompilationFailedException {
-    return testForR8(parameters.getBackend())
-        .addProgramFiles(getProgramFiles())
-        .addLibraryFiles(getLibraryFileWithoutDesugaredLibrary())
-        .addKeepRuleFiles(getKeepRuleFiles())
-        .addDontWarn("android.app.Activity$TranslucentConversionListener")
-        .allowDiagnosticMessages()
-        .allowUnusedDontWarnPatterns()
-        .allowUnusedProguardConfigurationRules()
-        .apply(configuration)
-        .setMinApi(getApiLevel())
-        .enableCoreLibraryDesugaring(
-            LibraryDesugaringTestConfiguration.builder()
-                .setKeepRuleConsumer(keepRuleConsumer)
-                .addDesugaredLibraryConfiguration(
-                    StringResource.fromFile(getDesugaredLibraryConfiguration()))
-                .build())
-        .compile()
-        .assertAllInfoMessagesMatch(
-            anyOf(
-                containsString("Ignoring option: -optimizations"),
-                containsString("Proguard configuration rule does not match anything"),
-                containsString("Invalid signature")))
-        .apply(this::printProtoStats);
-  }
-
-  private L8TestCompileResult compileDesugaredLibraryWithL8(KeepRuleConsumer keepRuleConsumer)
-      throws CompilationFailedException, IOException, ExecutionException {
-    return testForL8(getApiLevel())
-        .setDesugaredLibrarySpecification(getDesugaredLibraryConfiguration())
-        .addProgramFiles(getDesugaredLibraryJDKLibs())
-        .addGeneratedKeepRules(keepRuleConsumer.get())
-        .addKeepRuleFiles(getDesugaredLibraryKeepRuleFiles())
-        .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
-        .compile();
-  }
-
-  private void inspect(R8TestCompileResult r8CompileResult, L8TestCompileResult l8CompileResult)
-      throws IOException, ResourceException {
-    r8CompileResult.runDex2Oat(parameters.getRuntime()).assertNoVerificationErrors();
-    l8CompileResult.runDex2Oat(parameters.getRuntime()).assertNoVerificationErrors();
-
-    int applicationSize = r8CompileResult.getApp().applicationSize();
-    if (applicationSize > MAX_APPLICATION_SIZE) {
-      reporter.error(
-          "Expected application size to be <="
-              + MAX_APPLICATION_SIZE
-              + ", but was "
-              + applicationSize);
-    }
-
-    int desugaredLibrarySize = l8CompileResult.getApp().applicationSize();
-    if (desugaredLibrarySize > MAX_DESUGARED_LIBRARY_SIZE) {
-      reporter.error(
-          "Expected desugared library size to be <="
-              + MAX_DESUGARED_LIBRARY_SIZE
-              + ", but was "
-              + desugaredLibrarySize);
-    }
-
-    if (isLocalDevelopment()) {
-      System.out.println("Dex size (application, excluding desugared library): " + applicationSize);
-      System.out.println("Dex size (desugared library): " + desugaredLibrarySize);
-      System.out.println("Dex size (total): " + (applicationSize + desugaredLibrarySize));
-    }
-  }
-
-  private void dump(R8TestCompileResult r8CompileResult, L8TestCompileResult l8CompileResult)
-      throws IOException {
-    assertTrue(isLocalDevelopment());
-    Files.createDirectories(dumpDirectory);
-    r8CompileResult
-        .writeToDirectory(dumpDirectory)
-        .writeProguardMap(dumpDirectory.resolve("mapping.txt"));
-    l8CompileResult
-        .writeSingleDexOutputToFile(dumpDirectory.resolve("classes5.dex"))
-        .writeGeneratedKeepRules(dumpDirectory.resolve("l8-keep.txt"))
-        .writeProguardMap(dumpDirectory.resolve("l8-mapping.txt"));
-  }
-}
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeV1719Test.java b/src/test/java/com/android/tools/r8/internal/YouTubeV1719Test.java
new file mode 100644
index 0000000..e293b6e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeV1719Test.java
@@ -0,0 +1,331 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.internal;
+
+import static com.android.tools.r8.ToolHelper.isLocalDevelopment;
+import static com.android.tools.r8.ToolHelper.shouldRunSlowTests;
+import static com.android.tools.r8.internal.proto.ProtoShrinkingTestBase.assertRewrittenProtoSchemasMatch;
+import static com.android.tools.r8.internal.proto.ProtoShrinkingTestBase.keepAllProtosRule;
+import static com.android.tools.r8.internal.proto.ProtoShrinkingTestBase.keepDynamicMethodSignatureRule;
+import static com.android.tools.r8.internal.proto.ProtoShrinkingTestBase.keepNewMessageInfoSignatureRule;
+import static org.hamcrest.CoreMatchers.anyOf;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.D8TestCompileResult;
+import com.android.tools.r8.L8TestCompileResult;
+import com.android.tools.r8.LibraryDesugaringTestConfiguration;
+import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.ResourceException;
+import com.android.tools.r8.StartupProfileProvider;
+import com.android.tools.r8.StringConsumer.FileConsumer;
+import com.android.tools.r8.StringResource;
+import com.android.tools.r8.TestCompileResult;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ThrowableConsumer;
+import com.android.tools.r8.tracereferences.TraceReferences;
+import com.android.tools.r8.tracereferences.TraceReferencesCommand;
+import com.android.tools.r8.tracereferences.TraceReferencesKeepRules;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.concurrent.ExecutionException;
+import org.junit.After;
+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 YouTubeV1719Test extends YouTubeCompilationTestBase {
+
+  private static final int MAX_APPLICATION_SIZE = 29750000;
+  private static final int MAX_DESUGARED_LIBRARY_SIZE = 425000;
+
+  private final TestParameters parameters;
+
+  // Location where test artifacts will be dumped when `local_development` is set.
+  private final Path dumpDirectory = Paths.get("YouTubeV1719-" + System.currentTimeMillis());
+
+  // By setting this to an actual startup list, YouTube will be build with layout optimizations
+  // enabled.
+  private final StartupProfileProvider startupProfileProvider = null;
+  private final boolean enableMinimalStartupDex = true;
+  private final boolean enableStartupBoundaryOptimizations = false;
+
+  private final Reporter reporter = new Reporter();
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withDefaultDexRuntime().withApiLevel(AndroidApiLevel.M).build();
+  }
+
+  public YouTubeV1719Test(TestParameters parameters) {
+    super(17, 19, parameters.getApiLevel());
+    this.parameters = parameters;
+  }
+
+  /**
+   * Running this test will dump an instrumented version of YouTube in the {@link #dumpDirectory}.
+   */
+  @Test
+  public void testStartupInstrumentation() throws Exception {
+    assumeTrue(isLocalDevelopment());
+    assumeTrue(shouldRunSlowTests());
+
+    // Compile the app with instrumentation and core library desugaring enabled.
+    D8TestCompileResult d8CompileResult =
+        testForD8()
+            .addProgramFiles(getProgramFiles())
+            .addLibraryFiles(getLibraryFileWithoutDesugaredLibrary())
+            .addOptionsModification(
+                options ->
+                    options
+                        .getStartupInstrumentationOptions()
+                        .setEnableStartupInstrumentation()
+                        .setStartupInstrumentationTag("r8"))
+            .enableCoreLibraryDesugaring(
+                LibraryDesugaringTestConfiguration.builder()
+                    .addDesugaredLibraryConfiguration(
+                        StringResource.fromFile(getDesugaredLibraryConfiguration()))
+                    .build())
+            .release()
+            .setMinApi(parameters.getApiLevel())
+            .compile();
+
+    // Compile desugared library using cf backend (without keep rules).
+    L8TestCompileResult l8CompileResult = compileDesugaredLibraryWithL8();
+
+    // Generate keep rules for desugared library using trace references.
+    Path generatedKeepRules = temp.newFile().toPath();
+    TraceReferences.run(
+        TraceReferencesCommand.builder()
+            .addLibraryFiles(getLibraryFileWithoutDesugaredLibrary())
+            .addSourceFiles(d8CompileResult.writeToZip())
+            .addTargetFiles(l8CompileResult.writeToZip())
+            .setConsumer(
+                TraceReferencesKeepRules.builder()
+                    .setAllowObfuscation(false)
+                    .setOutputConsumer(new FileConsumer(generatedKeepRules))
+                    .build())
+            .build());
+
+    // Compile desugared library using the generated keep rules.
+    R8TestCompileResult l8R8CompileResult =
+        compileDesugaredLibraryWithR8(l8CompileResult, generatedKeepRules);
+
+    if (isLocalDevelopment()) {
+      dump(d8CompileResult, l8R8CompileResult);
+    }
+  }
+
+  /**
+   * Running this test will dump an R8 build of YouTube in the {@link #dumpDirectory}, where the
+   * desugared library keep rules are generated using trace references.
+   *
+   * <p>If {@link #startupProfileProvider} is set to a concrete startup list, YouTube will be build
+   * with layout optimizations enabled.
+   */
+  @Test
+  public void testR8() throws Exception {
+    assumeTrue(isLocalDevelopment());
+    assumeTrue(shouldRunSlowTests());
+
+    // Compile app using R8, passing the startup list.
+    R8TestCompileResult r8CompileResult =
+        compileApplicationWithR8(
+            testBuilder ->
+                testBuilder.addOptionsModification(
+                    options -> {
+                      if (startupProfileProvider != null) {
+                        options
+                            .getStartupOptions()
+                            .setStartupProfileProvider(startupProfileProvider)
+                            .setEnableMinimalStartupDex(enableMinimalStartupDex)
+                            .setEnableStartupBoundaryOptimizations(
+                                enableStartupBoundaryOptimizations);
+                      }
+                    }));
+
+    // Compile desugared library using cf backend (without keep rules).
+    L8TestCompileResult l8CompileResult = compileDesugaredLibraryWithL8();
+
+    // Generate keep rules for desugared library using trace references.
+    Path generatedKeepRules = temp.newFile().toPath();
+    TraceReferences.run(
+        TraceReferencesCommand.builder()
+            .addLibraryFiles(getLibraryFileWithoutDesugaredLibrary())
+            .addSourceFiles(r8CompileResult.writeToZip())
+            .addTargetFiles(l8CompileResult.writeToZip())
+            .setConsumer(
+                TraceReferencesKeepRules.builder()
+                    .setAllowObfuscation(false)
+                    .setOutputConsumer(new FileConsumer(generatedKeepRules))
+                    .build())
+            .build());
+
+    // Compile desugared library using the generated keep rules.
+    R8TestCompileResult l8R8CompileResult =
+        compileDesugaredLibraryWithR8(l8CompileResult, generatedKeepRules);
+
+    if (isLocalDevelopment()) {
+      dump(r8CompileResult, l8R8CompileResult);
+    }
+
+    inspect(r8CompileResult, l8CompileResult);
+  }
+
+  /**
+   * Validates that when all protos are kept and the proto optmization is enabled, the generated
+   * proto schemas are identical to the proto schemas in the input.
+   */
+  @Test
+  public void testProtoRewriting() throws Exception {
+    assumeTrue(shouldRunSlowTests());
+
+    R8TestCompileResult r8CompileResult =
+        compileApplicationWithR8(
+            builder ->
+                builder
+                    .addKeepRules(
+                        keepAllProtosRule(),
+                        keepDynamicMethodSignatureRule(),
+                        keepNewMessageInfoSignatureRule())
+                    .allowCheckDiscardedErrors(true));
+    assertRewrittenProtoSchemasMatch(
+        new CodeInspector(getProgramFiles()), r8CompileResult.inspector());
+  }
+
+  @After
+  public void teardown() {
+    reporter.failIfPendingErrors();
+  }
+
+  private R8TestCompileResult compileApplicationWithR8(
+      ThrowableConsumer<R8FullTestBuilder> configuration)
+      throws IOException, CompilationFailedException {
+    return testForR8(parameters.getBackend())
+        .addProgramFiles(getProgramFiles())
+        .addLibraryFiles(getLibraryFileWithoutDesugaredLibrary())
+        .addKeepRuleFiles(getKeepRuleFiles())
+        .addDontWarn("android.app.Activity$TranslucentConversionListener")
+        .apply(configuration)
+        .apply(this::disableR8StrictMode)
+        .apply(this::disableR8TestingDefaults)
+        .setMinApi(getApiLevel())
+        .enableCoreLibraryDesugaring(
+            LibraryDesugaringTestConfiguration.builder()
+                .addDesugaredLibraryConfiguration(
+                    StringResource.fromFile(getDesugaredLibraryConfiguration()))
+                .build())
+        .compile()
+        .assertAllInfoMessagesMatch(
+            anyOf(
+                containsString("Ignoring option: -optimizations"),
+                containsString("Proguard configuration rule does not match anything"),
+                containsString("Invalid signature")))
+        .apply(this::printProtoStats);
+  }
+
+  private L8TestCompileResult compileDesugaredLibraryWithL8()
+      throws CompilationFailedException, IOException, ExecutionException {
+    return testForL8(getApiLevel(), Backend.CF)
+        .setDesugaredLibrarySpecification(getDesugaredLibraryConfiguration())
+        .addProgramFiles(getDesugaredLibraryJDKLibs())
+        .addLibraryFiles(getLibraryFileWithoutDesugaredLibrary())
+        .compile();
+  }
+
+  private R8TestCompileResult compileDesugaredLibraryWithR8(
+      L8TestCompileResult l8CompileResult, Path generatedKeepRules)
+      throws IOException, CompilationFailedException {
+    return testForR8(parameters.getBackend())
+        .addProgramFiles(l8CompileResult.writeToZip())
+        .addLibraryFiles(getLibraryFileWithoutDesugaredLibrary())
+        .addKeepRuleFiles(getDesugaredLibraryKeepRuleFiles(generatedKeepRules))
+        .apply(this::disableR8StrictMode)
+        .apply(this::disableR8TestingDefaults)
+        .setMinApi(parameters.getApiLevel())
+        .compile();
+  }
+
+  private void inspect(R8TestCompileResult r8CompileResult, L8TestCompileResult l8CompileResult)
+      throws IOException, ResourceException {
+    r8CompileResult.runDex2Oat(parameters.getRuntime()).assertNoVerificationErrors();
+    l8CompileResult.runDex2Oat(parameters.getRuntime()).assertNoVerificationErrors();
+
+    int applicationSize = r8CompileResult.getApp().applicationSize();
+    if (applicationSize > MAX_APPLICATION_SIZE) {
+      reporter.error(
+          "Expected application size to be <="
+              + MAX_APPLICATION_SIZE
+              + ", but was "
+              + applicationSize);
+    }
+
+    int desugaredLibrarySize = l8CompileResult.getApp().applicationSize();
+    if (desugaredLibrarySize > MAX_DESUGARED_LIBRARY_SIZE) {
+      reporter.error(
+          "Expected desugared library size to be <="
+              + MAX_DESUGARED_LIBRARY_SIZE
+              + ", but was "
+              + desugaredLibrarySize);
+    }
+
+    if (isLocalDevelopment()) {
+      System.out.println("Dex size (application, excluding desugared library): " + applicationSize);
+      System.out.println("Dex size (desugared library): " + desugaredLibrarySize);
+      System.out.println("Dex size (total): " + (applicationSize + desugaredLibrarySize));
+    }
+  }
+
+  private void dump(TestCompileResult<?, ?> compileResult, R8TestCompileResult l8R8CompileResult)
+      throws IOException {
+    assertTrue(isLocalDevelopment());
+    Files.createDirectories(dumpDirectory);
+
+    // Dump dex.
+    compileResult.writeToDirectory(dumpDirectory);
+
+    // Dump mapping.
+    if (compileResult instanceof R8TestCompileResult) {
+      R8TestCompileResult r8TestCompileResult = (R8TestCompileResult) compileResult;
+      r8TestCompileResult.writeProguardMap(dumpDirectory.resolve("mapping.txt"));
+    }
+
+    // Dump desugared library dex.
+    int i = 2;
+    while (true) {
+      Path desugaredLibraryDexFile = dumpDirectory.resolve("classes" + i + ".dex");
+      if (!desugaredLibraryDexFile.toFile().exists()) {
+        l8R8CompileResult.writeSingleDexOutputToFile(desugaredLibraryDexFile);
+        break;
+      }
+      i++;
+    }
+
+    // Dump desugared library mapping.
+    l8R8CompileResult.writeProguardMap(dumpDirectory.resolve("mapping-desugared-library.txt"));
+  }
+
+  private void disableR8StrictMode(R8FullTestBuilder testBuilder) {
+    testBuilder
+        .allowDiagnosticMessages()
+        .allowUnusedDontWarnPatterns()
+        .allowUnusedProguardConfigurationRules();
+  }
+
+  private void disableR8TestingDefaults(R8FullTestBuilder testBuilder) {
+    testBuilder.addOptionsModification(
+        options -> options.horizontalClassMergerOptions().setEnableInterfaceMerging(false));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/startup/ChromeStartupTest.java b/src/test/java/com/android/tools/r8/internal/startup/ChromeStartupTest.java
new file mode 100644
index 0000000..89b37d0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/startup/ChromeStartupTest.java
@@ -0,0 +1,267 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.internal.startup;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.ArchiveProgramResourceProvider;
+import com.android.tools.r8.DexIndexedConsumer;
+import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.StartupProfileProvider;
+import com.android.tools.r8.StringResource;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ThrowableConsumer;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.ZipUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+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 ChromeStartupTest extends TestBase {
+
+  private static AndroidApiLevel apiLevel = AndroidApiLevel.N;
+
+  // Location of dump.zip and startup.txt.
+  private static Path chromeDirectory = Paths.get("build/chrome/startup");
+
+  // Location of test artifacts.
+  private static Path artifactDirectory = Paths.get("build/chrome/startup");
+
+  // Temporary directory where dump.zip is extracted into.
+  private static Path dumpDirectory;
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  @BeforeClass
+  public static void setup() throws IOException {
+    assumeTrue(ToolHelper.isLocalDevelopment());
+    dumpDirectory = getStaticTemp().newFolder().toPath();
+    ZipUtils.unzip(chromeDirectory.resolve("dump.zip"), dumpDirectory);
+  }
+
+  // Outputs the instrumented dex in chrome/instrumented.
+  @Test
+  public void buildInstrumentedDex() throws Exception {
+    buildInstrumentedBase();
+    buildInstrumentedChromeSplit();
+  }
+
+  private void buildInstrumentedBase() throws Exception {
+    Files.createDirectories(artifactDirectory.resolve("instrumented/base/dex"));
+    testForD8()
+        .addProgramFiles(dumpDirectory.resolve("program.jar"))
+        .addClasspathFiles(dumpDirectory.resolve("classpath.jar"))
+        .addLibraryFiles(dumpDirectory.resolve("library.jar"))
+        .addOptionsModification(
+            options ->
+                options
+                    .getStartupInstrumentationOptions()
+                    .setEnableStartupInstrumentation()
+                    .setStartupInstrumentationTag("r8"))
+        .setMinApi(apiLevel)
+        .release()
+        .compile()
+        .writeToDirectory(artifactDirectory.resolve("instrumented/base/dex"));
+  }
+
+  private void buildInstrumentedChromeSplit() throws Exception {
+    Files.createDirectories(artifactDirectory.resolve("instrumented/chrome/dex"));
+    testForD8()
+        // The Chrome split is the feature that contains ChromeApplicationImpl.
+        .addProgramFiles(getChromeSplit())
+        .addClasspathFiles(dumpDirectory.resolve("program.jar"))
+        .addClasspathFiles(dumpDirectory.resolve("classpath.jar"))
+        .addLibraryFiles(dumpDirectory.resolve("library.jar"))
+        .addOptionsModification(
+            options ->
+                options
+                    .getStartupInstrumentationOptions()
+                    .setEnableStartupInstrumentation()
+                    .setStartupInstrumentationTag("r8"))
+        .setMinApi(apiLevel)
+        .release()
+        .compile()
+        .inspect(
+            inspector ->
+                assertThat(
+                    inspector.clazz("org.chromium.chrome.browser.ChromeApplicationImpl"),
+                    isPresent()))
+        .writeToDirectory(artifactDirectory.resolve("instrumented/chrome/dex"));
+  }
+
+  private Path getChromeSplit() {
+    return getFeatureSplit(9);
+  }
+
+  private Path getFeatureSplit(int index) {
+    return dumpDirectory.resolve("feature-" + index + ".jar");
+  }
+
+  // Outputs Chrome built using R8 in chrome/default.
+  @Test
+  public void buildR8Default() throws Exception {
+    buildR8(ThrowableConsumer.empty(), artifactDirectory.resolve("default"));
+  }
+
+  // Outputs Chrome built using R8 with limited class merging in chrome/default-with-patches.
+  @Test
+  public void buildR8DefaultWithPatches() throws Exception {
+    buildR8(
+        testBuilder ->
+            testBuilder.addOptionsModification(
+                options -> options.horizontalClassMergerOptions().setEnableSameFilePolicy(true)),
+        artifactDirectory.resolve("default-with-patches"));
+  }
+
+  // Outputs Chrome built using R8 with minimal startup dex and no boundary optimizations in
+  // chrome/optimized-minimal-nooptimize.
+  @Test
+  public void buildR8MinimalStartupDexWithoutBoundaryOptimizations() throws Exception {
+    boolean enableMinimalStartupDex = true;
+    boolean enableStartupBoundaryOptimizations = false;
+    buildR8Startup(
+        enableMinimalStartupDex,
+        enableStartupBoundaryOptimizations,
+        artifactDirectory.resolve("optimized-minimal-nooptimize"));
+  }
+
+  // Outputs Chrome built using R8 with minimal startup dex and boundary optimizations enabled in
+  // chrome/optimized-minimal-optimize.
+  @Test
+  public void buildR8MinimalStartupDexWithBoundaryOptimizations() throws Exception {
+    boolean enableMinimalStartupDex = true;
+    boolean enableStartupBoundaryOptimizations = true;
+    buildR8Startup(
+        enableMinimalStartupDex,
+        enableStartupBoundaryOptimizations,
+        artifactDirectory.resolve("optimized-minimal-optimize"));
+  }
+
+  // Outputs Chrome built using R8 with startup layout enabled and no boundary optimizations in
+  // chrome/optimized-nominimal-nooptimize.
+  @Test
+  public void buildR8StartupLayoutWithoutBoundaryOptimizations() throws Exception {
+    boolean enableMinimalStartupDex = false;
+    boolean enableStartupBoundaryOptimizations = false;
+    buildR8Startup(
+        enableMinimalStartupDex,
+        enableStartupBoundaryOptimizations,
+        artifactDirectory.resolve("optimized-nominimal-nooptimize"));
+  }
+
+  // Outputs Chrome built using R8 with startup layout enabled and no boundary optimizations in
+  // chrome/optimized-nominimal-optimize.
+  @Test
+  public void buildR8StartupLayoutWithBoundaryOptimizations() throws Exception {
+    boolean enableMinimalStartupDex = false;
+    boolean enableStartupBoundaryOptimizations = true;
+    buildR8Startup(
+        enableMinimalStartupDex,
+        enableStartupBoundaryOptimizations,
+        artifactDirectory.resolve("optimized-nominimal-optimize"));
+  }
+
+  private void buildR8(ThrowableConsumer<R8FullTestBuilder> configuration, Path outDirectory)
+      throws Exception {
+    Files.createDirectories(outDirectory.resolve("base/dex"));
+    Files.createDirectories(outDirectory.resolve("chrome/dex"));
+    testForR8(Backend.DEX)
+        .addProgramFiles(dumpDirectory.resolve("program.jar"))
+        .addClasspathFiles(dumpDirectory.resolve("classpath.jar"))
+        .addLibraryFiles(dumpDirectory.resolve("library.jar"))
+        .addKeepRuleFiles(dumpDirectory.resolve("proguard.config"))
+        .apply(
+            testBuilder -> {
+              int i = 1;
+              boolean seenChromeSplit = false;
+              for (; i <= 12; i++) {
+                Path feature = getFeatureSplit(i);
+                boolean isChromeSplit = feature.equals(getChromeSplit());
+                seenChromeSplit |= isChromeSplit;
+                assertTrue(feature.toFile().exists());
+                testBuilder.addFeatureSplit(
+                    featureSplitBuilder ->
+                        featureSplitBuilder
+                            .addProgramResourceProvider(
+                                ArchiveProgramResourceProvider.fromArchive(feature))
+                            .setProgramConsumer(
+                                isChromeSplit
+                                    ? new DexIndexedConsumer.DirectoryConsumer(
+                                        outDirectory.resolve("chrome/dex"))
+                                    : DexIndexedConsumer.emptyConsumer())
+                            .build());
+              }
+              assertFalse(dumpDirectory.resolve("feature-" + i + ".jar").toFile().exists());
+              assertTrue(seenChromeSplit);
+              assertThat(
+                  new CodeInspector(getChromeSplit())
+                      .clazz("org.chromium.chrome.browser.ChromeApplicationImpl"),
+                  isPresent());
+            })
+        .apply(configuration)
+        .apply(this::disableR8StrictMode)
+        .apply(this::disableR8TestingDefaults)
+        .setMinApi(apiLevel)
+        .compile()
+        .writeToDirectory(outDirectory.resolve("base/dex"));
+  }
+
+  private void buildR8Startup(
+      boolean enableMinimalStartupDex,
+      boolean enableStartupBoundaryOptimizations,
+      Path outDirectory)
+      throws Exception {
+    StartupProfileProvider startupProfileProvider =
+        StringResource.fromFile(chromeDirectory.resolve("startup.txt"))
+            ::getStringWithRuntimeException;
+    buildR8(
+        testBuilder ->
+            testBuilder.addOptionsModification(
+                options ->
+                    options
+                        .getStartupOptions()
+                        .setEnableMinimalStartupDex(enableMinimalStartupDex)
+                        .setEnableStartupBoundaryOptimizations(enableStartupBoundaryOptimizations)
+                        .setStartupProfileProvider(startupProfileProvider)),
+        outDirectory);
+  }
+
+  private void disableR8StrictMode(R8FullTestBuilder testBuilder) {
+    testBuilder
+        .allowDiagnosticMessages()
+        .allowUnnecessaryDontWarnWildcards()
+        .allowUnusedDontWarnPatterns()
+        .allowUnusedProguardConfigurationRules()
+        .addOptionsModification(
+            options -> options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces());
+  }
+
+  private void disableR8TestingDefaults(R8FullTestBuilder testBuilder) {
+    testBuilder.addOptionsModification(
+        options -> options.horizontalClassMergerOptions().setEnableInterfaceMerging(false));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/conversion/StringSwitchConversionFromIfTest.java b/src/test/java/com/android/tools/r8/ir/conversion/StringSwitchConversionFromIfTest.java
index bcdd93f..6573810 100644
--- a/src/test/java/com/android/tools/r8/ir/conversion/StringSwitchConversionFromIfTest.java
+++ b/src/test/java/com/android/tools/r8/ir/conversion/StringSwitchConversionFromIfTest.java
@@ -55,7 +55,7 @@
         .enableInliningAnnotations()
         // TODO(b/135560746): Add support for treating the keys of a string-switch instruction as an
         //  identifier name string.
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/AlwaysThrowNullTest.java b/src/test/java/com/android/tools/r8/ir/optimize/AlwaysThrowNullTest.java
index 684b669..9dcfa24 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/AlwaysThrowNullTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/AlwaysThrowNullTest.java
@@ -243,7 +243,7 @@
             .enableInliningAnnotations()
             .enableMemberValuePropagationAnnotations()
             .addKeepMainRule(MAIN)
-            .noMinification()
+            .addDontObfuscate()
             .setMinApi(parameters.getApiLevel())
             .run(parameters.getRuntime(), MAIN)
             .assertSuccessWithOutput(JAVA_OUTPUT);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/MemberValuePropagationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/MemberValuePropagationTest.java
index 1433f2e..7d1141c 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/MemberValuePropagationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/MemberValuePropagationTest.java
@@ -75,7 +75,7 @@
     return testForR8(backend)
         .addProgramFiles(EXAMPLE_JAR)
         .addKeepRuleFiles(proguardConfig)
-        .noMinification()
+        .addDontObfuscate()
         .addOptionsModification(o -> o.enableClassInlining = false)
         .compile()
         .inspector();
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/NonNullParamTest.java b/src/test/java/com/android/tools/r8/ir/optimize/NonNullParamTest.java
index 9b0bb90..e582d84 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/NonNullParamTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/NonNullParamTest.java
@@ -66,7 +66,7 @@
         .addKeepMainRule(mainClass)
         .addKeepRules(ImmutableList.of("-keepattributes InnerClasses,Signature,EnclosingMethod"))
         // All tests are checking if invocations to certain null-check utils are gone.
-        .noMinification()
+        .addDontObfuscate()
         .addOptionsModification(
             options -> {
               // Need to increase a little bit to inline System.out.println
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/ObjectsRequireNonNullTest.java b/src/test/java/com/android/tools/r8/ir/optimize/ObjectsRequireNonNullTest.java
index 77da418..f40816f 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/ObjectsRequireNonNullTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/ObjectsRequireNonNullTest.java
@@ -136,7 +136,7 @@
             .enableInliningAnnotations()
             .enableMemberValuePropagationAnnotations()
             .addKeepMainRule(MAIN)
-            .noMinification()
+            .addDontObfuscate()
             .setMinApi(parameters.getApiLevel())
             .run(parameters.getRuntime(), MAIN)
             .assertSuccessWithOutput(JAVA_OUTPUT);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
index b7a3474..303bf0c 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
@@ -137,7 +137,7 @@
             })
         .allowAccessModification(allowAccessModification)
         .enableProguardTestOptions()
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .applyIf(
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/ShortenLiveRangesOfInstanceGetWithPhiUserRegressionTest.java b/src/test/java/com/android/tools/r8/ir/optimize/ShortenLiveRangesOfInstanceGetWithPhiUserRegressionTest.java
new file mode 100644
index 0000000..20b3e58
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/ShortenLiveRangesOfInstanceGetWithPhiUserRegressionTest.java
@@ -0,0 +1,56 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+/** Reproduction of b/241636314. */
+@RunWith(Parameterized.class)
+public class ShortenLiveRangesOfInstanceGetWithPhiUserRegressionTest 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)
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("0", "1", "2");
+  }
+
+  static class Main {
+
+    public final long index;
+
+    Main(long index) {
+      this.index = index;
+    }
+
+    public static void main(String[] args) {
+      Main main = new Main(1);
+      long index = 0;
+      do {
+        System.out.println(index); // Print the value of the phi.
+        index = main.index;
+        main = new Main(index + 1);
+      } while (index < 3);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeDirectPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeDirectPositiveTest.java
index f726e4f..2c79f4c 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeDirectPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeDirectPositiveTest.java
@@ -44,7 +44,7 @@
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         // TODO(b/173398086): uniqueMethodWithName() does not work with argument removal.
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), MAIN)
         .assertSuccessWithOutputLines("non-null")
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeInterfacePositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeInterfacePositiveTest.java
index 39f33af..d1a160a 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeInterfacePositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeInterfacePositiveTest.java
@@ -52,7 +52,7 @@
         .enableNeverClassInliningAnnotations()
         .enableNoHorizontalClassMergingAnnotations()
         // TODO(b/173398086): uniqueMethodWithName() does not work with argument removal.
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), MAIN)
         .assertSuccessWithOutputLines("non-null")
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeStaticPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeStaticPositiveTest.java
index 3694dc8..9f01b31 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeStaticPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeStaticPositiveTest.java
@@ -42,7 +42,7 @@
         .addKeepMainRule(MAIN)
         .enableInliningAnnotations()
         // TODO(b/173398086): uniqueMethodWithName() does not work with argument removal.
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), MAIN)
         .assertSuccessWithOutputLines("non-null")
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/ConstClassCanonicalizationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/ConstClassCanonicalizationTest.java
index 380c5be..5d32d7a 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/ConstClassCanonicalizationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/ConstClassCanonicalizationTest.java
@@ -214,7 +214,7 @@
             .addProgramClassesAndInnerClasses(MAIN)
             .addKeepMainRule(MAIN)
             .addKeepAttributeInnerClassesAndEnclosingMethod()
-            .noMinification()
+            .addDontObfuscate()
             .addOptionsModification(InternalOptions::disableNameReflectionOptimization)
             .setMinApi(parameters.getApiLevel())
             .run(parameters.getRuntime(), MAIN)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/IdempotentFunctionCallCanonicalizationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/IdempotentFunctionCallCanonicalizationTest.java
index 5c8cfd8..b3e1364 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/IdempotentFunctionCallCanonicalizationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/IdempotentFunctionCallCanonicalizationTest.java
@@ -242,7 +242,7 @@
             .addProgramClasses(MAIN)
             .enableInliningAnnotations()
             .addKeepMainRule(MAIN)
-            .noMinification()
+            .addDontObfuscate()
             .setMinApi(parameters.getRuntime())
             .run(parameters.getRuntime(), MAIN)
             .assertSuccessWithOutput(JAVA_OUTPUT);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/IllegalAccessConstClassTest.java b/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/IllegalAccessConstClassTest.java
index 7601389..126c1f0 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/IllegalAccessConstClassTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/IllegalAccessConstClassTest.java
@@ -86,12 +86,11 @@
   @Test
   public void testR8() throws Exception {
     testForR8(parameters.getBackend())
-        .addProgramClassFileData(
-            IllegalAccessConstClassTestDump.PackagePrivateClassDump.dump())
+        .addProgramClassFileData(IllegalAccessConstClassTestDump.PackagePrivateClassDump.dump())
         .addProgramClassFileData(
             IllegalAccessConstClassTestDump.FakePackagePrivateClassConsumerDump.dump())
         .addKeepMainRule(MAIN)
-        .noMinification()
+        .addDontObfuscate()
         .addOptionsModification(InternalOptions::disableNameReflectionOptimization)
         .setMinApi(parameters.getRuntime())
         .compile()
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/UnresolvableLibraryConstClassTest.java b/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/UnresolvableLibraryConstClassTest.java
index 2765bb2..cc65eae 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/UnresolvableLibraryConstClassTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/UnresolvableLibraryConstClassTest.java
@@ -112,7 +112,7 @@
         .addLibraryFiles(ToolHelper.getDefaultAndroidJar())
         .addProgramClasses(ProgramClass1.class, ProgramClass2.class, ProgramSubClass.class, MAIN)
         .addKeepMainRule(MAIN)
-        .noMinification()
+        .addDontObfuscate()
         .enableNoVerticalClassMergingAnnotations()
         .addOptionsModification(InternalOptions::disableNameReflectionOptimization)
         .setMinApi(parameters.getRuntime())
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/checkcast/CheckCastDebugTestRunner.java b/src/test/java/com/android/tools/r8/ir/optimize/checkcast/CheckCastDebugTestRunner.java
index dc86e78..e398a65 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/checkcast/CheckCastDebugTestRunner.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/checkcast/CheckCastDebugTestRunner.java
@@ -53,7 +53,7 @@
             .addOptionsModification(options -> options.enableVerticalClassMerging = false)
             .debug()
             .enableInliningAnnotations()
-            .noMinification()
+            .addDontObfuscate()
             .setMinApi(parameters.getRuntime())
             .compile()
             .writeToZip(this::setR8Out)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/checkcast/CheckCastInterfaceArrayTest.java b/src/test/java/com/android/tools/r8/ir/optimize/checkcast/CheckCastInterfaceArrayTest.java
index 4431c5a..481bdcd 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/checkcast/CheckCastInterfaceArrayTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/checkcast/CheckCastInterfaceArrayTest.java
@@ -43,7 +43,7 @@
         .addInnerClasses(CheckCastInterfaceArrayTest.class)
         .addKeepMainRule(TestClass.class)
         .setMinApi(parameters.getApiLevel())
-        .noMinification()
+        .addDontObfuscate()
         .noTreeShaking()
         .compile()
         .run(parameters.getRuntime(), TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/checkcast/RemoveCheckCastAfterClassInlining.java b/src/test/java/com/android/tools/r8/ir/optimize/checkcast/RemoveCheckCastAfterClassInlining.java
index 53d28eb..1f5c944 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/checkcast/RemoveCheckCastAfterClassInlining.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/checkcast/RemoveCheckCastAfterClassInlining.java
@@ -45,7 +45,7 @@
         testForR8(parameters.getBackend())
             .addProgramClassesAndInnerClasses(Lambda.class)
             .addKeepMainRule(Lambda.class)
-            .noMinification()
+            .addDontObfuscate()
             .setMinApi(parameters.getRuntime())
             .compile()
             .inspector();
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerBuilderWithControlFlowTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerBuilderWithControlFlowTest.java
index 44db913..4adf9ef 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerBuilderWithControlFlowTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerBuilderWithControlFlowTest.java
@@ -47,7 +47,7 @@
     testForR8(parameters.getBackend())
         .addInnerClasses(ClassInlinerBuilderWithControlFlowTest.class)
         .addKeepMainRule(TestClass.class)
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(this::inspect)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerBuilderWithMoreControlFlowTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerBuilderWithMoreControlFlowTest.java
index 3778db4..c0277d7 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerBuilderWithMoreControlFlowTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerBuilderWithMoreControlFlowTest.java
@@ -40,7 +40,7 @@
     testForR8(parameters.getBackend())
         .addInnerClasses(ClassInlinerBuilderWithMoreControlFlowTest.class)
         .addKeepMainRule(TestClass.class)
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(this::inspect)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerSimplePairBuilderTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerSimplePairBuilderTest.java
index d9b5cd1..efdaea1 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerSimplePairBuilderTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerSimplePairBuilderTest.java
@@ -61,7 +61,7 @@
         .enableAlwaysInliningAnnotations()
         .enableInliningAnnotations()
         .enableNoHorizontalClassMergingAnnotations()
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(this::inspect)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerSimplePairBuilderWithMultipleBuildsTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerSimplePairBuilderWithMultipleBuildsTest.java
index d23951c..35dbdce 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerSimplePairBuilderWithMultipleBuildsTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerSimplePairBuilderWithMultipleBuildsTest.java
@@ -50,7 +50,7 @@
         .addInnerClasses(ClassInlinerSimplePairBuilderWithMultipleBuildsTest.class)
         .addKeepMainRule(TestClass.class)
         .enableAlwaysInliningAnnotations()
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(this::inspect)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerStaticGetDirectMonitorTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerStaticGetDirectMonitorTest.java
index f6252ea..cd28e2a 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerStaticGetDirectMonitorTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerStaticGetDirectMonitorTest.java
@@ -42,7 +42,7 @@
         .addKeepMainRule(TestClass.class)
         .enableInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
-        .noMinification()
+        .addDontObfuscate()
         .compile()
         .inspect(this::inspect)
         .run(parameters.getRuntime(), TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerStaticGetExtraMethodMonitorTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerStaticGetExtraMethodMonitorTest.java
index 0e0b260..23c364e 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerStaticGetExtraMethodMonitorTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerStaticGetExtraMethodMonitorTest.java
@@ -41,7 +41,7 @@
         .addKeepMainRule(TestClass.class)
         .enableInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
-        .noMinification()
+        .addDontObfuscate()
         .compile()
         .inspect(this::inspect)
         .run(parameters.getRuntime(), TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerStaticGetMonitorTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerStaticGetMonitorTest.java
index 216f34e..f858a0e 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerStaticGetMonitorTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerStaticGetMonitorTest.java
@@ -41,7 +41,7 @@
         .addKeepMainRule(TestClass.class)
         .enableInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
-        .noMinification()
+        .addDontObfuscate()
         .compile()
         .inspect(this::inspect)
         .run(parameters.getRuntime(), TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
index 1884e5e..8e5d660 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
@@ -99,7 +99,7 @@
                         .assertMergedInto(CycleReferenceBA.class, Iface1Impl.class)
                         .assertMergedInto(Iface2Impl.class, Iface1Impl.class))
             .allowAccessModification()
-            .noMinification()
+            .addDontObfuscate()
             .setMinApi(parameters.getApiLevel())
             .run(parameters.getRuntime(), main)
             .assertSuccessWithOutput(javaOutput);
@@ -212,7 +212,7 @@
             .addKeepMainRule(main)
             .addKeepAttributes("LineNumberTable")
             .allowAccessModification()
-            .noMinification()
+            .addDontObfuscate()
             .run(main)
             .assertSuccessWithOutput(javaOutput);
 
@@ -254,7 +254,7 @@
                   o.classInlinerOptions().classInliningInstructionAllowance = 1000;
                 })
             .allowAccessModification()
-            .noMinification()
+            .addDontObfuscate()
             .run(main)
             .assertSuccessWithOutput(javaOutput);
 
@@ -306,7 +306,7 @@
             .addKeepAttributes("LineNumberTable")
             .allowAccessModification()
             .enableInliningAnnotations()
-            .noMinification()
+            .addDontObfuscate()
             .setMinApi(parameters.getApiLevel())
             .run(parameters.getRuntime(), main)
             .assertSuccessWithOutput(javaOutput);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTupleBuilderConstructorsTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTupleBuilderConstructorsTest.java
index 187c5f3..179c5ba 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTupleBuilderConstructorsTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTupleBuilderConstructorsTest.java
@@ -46,7 +46,7 @@
         .addInnerClasses(ClassInlinerTupleBuilderConstructorsTest.class)
         .addKeepMainRule(TestClass.class)
         .enableAlwaysClassInlineAnnotations()
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(this::inspect)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/IndirectInstanceOfUserInParentConstructorClassInliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/IndirectInstanceOfUserInParentConstructorClassInliningTest.java
new file mode 100644
index 0000000..d0e7d73
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/IndirectInstanceOfUserInParentConstructorClassInliningTest.java
@@ -0,0 +1,80 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.classinliner;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+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 IndirectInstanceOfUserInParentConstructorClassInliningTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(Main.class)
+        .enableInliningAnnotations()
+        .enableNoVerticalClassMergingAnnotations()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .inspect(this::inspect)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("Hello, world!");
+  }
+
+  private void inspect(CodeInspector inspector) {
+    // Verify A and B are absent (due to class inlining).
+    assertThat(inspector.clazz(Main.class), isPresent());
+    assertEquals(1, inspector.allClasses().size());
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      System.out.println(new B().get());
+    }
+  }
+
+  @NoVerticalClassMerging
+  static class A {
+
+    A() {
+      if (this instanceof B) {
+        System.out.print("Hello");
+      } else {
+        throw new RuntimeException();
+      }
+    }
+  }
+
+  static class B extends A {
+
+    @NeverInline
+    String get() {
+      return System.currentTimeMillis() > 0 ? ", world!" : null;
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/LibraryOverrideClassInliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/LibraryOverrideClassInliningTest.java
index 7b68b6d..463efbb 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/LibraryOverrideClassInliningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/LibraryOverrideClassInliningTest.java
@@ -89,7 +89,7 @@
     CodeInspector inspector =
         testForR8(parameters.getBackend())
             .enableInliningAnnotations()
-            .noMinification()
+            .addDontObfuscate()
             .addProgramClasses(
                 Main.class, SimpleLibraryOverride.class, NonSimpleLibraryOverride.class)
             .addKeepMainRule(Main.class)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/PrivateOverridePublicizerDevirtualizerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/PrivateOverridePublicizerDevirtualizerTest.java
index 86739dc..6570849 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/PrivateOverridePublicizerDevirtualizerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/PrivateOverridePublicizerDevirtualizerTest.java
@@ -53,7 +53,7 @@
         .enableNeverClassInliningAnnotations()
         .addKeepMainRule(Main.class)
         .allowAccessModification()
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/fields/SwitchOnConstantClassIdAfterBranchPruningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/fields/SwitchOnConstantClassIdAfterBranchPruningTest.java
index f3d6a4d..8c029a5 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/fields/SwitchOnConstantClassIdAfterBranchPruningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/fields/SwitchOnConstantClassIdAfterBranchPruningTest.java
@@ -46,7 +46,7 @@
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
-        .noMinification()
+        .addDontObfuscate()
         .compile()
         .inspect(
             inspector -> {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/fields/SwitchOnNonConstantClassIdAfterBranchPruningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/fields/SwitchOnNonConstantClassIdAfterBranchPruningTest.java
index 1c2705e..88efb9c 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/fields/SwitchOnNonConstantClassIdAfterBranchPruningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/fields/SwitchOnNonConstantClassIdAfterBranchPruningTest.java
@@ -46,7 +46,7 @@
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
-        .noMinification()
+        .addDontObfuscate()
         .compile()
         .inspect(
             inspector -> {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/DoubleInliningNullCheckTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/DoubleInliningNullCheckTest.java
index 94fba01..bf9b044 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/DoubleInliningNullCheckTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/DoubleInliningNullCheckTest.java
@@ -36,7 +36,7 @@
     testForR8(parameters.getBackend())
         .addInnerClasses(DoubleInliningNullCheckTest.class)
         .addKeepMainRule(TestClass.class)
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), TestClass.class)
         .assertSuccessWithOutputLines("true")
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineLibraryInterfaceMethodTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineLibraryInterfaceMethodTest.java
index 3c76052..634d0a8 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineLibraryInterfaceMethodTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineLibraryInterfaceMethodTest.java
@@ -46,7 +46,7 @@
         .addInnerClasses(InlineLibraryInterfaceMethodTest.class)
         .addKeepMainRule(TestClass.class)
         .setMinApi(testRuntime)
-        .noMinification()
+        .addDontObfuscate()
         .run(testRuntime, TestClass.class)
         .assertSuccess()
         .inspect(
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineSynthesizedLambdaClass.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineSynthesizedLambdaClass.java
index 1bd8db6..dbc7c5b 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineSynthesizedLambdaClass.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineSynthesizedLambdaClass.java
@@ -41,7 +41,7 @@
             .addProgramClassesAndInnerClasses(Lambda.class)
             .addKeepMainRule(Lambda.class)
             .allowAccessModification()
-            .noMinification()
+            .addDontObfuscate()
             .setMinApi(parameters.getApiLevel())
             .run(parameters.getRuntime(), Lambda.class)
             .assertSuccessWithOutput(javaOutput)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/conditionalsimpleinlining/NopInliningConstraintTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/conditionalsimpleinlining/NopInliningConstraintTest.java
index dcc572b..c55ea29 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/conditionalsimpleinlining/NopInliningConstraintTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/conditionalsimpleinlining/NopInliningConstraintTest.java
@@ -41,7 +41,7 @@
         .enableAlwaysInliningAnnotations()
         .enableInliningAnnotations()
         // TODO(b/173398086): uniqueMethodWithName() does not work with argument removal.
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/interfacemethods/InlineDefaultInterfaceMethodTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/interfacemethods/InlineDefaultInterfaceMethodTest.java
index 177353f..247781d 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/interfacemethods/InlineDefaultInterfaceMethodTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/interfacemethods/InlineDefaultInterfaceMethodTest.java
@@ -43,7 +43,7 @@
             .setMinApi(parameters.getApiLevel())
             .enableNeverClassInliningAnnotations()
             .enableNoVerticalClassMergingAnnotations()
-            .noMinification()
+            .addDontObfuscate()
             .run(parameters.getRuntime(), TestClass.class)
             .assertSuccessWithOutput(expectedOutput)
             .inspector();
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/lambda/LambdaMethodInliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/lambda/LambdaMethodInliningTest.java
index a63addb..b6ca59d 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/lambda/LambdaMethodInliningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/lambda/LambdaMethodInliningTest.java
@@ -44,7 +44,7 @@
         .enableNoVerticalClassMergingAnnotations()
         .addOptionsModification(options -> options.enableClassInlining = false)
         // TODO(b/173398086): Horizontal class merging breaks uniqueMethodWithName().
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(this::inspect)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/logging/CustomLoggingFrameworkRemovalTest.java b/src/test/java/com/android/tools/r8/ir/optimize/logging/CustomLoggingFrameworkRemovalTest.java
index b40d47e..c1957f4 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/logging/CustomLoggingFrameworkRemovalTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/logging/CustomLoggingFrameworkRemovalTest.java
@@ -55,7 +55,7 @@
         .enableAssumeNoSideEffectsAnnotations()
         .enableInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
-        .noMinification()
+        .addDontObfuscate()
         .compile()
         .inspect(
             inspector -> {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/loops/LoopWith1Iterations.java b/src/test/java/com/android/tools/r8/ir/optimize/loops/LoopWith1Iterations.java
index dc3ca19..9190a4c 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/loops/LoopWith1Iterations.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/loops/LoopWith1Iterations.java
@@ -34,7 +34,7 @@
         .addProgramClasses(Main.class)
         .addKeepMainRule(Main.class)
         .enableInliningAnnotations()
-        .noMinification()
+        .addDontObfuscate()
         .compile()
         .inspect(this::assertLoopRemoved)
         .run(parameters.getRuntime(), Main.class)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/loops/LoopWith1IterationsEscape.java b/src/test/java/com/android/tools/r8/ir/optimize/loops/LoopWith1IterationsEscape.java
index 07efa85..f8260c0 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/loops/LoopWith1IterationsEscape.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/loops/LoopWith1IterationsEscape.java
@@ -33,7 +33,7 @@
         .addProgramClasses(Main.class)
         .addKeepMainRule(Main.class)
         .enableInliningAnnotations()
-        .noMinification()
+        .addDontObfuscate()
         .compile()
         .run(parameters.getRuntime(), Main.class)
         .assertSuccessWithOutputLines("end 0", "iteration", "end 1");
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/B138912149.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/B138912149.java
index bee06af..16dbf43a 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/B138912149.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/B138912149.java
@@ -37,16 +37,18 @@
         .addInnerClasses(B138912149.class)
         .addKeepMainRule(TestClass.class)
         .setMinApi(parameters.getRuntime())
-        .noMinification()
+        .addDontObfuscate()
         .compile()
         .run(parameters.getRuntime(), TestClass.class)
         .assertSuccessWithOutputLines("The end")
-        .inspect(codeInspector -> {
-          // All <clinit>s are simplified and shrunk.
-          codeInspector.forAllClasses(classSubject -> {
-            assertThat(classSubject.clinit(), not(isPresent()));
-          });
-        });
+        .inspect(
+            codeInspector -> {
+              // All <clinit>s are simplified and shrunk.
+              codeInspector.forAllClasses(
+                  classSubject -> {
+                    assertThat(classSubject.clinit(), not(isPresent()));
+                  });
+            });
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/StaticFieldPropagationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/StaticFieldPropagationTest.java
index bd204c7..9a7d252 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/StaticFieldPropagationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/StaticFieldPropagationTest.java
@@ -31,7 +31,7 @@
             .addProgramClasses(TestClass.class, Log.class)
             .addKeepMainRule(TestClass.class)
             .enableInliningAnnotations()
-            .noMinification()
+            .addDontObfuscate()
             .run(TestClass.class)
             .assertSuccessWithOutput(expectedOutput);
 
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/fields/FieldTypeStrengtheningCollisionTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/fields/FieldTypeStrengtheningCollisionTest.java
index 9e5e433..4a7aae0 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/fields/FieldTypeStrengtheningCollisionTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/fields/FieldTypeStrengtheningCollisionTest.java
@@ -12,8 +12,6 @@
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.ir.optimize.membervaluepropagation.fields.FieldTypeStrengtheningTest.A;
-import com.android.tools.r8.ir.optimize.membervaluepropagation.fields.FieldTypeStrengtheningTest.Main;
 import com.android.tools.r8.transformers.ClassFileTransformer.FieldPredicate;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.FieldSubject;
@@ -44,7 +42,7 @@
         .addKeepRules(
             "-keep class " + Main.class.getTypeName() + " { " + A.class.getTypeName() + " f; }")
         .enableInliningAnnotations()
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/OutlinesWithNonNullTest.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/OutlinesWithNonNullTest.java
index c4b84a7..696a413 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/outliner/OutlinesWithNonNullTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/OutlinesWithNonNullTest.java
@@ -51,7 +51,7 @@
         .addKeepMainRule(TestClassWithNonNullOnOneSide.class)
         .setMinApi(parameters.getApiLevel())
         .allowAccessModification()
-        .noMinification()
+        .addDontObfuscate()
         .addOptionsModification(
             options -> {
               options.outline.threshold = 2;
@@ -72,7 +72,7 @@
         .addKeepMainRule(TestClassWithNonNullOnBothSides.class)
         .setMinApi(parameters.getApiLevel())
         .allowAccessModification()
-        .noMinification()
+        .addDontObfuscate()
         .addOptionsModification(
             options -> {
               options.outline.threshold = 2;
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/arraytypes/OutlinesWithClassArrayTypeArguments.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/arraytypes/OutlinesWithClassArrayTypeArguments.java
index 8bd05e0..63aed12 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/outliner/arraytypes/OutlinesWithClassArrayTypeArguments.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/arraytypes/OutlinesWithClassArrayTypeArguments.java
@@ -61,7 +61,7 @@
         .addInnerClasses(OutlinesWithClassArrayTypeArguments.class)
         .addKeepMainRule(TestClass.class)
         .setMinApi(parameters.getApiLevel())
-        .noMinification()
+        .addDontObfuscate()
         .addOptionsModification(
             options -> {
               options.outline.threshold = 2;
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/arraytypes/OutlinesWithInterfaceArrayTypeArguments.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/arraytypes/OutlinesWithInterfaceArrayTypeArguments.java
index 5a0b120..4f6a822 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/outliner/arraytypes/OutlinesWithInterfaceArrayTypeArguments.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/arraytypes/OutlinesWithInterfaceArrayTypeArguments.java
@@ -75,7 +75,7 @@
         .addKeepClassAndMembersRules(ClassImplementingIface.class)
         .addKeepClassAndMembersRules(OtherClassImplementingIface.class)
         .setMinApi(parameters.getApiLevel())
-        .noMinification()
+        .addDontObfuscate()
         .addOptionsModification(
             options -> {
               options.outline.threshold = 2;
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/arraytypes/OutlinesWithPrimitiveArrayTypeArguments.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/arraytypes/OutlinesWithPrimitiveArrayTypeArguments.java
index 6334006..2065f83 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/outliner/arraytypes/OutlinesWithPrimitiveArrayTypeArguments.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/arraytypes/OutlinesWithPrimitiveArrayTypeArguments.java
@@ -60,7 +60,7 @@
         .addInnerClasses(OutlinesWithPrimitiveArrayTypeArguments.class)
         .addKeepMainRule(TestClass.class)
         .setMinApi(parameters.getRuntime())
-        .noMinification()
+        .addDontObfuscate()
         .addOptionsModification(
             options -> {
               options.outline.threshold = 2;
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/b112247415/B112247415.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/b112247415/B112247415.java
index 685d805..9b5e916 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/outliner/b112247415/B112247415.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/b112247415/B112247415.java
@@ -82,7 +82,7 @@
 
     CodeInspector inspector =
         testForR8(parameters.getBackend())
-            .noMinification()
+            .addDontObfuscate()
             .setMinApi(parameters.getApiLevel())
             .addProgramClassesAndInnerClasses(TestClass.class)
             .addKeepMainRule(TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/b133215941/B133215941.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/b133215941/B133215941.java
index 9c8eb76..ee81c79 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/outliner/b133215941/B133215941.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/b133215941/B133215941.java
@@ -70,7 +70,7 @@
         .addKeepMainRule(TestClass.class)
         .addKeepClassAndMembersRules(ClassWithStaticMethod.class)
         .setMinApi(parameters.getApiLevel())
-        .noMinification()
+        .addDontObfuscate()
         .addOptionsModification(options -> options.outline.threshold = 2)
         .compile()
         .inspect(this::validateOutlining)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/classtypes/B134462736.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/classtypes/B134462736.java
index 883c62a..9437d1d 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/outliner/classtypes/B134462736.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/classtypes/B134462736.java
@@ -69,7 +69,7 @@
         .enableConstantArgumentAnnotations()
         .enableNoMethodStaticizingAnnotations()
         .setMinApi(parameters.getApiLevel())
-        .noMinification()
+        .addDontObfuscate()
         .addOptionsModification(
             options -> {
               options.outline.threshold = 2;
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/primitivetypes/PrimitiveTypesTest.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/primitivetypes/PrimitiveTypesTest.java
index 0e8b0ad..8385328 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/outliner/primitivetypes/PrimitiveTypesTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/primitivetypes/PrimitiveTypesTest.java
@@ -78,7 +78,7 @@
         .addProgramClasses(MyStringBuilder.class)
         .addKeepMainRule(testClass)
         .setMinApi(parameters.getApiLevel())
-        .noMinification()
+        .addDontObfuscate()
         .addOptionsModification(
             options -> {
               options.outline.threshold = 2;
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/reflection/ForNameTest.java b/src/test/java/com/android/tools/r8/ir/optimize/reflection/ForNameTest.java
index 26b927e..2e36ba3 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/reflection/ForNameTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/reflection/ForNameTest.java
@@ -145,7 +145,7 @@
             .addKeepMainRule(MAIN)
             .addKeepAllClassesRule()
             .addKeepAttributes("EnclosingMethod", "InnerClasses")
-            .noMinification()
+            .addDontObfuscate()
             .setMinApi(parameters.getApiLevel())
             .run(parameters.getRuntime(), MAIN);
     test(result, 4, 0);
@@ -157,7 +157,7 @@
             .addKeepMainRule(MAIN)
             .addKeepAllClassesRule()
             .addKeepAttributes("EnclosingMethod", "InnerClasses")
-            .noMinification()
+            .addDontObfuscate()
             .setMinApi(parameters.getApiLevel())
             .run(parameters.getRuntime(), MAIN)
             .assertSuccessWithOutput(JAVA_OUTPUT);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetClassBaseAndSubTest.java b/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetClassBaseAndSubTest.java
index b5daa52..8190001 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetClassBaseAndSubTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetClassBaseAndSubTest.java
@@ -42,7 +42,7 @@
   @Test
   public void testR8() throws Exception {
     testForR8(parameters.getBackend())
-        .noMinification()
+        .addDontObfuscate()
         .addInnerClasses(GetClassBaseAndSubTest.class)
         .addKeepMainRule(TestClass.class)
         .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetClassTest.java b/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetClassTest.java
index 55867a1..908ce00 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetClassTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetClassTest.java
@@ -14,7 +14,6 @@
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.NoHorizontalClassMerging;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -215,7 +214,7 @@
         .enableInliningAnnotations()
         .enableNoHorizontalClassMergingAnnotations()
         .addKeepMainRule(MAIN)
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), MAIN)
         .assertSuccessWithOutput(JAVA_OUTPUT)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java
index 9cd0f5f..f265abe 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java
@@ -131,7 +131,7 @@
             .enableInliningAnnotations()
             .enableNoHorizontalClassMergingAnnotations()
             .addKeepMainRule(main)
-            .noMinification()
+            .addDontObfuscate()
             .addKeepAttributes("InnerClasses", "EnclosingMethod")
             .addOptionsModification(this::configure)
             .allowAccessModification()
@@ -247,7 +247,7 @@
             .enableSideEffectAnnotations()
             .addKeepMainRule(main)
             .allowAccessModification()
-            .noMinification()
+            .addDontObfuscate()
             .addOptionsModification(this::configure)
             .setMinApi(parameters.getApiLevel())
             .run(parameters.getRuntime(), main);
@@ -286,7 +286,7 @@
             .enableMemberValuePropagationAnnotations()
             .addKeepMainRule(main)
             .allowAccessModification()
-            .noMinification()
+            .addDontObfuscate()
             .addOptionsModification(this::configure)
             .setMinApi(parameters.getApiLevel())
             .run(parameters.getRuntime(), main)
@@ -411,7 +411,7 @@
             .enableInliningAnnotations()
             .addKeepMainRule(main)
             .allowAccessModification()
-            .noMinification()
+            .addDontObfuscate()
             .addOptionsModification(this::configure)
             .setMinApi(parameters.getApiLevel())
             .run(parameters.getRuntime(), main)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/NameThenLengthTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/NameThenLengthTest.java
index b96ee7f..f49ca5e 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/NameThenLengthTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/NameThenLengthTest.java
@@ -168,7 +168,7 @@
             .addProgramClasses(MAIN)
             .enableInliningAnnotations()
             .addKeepMainRule(MAIN)
-            .noMinification()
+            .addDontObfuscate()
             .addOptionsModification(this::configure)
             .setMinApi(parameters.getApiLevel())
             .run(parameters.getRuntime(), MAIN)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderStoredToDeadFieldTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderStoredToDeadFieldTest.java
index 7c27841..c840e13 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderStoredToDeadFieldTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderStoredToDeadFieldTest.java
@@ -41,7 +41,7 @@
     testForR8(parameters.getBackend())
         .addInnerClasses(StringBuilderStoredToDeadFieldTest.class)
         .addKeepMainRule(MAIN)
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getRuntime())
         .run(parameters.getRuntime(), MAIN)
         .assertSuccess()
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatenationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatenationTest.java
index 617fd63..277e1e3 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatenationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatenationTest.java
@@ -229,7 +229,7 @@
             .addProgramClasses(MAIN)
             .enableInliningAnnotations()
             .addKeepMainRule(MAIN)
-            .noMinification()
+            .addDontObfuscate()
             .setMinApi(parameters.getApiLevel())
             .run(parameters.getRuntime(), MAIN)
             .assertSuccessWithOutput(JAVA_OUTPUT);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringToStringTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringToStringTest.java
index a6f314d..b2ecca3 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringToStringTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringToStringTest.java
@@ -109,7 +109,7 @@
             .enableInliningAnnotations()
             .addKeepMainRule(MAIN)
             .setMinApi(parameters.getRuntime())
-            .noMinification()
+            .addDontObfuscate()
             .run(parameters.getRuntime(), MAIN)
             .assertSuccessWithOutput(JAVA_OUTPUT);
     test(result, 0);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringValueOfTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringValueOfTest.java
index 3ea38e7..dba68cd 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringValueOfTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringValueOfTest.java
@@ -113,7 +113,7 @@
             .enableMemberValuePropagationAnnotations()
             .addKeepMainRule(MAIN)
             .setMinApi(parameters.getApiLevel())
-            .noMinification()
+            .addDontObfuscate()
             .addOptionsModification(this::configure)
             .compile()
             .inspect(
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/switches/SwitchMapWithEnumDependencyTest.java b/src/test/java/com/android/tools/r8/ir/optimize/switches/SwitchMapWithEnumDependencyTest.java
index 14d5f18..c533bbb 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/switches/SwitchMapWithEnumDependencyTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/switches/SwitchMapWithEnumDependencyTest.java
@@ -34,7 +34,7 @@
         testForR8(parameters.getBackend())
             .addInnerClasses(SwitchMapWithEnumDependencyTest.class)
             .addKeepMainRule(TestClass.class)
-            .noMinification()
+            .addDontObfuscate()
             .enableInliningAnnotations()
             .setMinApi(parameters.getApiLevel())
             .compile();
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/switches/SwitchMapWithMissingFieldTest.java b/src/test/java/com/android/tools/r8/ir/optimize/switches/SwitchMapWithMissingFieldTest.java
index 707eb10..5489745 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/switches/SwitchMapWithMissingFieldTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/switches/SwitchMapWithMissingFieldTest.java
@@ -48,7 +48,7 @@
                 .setClassDescriptor(descriptor(IncompleteEnum.class))
                 .transform())
         .addKeepMainRule(TestClass.class)
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .run(parameters.getRuntime(), TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/switches/SwitchMapWithSubtypesTest.java b/src/test/java/com/android/tools/r8/ir/optimize/switches/SwitchMapWithSubtypesTest.java
index 7bc82e8..d9180bd 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/switches/SwitchMapWithSubtypesTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/switches/SwitchMapWithSubtypesTest.java
@@ -36,7 +36,7 @@
         testForR8(parameters.getBackend())
             .addInnerClasses(SwitchMapWithSubtypesTest.class)
             .addKeepMainRule(TestClass.class)
-            .noMinification()
+            .addDontObfuscate()
             .enableInliningAnnotations()
             .setMinApi(parameters.getApiLevel())
             .compile();
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/switches/SwitchMapWithUnexpectedFieldTest.java b/src/test/java/com/android/tools/r8/ir/optimize/switches/SwitchMapWithUnexpectedFieldTest.java
index 167cd47..ec3c1ed 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/switches/SwitchMapWithUnexpectedFieldTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/switches/SwitchMapWithUnexpectedFieldTest.java
@@ -54,7 +54,7 @@
                 .setClassDescriptor(descriptor(CompleteEnum.class))
                 .transform())
         .addKeepMainRule(TestClass.class)
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .run(parameters.getRuntime(), TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/InvokeMethodWithReceiverOptimizationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/InvokeMethodWithReceiverOptimizationTest.java
index eca11f0..55889a3 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/InvokeMethodWithReceiverOptimizationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/InvokeMethodWithReceiverOptimizationTest.java
@@ -68,7 +68,7 @@
                     options.callSiteOptimizationOptions().setEnabled(enableArgumentPropagation))
             // TODO(b/120764902): The calls to getOriginalName() below does not work in presence of
             //  argument removal.
-            .noMinification()
+            .addDontObfuscate()
             .setMinApi(parameters.getApiLevel())
             .run(parameters.getRuntime(), TestClass.class)
             .assertSuccessWithOutput(expected)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/ParameterRewritingTest.java b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/ParameterRewritingTest.java
index ee595ce..17d3ccc 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/ParameterRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/ParameterRewritingTest.java
@@ -56,7 +56,7 @@
             .enableInliningAnnotations()
             .enableNoHorizontalClassMergingAnnotations()
             .addOptionsModification(options -> options.enableClassInlining = false)
-            .noMinification()
+            .addDontObfuscate()
             .setMinApi(parameters.getApiLevel())
             .run(parameters.getRuntime(), TestClass.class)
             .assertSuccessWithOutput(expected)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/VoidReturnTypeRewritingTest.java b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/VoidReturnTypeRewritingTest.java
index 16c8f61..831330d 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/VoidReturnTypeRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/VoidReturnTypeRewritingTest.java
@@ -61,7 +61,7 @@
             .enableNoVerticalClassMergingAnnotations()
             .enableNoHorizontalClassMergingAnnotations()
             .addOptionsModification(options -> options.enableClassInlining = false)
-            .noMinification()
+            .addDontObfuscate()
             .setMinApi(parameters.getApiLevel())
             .run(parameters.getRuntime(), TestClass.class)
             .assertSuccessWithOutput(expected)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/EffectivelyUnusedNullableArgumentTest.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/EffectivelyUnusedNullableArgumentTest.java
index f01e7b7..0f5b122 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/EffectivelyUnusedNullableArgumentTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/EffectivelyUnusedNullableArgumentTest.java
@@ -42,7 +42,7 @@
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         // TODO(b/173398086): uniqueMethodWithName() does not work with argument changes.
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedAndUninstantiatedTypesTest.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedAndUninstantiatedTypesTest.java
index 7ca05fb..96577a4 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedAndUninstantiatedTypesTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedAndUninstantiatedTypesTest.java
@@ -38,7 +38,7 @@
     testForR8(parameters.getBackend())
         .addInnerClasses(UnusedAndUninstantiatedTypesTest.class)
         .addKeepMainRule(Main.class)
-        .noMinification()
+        .addDontObfuscate()
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceRemovalPackageBoundaryTest.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceRemovalPackageBoundaryTest.java
index 9c4046d..2ea0223 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceRemovalPackageBoundaryTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceRemovalPackageBoundaryTest.java
@@ -47,7 +47,7 @@
         .enableNeverClassInliningAnnotations()
         .enableNoVerticalClassMergingAnnotations()
         .setMinApi(parameters.getApiLevel())
-        .noMinification()
+        .addDontObfuscate()
         .compile()
         .inspect(
             inspector -> {
diff --git a/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java b/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
index 5759deb..5cdae5d 100644
--- a/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
+++ b/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
@@ -262,7 +262,7 @@
         .allowAccessModification(allowAccessModification)
         .allowDiagnosticWarningMessages()
         .enableProguardTestOptions()
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(testParameters.getApiLevel())
         .apply(configuration)
         .compile()
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinDuplicateAnnotationTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinDuplicateAnnotationTest.java
index f2792a4..6f65db0 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinDuplicateAnnotationTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinDuplicateAnnotationTest.java
@@ -80,7 +80,7 @@
           .addProgramFiles(compiledJars.getForConfiguration(kotlinc, targetVersion))
           .addKeepMainRule(MAIN)
           .addKeepRules(KEEP_RULES)
-          .noMinification()
+          .addDontObfuscate()
           .addOptionsModification(optionsModifier)
           .compile();
       fail("Expect to fail");
@@ -97,7 +97,7 @@
         .addProgramFiles(compiledJars.getForConfiguration(kotlinc, targetVersion))
         .addKeepMainRule(MAIN)
         .addKeepRules(KEEP_RULES)
-        .noMinification()
+        .addDontObfuscate()
         .addOptionsModification(optionsModifier)
         .compile()
         .assertNoMessages();
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineChainTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineChainTest.java
index f9e965c..011cc58 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineChainTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineChainTest.java
@@ -61,7 +61,7 @@
         .allowAccessModification(allowAccessModification)
         .allowDiagnosticWarningMessages()
         .setMinApi(parameters.getApiLevel())
-        .noMinification()
+        .addDontObfuscate()
         .compile()
         .assertAllWarningMessagesMatch(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
         .run(parameters.getRuntime(), MAIN, "foobar")
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineTest.java
index 6d64e37..a664d59 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineTest.java
@@ -63,7 +63,7 @@
                 "-keepclasseswithmembers class " + MAIN + "{", "  public static *** *(...);", "}"))
         .allowAccessModification(allowAccessModification)
         .allowDiagnosticWarningMessages()
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .assertAllWarningMessagesMatch(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
@@ -118,7 +118,7 @@
                 "}"))
         .allowAccessModification(allowAccessModification)
         .allowDiagnosticWarningMessages()
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .assertAllWarningMessagesMatch(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
diff --git a/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinReflectionLibTest.java b/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinReflectionLibTest.java
index c53700b..0a93f62 100644
--- a/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinReflectionLibTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinReflectionLibTest.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8.kotlin;
 
 import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_3_72;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_7_0;
 
 import com.android.tools.r8.KotlinTestBase;
 import com.android.tools.r8.KotlinTestParameters;
@@ -56,6 +57,8 @@
         .addKeepAttributes(ProguardKeepAttributes.ENCLOSING_METHOD)
         .allowUnusedDontWarnKotlinReflectJvmInternal(kotlinc.isNot(KOTLINC_1_3_72))
         .allowUnusedProguardConfigurationRules(kotlinc.isNot(KOTLINC_1_3_72))
+        .allowUnusedDontWarnJavaLangClassValue(
+            kotlinc.getCompilerVersion().isGreaterThan(KOTLINC_1_7_0))
         .apply(testBuilderConsumer)
         .compile()
         .apply(compileResultBuilder)
@@ -64,7 +67,7 @@
 
   @Test
   public void testAsIs() throws Exception {
-    test(builder -> builder.noMinification().addDontOptimize().noTreeShaking());
+    test(builder -> builder.addDontObfuscate().addDontOptimize().noTreeShaking());
   }
 
   @Test
@@ -74,7 +77,7 @@
 
   @Test
   public void testDontShrinkAndDontObfuscate() throws Exception {
-    test(builder -> builder.noMinification().noTreeShaking());
+    test(builder -> builder.addDontObfuscate().noTreeShaking());
   }
 
   @Test
@@ -98,7 +101,7 @@
 
   @Test
   public void testDontObfuscate() throws Exception {
-    test(TestShrinkerBuilder::noMinification);
+    test(TestShrinkerBuilder::addDontObfuscate);
   }
 
 }
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KStyleKotlinLambdaMergingWithEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KStyleKotlinLambdaMergingWithEnumUnboxingTest.java
index b4abfb6..a3e5c28 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/KStyleKotlinLambdaMergingWithEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KStyleKotlinLambdaMergingWithEnumUnboxingTest.java
@@ -51,7 +51,7 @@
         .allowDiagnosticWarningMessages()
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .assertAllWarningMessagesMatch(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionFunctionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionFunctionTest.java
index 7a37728..619f4a1 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionFunctionTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionFunctionTest.java
@@ -12,10 +12,12 @@
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
 
 import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.Box;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -179,6 +181,43 @@
         .assertSuccessWithOutput(EXPECTED);
   }
 
+  @Test
+  public void testMetadataInExtensionFunction_renamedKotlinSources() throws Exception {
+    assumeTrue(kotlinc.getCompilerVersion().isGreaterThanOrEqualTo(KOTLINC_1_4_20));
+    Box<String> renamedKtHolder = new Box<>();
+    Path libJar =
+        testForR8(parameters.getBackend())
+            .addClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
+            .addProgramFiles(extLibJarMap.getForConfiguration(kotlinc, targetVersion))
+            // Keep the B class and its interface (which has the doStuff method).
+            .addKeepRules("-keep class **.B")
+            .addKeepRules("-keep class **.I { <methods>; }")
+            // Keep Super, but allow minification.
+            .addKeepRules("-keep,allowobfuscation class **.Super")
+            // Keep the BKt extension function which requires metadata
+            // to be called with Kotlin syntax from other kotlin code.
+            .addKeepRules("-keep,allowobfuscation class **.BKt { <methods>; }")
+            .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+            .addKeepAttributes(ProguardKeepAttributes.SIGNATURE)
+            .addKeepAttributes(ProguardKeepAttributes.INNER_CLASSES)
+            .addKeepAttributes(ProguardKeepAttributes.ENCLOSING_METHOD)
+            .compile()
+            .inspect(
+                inspector -> {
+                  ClassSubject clazz = inspector.clazz(PKG + ".extension_function_lib.BKt");
+                  assertThat(clazz, isPresentAndRenamed());
+                  renamedKtHolder.set(clazz.getFinalName());
+                })
+            .writeToZip();
+
+    kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion)
+        .addClasspathFiles(libJar)
+        .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/extension_function_app", "main"))
+        .setOutputPath(temp.newFolder().toPath())
+        // TODO(b/242289529): Expect that we can compile without errors.
+        .compile(true);
+  }
+
   private void inspectRenamed(CodeInspector inspector) {
     String superClassName = PKG + ".extension_function_lib.Super";
     String bClassName = PKG + ".extension_function_lib.B";
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePrunedObjectsTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePrunedObjectsTest.java
index 8532cf9..26d9934 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePrunedObjectsTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePrunedObjectsTest.java
@@ -81,7 +81,7 @@
             .addKeepClassAndMembersRules(PKG_LIB + ".SubUser")
             .addKeepRuntimeVisibleAnnotations()
             .enableProguardTestOptions()
-            .noMinification()
+            .addDontObfuscate()
             .compile()
             .inspect(this::checkPruned)
             .writeToZip();
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
index baf09cc..d74e3d2 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8.kotlin.metadata;
 
 import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_3_72;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_7_0;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
@@ -57,6 +58,8 @@
             .allowDiagnosticWarningMessages()
             .setMinApi(parameters.getApiLevel())
             .allowUnusedDontWarnKotlinReflectJvmInternal(kotlinc.isNot(KOTLINC_1_3_72))
+            .allowUnusedDontWarnJavaLangClassValue(
+                kotlinc.getCompilerVersion().isGreaterThan(KOTLINC_1_7_0))
             .compile()
             .assertNoErrorMessages()
             .apply(KotlinMetadataTestBase::verifyExpectedWarningsFromKotlinReflectAndStdLib)
diff --git a/src/test/java/com/android/tools/r8/kotlin/optimize/switches/KotlinEnumSwitchTest.java b/src/test/java/com/android/tools/r8/kotlin/optimize/switches/KotlinEnumSwitchTest.java
index b14aeb4..0963963 100644
--- a/src/test/java/com/android/tools/r8/kotlin/optimize/switches/KotlinEnumSwitchTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/optimize/switches/KotlinEnumSwitchTest.java
@@ -60,7 +60,7 @@
               options.enableEnumSwitchMapRemoval = enableSwitchMapRemoval;
             })
         .setMinApi(parameters.getApiLevel())
-        .noMinification()
+        .addDontObfuscate()
         .allowDiagnosticWarningMessages()
         .compile()
         .assertAllWarningMessagesMatch(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
diff --git a/src/test/java/com/android/tools/r8/kotlin/reflection/ReflectiveConstructionWithInlineClassTest.java b/src/test/java/com/android/tools/r8/kotlin/reflection/ReflectiveConstructionWithInlineClassTest.java
index 3b9e678..6273755 100644
--- a/src/test/java/com/android/tools/r8/kotlin/reflection/ReflectiveConstructionWithInlineClassTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/reflection/ReflectiveConstructionWithInlineClassTest.java
@@ -100,6 +100,12 @@
         .setMinApi(parameters.getApiLevel())
         .addKeepMainRule(MAIN_CLASS)
         .addKeepClassAndMembersRules(PKG + ".Data")
+        // TODO(b/242158616): Figure out why this is necessary.
+        .applyIf(
+            kotlinc.getCompilerVersion().isGreaterThan(KotlinCompilerVersion.KOTLINC_1_7_0),
+            b ->
+                b.addKeepClassAndMembersRules(
+                    "kotlin.reflect.jvm.internal.ClassValueCache$initClassValue$1"))
         .addKeepEnumsRule()
         .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
         .allowDiagnosticMessages()
diff --git a/src/test/java/com/android/tools/r8/kotlin/stringplus/StringPlusTest.java b/src/test/java/com/android/tools/r8/kotlin/stringplus/StringPlusTest.java
index d189806..b3ba245 100644
--- a/src/test/java/com/android/tools/r8/kotlin/stringplus/StringPlusTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/stringplus/StringPlusTest.java
@@ -85,7 +85,7 @@
         .allowDiagnosticWarningMessages()
         .addKeepMainRule(MAIN)
         .addKeepRules("-keep class " + MAIN + "{ void keepFor*(...); }")
-        .noMinification()
+        .addDontObfuscate()
         .compile()
         .inspect(
             inspector -> {
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListFromGenerateMainDexInliningSpuriousRootTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListFromGenerateMainDexInliningSpuriousRootTest.java
index 3290031..47bd62e 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListFromGenerateMainDexInliningSpuriousRootTest.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListFromGenerateMainDexInliningSpuriousRootTest.java
@@ -84,7 +84,7 @@
             .enableInliningAnnotations()
             .enableNoHorizontalClassMergingAnnotations()
             .setMinApi(parameters.getApiLevel())
-            .noMinification()
+            .addDontObfuscate()
             .allowDiagnosticMessages()
             .compileWithExpectedDiagnostics(
                 diagnostics ->
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListFromGenerateMainDexInliningWithTracingTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListFromGenerateMainDexInliningWithTracingTest.java
index bb3b81c..50e65c9 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListFromGenerateMainDexInliningWithTracingTest.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListFromGenerateMainDexInliningWithTracingTest.java
@@ -85,7 +85,7 @@
             .collectMainDexClasses()
             .enableInliningAnnotations()
             .enableNoHorizontalClassMergingAnnotations()
-            .noMinification()
+            .addDontObfuscate()
             .setMinApi(parameters.getApiLevel())
             .allowDiagnosticMessages()
             .compileWithExpectedDiagnostics(
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListMergeInRootTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListMergeInRootTest.java
index 9451f9a..45df36d 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListMergeInRootTest.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListMergeInRootTest.java
@@ -42,7 +42,7 @@
         .enableNeverClassInliningAnnotations()
         .enableNoHorizontalClassMergingAnnotations()
         .enableInliningAnnotations()
-        .noMinification()
+        .addDontObfuscate()
         .addMainDexRules(
             "-keep class " + Main.class.getTypeName() + " { public static void main(***); }")
         .addOptionsModification(
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListOutputTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListOutputTest.java
index 2dd90c1..5bc57ba 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListOutputTest.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListOutputTest.java
@@ -100,7 +100,7 @@
     testForR8(Backend.DEX)
         .addProgramClasses(HelloWorldMain.class)
         .noTreeShaking()
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(AndroidApiLevel.K)
         .addMainDexRuleFiles(mainDexRules)
         .setMainDexListConsumer(new FileConsumer(mainDexListOutput))
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
index f375004..8f1833a 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
@@ -393,7 +393,7 @@
         .apply(configuration)
         .assumeAllMethodsMayHaveSideEffects()
         .setMinApi(minSdk)
-        .noMinification()
+        .addDontObfuscate()
         .noTreeShaking()
         .addDontOptimize()
         .setMainDexListConsumer(ToolHelper.consumeString(r8MainDexListOutput::set))
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexWithSynthesizedClassesTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexWithSynthesizedClassesTest.java
index 16dc8db..84d8eca 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexWithSynthesizedClassesTest.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexWithSynthesizedClassesTest.java
@@ -174,7 +174,7 @@
             .addOptionsModification(o -> o.minimalMainDex = true)
             .addMainDexListClasses(TestClass.class)
             .addMainDexListFiles(mainDexFile)
-            .noMinification()
+            .addDontObfuscate()
             .noTreeShaking()
             .allowDiagnosticWarningMessages()
             .compileWithExpectedDiagnostics(
diff --git a/src/test/java/com/android/tools/r8/maindexlist/checkdiscard/MainDexListCheckDiscard.java b/src/test/java/com/android/tools/r8/maindexlist/checkdiscard/MainDexListCheckDiscard.java
index 125c364..3f1fb69 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/checkdiscard/MainDexListCheckDiscard.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/checkdiscard/MainDexListCheckDiscard.java
@@ -55,7 +55,7 @@
         .addMainDexRules(keepMainProguardConfiguration(HelloWorldMain.class))
         .addMainDexRules(checkDiscardRule)
         .noTreeShaking()
-        .noMinification()
+        .addDontObfuscate()
         .compile();
   }
 
diff --git a/src/test/java/com/android/tools/r8/maindexlist/whyareyoukeeping/MainDexListWhyAreYouKeeping.java b/src/test/java/com/android/tools/r8/maindexlist/whyareyoukeeping/MainDexListWhyAreYouKeeping.java
index c4a616c..0aafb8d 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/whyareyoukeeping/MainDexListWhyAreYouKeeping.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/whyareyoukeeping/MainDexListWhyAreYouKeeping.java
@@ -86,7 +86,7 @@
     R8FullTestBuilder builder =
         testForR8(Backend.DEX)
             .noTreeShaking()
-            .noMinification()
+            .addDontObfuscate()
             .setMinApi(AndroidApiLevel.K)
             .addProgramClasses(CLASSES)
             .addMainDexRules(keepMainProguardConfiguration(HelloWorldMain.class))
diff --git a/src/test/java/com/android/tools/r8/mappingcompose/ComposeAlphaRenamingTest.java b/src/test/java/com/android/tools/r8/mappingcompose/ComposeAlphaRenamingTest.java
new file mode 100644
index 0000000..a3f010d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/mappingcompose/ComposeAlphaRenamingTest.java
@@ -0,0 +1,55 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.mappingcompose;
+
+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.naming.ClassNameMapper;
+import com.android.tools.r8.naming.MappingComposer;
+import com.android.tools.r8.utils.StringUtils;
+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 ComposeAlphaRenamingTest extends TestBase {
+
+  @Parameter() public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  private static final String mapping =
+      StringUtils.unixLines(
+          "a -> b:",
+          "    int a -> b",
+          "    void a() -> b",
+          "b -> a:",
+          "    int b -> a",
+          "    void b() -> a");
+  private static final String mappingResult =
+      StringUtils.unixLines(
+          "a -> a:",
+          "    int a -> a",
+          "    void a() -> a",
+          "b -> b:",
+          "    int b -> b",
+          "    void b() -> b");
+
+  @Test
+  public void testCompose() throws Exception {
+    ClassNameMapper mappingForFoo = ClassNameMapper.mapperFromString(mapping);
+    ClassNameMapper mappingForBar = ClassNameMapper.mapperFromString(mapping);
+    String composed = MappingComposer.compose(mappingForFoo, mappingForBar);
+    assertEquals(mappingResult, composed);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/mappingcompose/ComposeChangeLineNumberTest.java b/src/test/java/com/android/tools/r8/mappingcompose/ComposeChangeLineNumberTest.java
new file mode 100644
index 0000000..b1ff980
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/mappingcompose/ComposeChangeLineNumberTest.java
@@ -0,0 +1,45 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.mappingcompose;
+
+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.naming.ClassNameMapper;
+import com.android.tools.r8.naming.MappingComposer;
+import com.android.tools.r8.utils.StringUtils;
+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 ComposeChangeLineNumberTest extends TestBase {
+
+  @Parameter() public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  private static final String mappingFoo =
+      StringUtils.unixLines("com.foo -> a:", "    1:1:void m():42:42 -> m");
+  private static final String mappingBar =
+      StringUtils.unixLines("a -> b:", "    2:2:void m():1:1 -> m");
+  private static final String mappingResult =
+      StringUtils.unixLines("com.foo -> b:", "    2:2:void m():42:42 -> m");
+
+  @Test
+  public void testCompose() throws Exception {
+    ClassNameMapper mappingForFoo = ClassNameMapper.mapperFromString(mappingFoo);
+    ClassNameMapper mappingForBar = ClassNameMapper.mapperFromString(mappingBar);
+    String composed = MappingComposer.compose(mappingForFoo, mappingForBar);
+    assertEquals(mappingResult, composed);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/mappingcompose/ComposeChangeMethodAndLineNumberTest.java b/src/test/java/com/android/tools/r8/mappingcompose/ComposeChangeMethodAndLineNumberTest.java
new file mode 100644
index 0000000..c0fe2b7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/mappingcompose/ComposeChangeMethodAndLineNumberTest.java
@@ -0,0 +1,45 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.mappingcompose;
+
+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.naming.ClassNameMapper;
+import com.android.tools.r8.naming.MappingComposer;
+import com.android.tools.r8.utils.StringUtils;
+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 ComposeChangeMethodAndLineNumberTest extends TestBase {
+
+  @Parameter() public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  private static final String mappingFoo =
+      StringUtils.unixLines("com.foo -> a:", "    1:1:void m1():42:42 -> m2");
+  private static final String mappingBar =
+      StringUtils.unixLines("a -> b:", "    2:2:void m2():1:1 -> m3");
+  private static final String mappingResult =
+      StringUtils.unixLines("com.foo -> b:", "    2:2:void m1():42:42 -> m3");
+
+  @Test
+  public void testCompose() throws Exception {
+    ClassNameMapper mappingForFoo = ClassNameMapper.mapperFromString(mappingFoo);
+    ClassNameMapper mappingForBar = ClassNameMapper.mapperFromString(mappingBar);
+    String composed = MappingComposer.compose(mappingForFoo, mappingForBar);
+    assertEquals(mappingResult, composed);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/mappingcompose/ComposeChangeMethodTest.java b/src/test/java/com/android/tools/r8/mappingcompose/ComposeChangeMethodTest.java
new file mode 100644
index 0000000..83026e6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/mappingcompose/ComposeChangeMethodTest.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.mappingcompose;
+
+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.naming.ClassNameMapper;
+import com.android.tools.r8.naming.MappingComposer;
+import com.android.tools.r8.utils.StringUtils;
+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 ComposeChangeMethodTest extends TestBase {
+
+  @Parameter() public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  private static final String mappingFoo =
+      StringUtils.unixLines("com.foo -> a:", "    void f1() -> f2");
+  private static final String mappingBar = StringUtils.unixLines("a -> b:", "    void f2() -> f3");
+  private static final String mappingResult =
+      StringUtils.unixLines("com.foo -> b:", "    void f1() -> f3");
+
+  @Test
+  public void testCompose() throws Exception {
+    ClassNameMapper mappingForFoo = ClassNameMapper.mapperFromString(mappingFoo);
+    ClassNameMapper mappingForBar = ClassNameMapper.mapperFromString(mappingBar);
+    String composed = MappingComposer.compose(mappingForFoo, mappingForBar);
+    assertEquals(mappingResult, composed);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/mappingcompose/ComposeDifferentMethodTest.java b/src/test/java/com/android/tools/r8/mappingcompose/ComposeDifferentMethodTest.java
new file mode 100644
index 0000000..a145c17
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/mappingcompose/ComposeDifferentMethodTest.java
@@ -0,0 +1,48 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.mappingcompose;
+
+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.naming.ClassNameMapper;
+import com.android.tools.r8.naming.MappingComposer;
+import com.android.tools.r8.utils.StringUtils;
+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 ComposeDifferentMethodTest extends TestBase {
+
+  @Parameter() public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  // Here we are testing that a shrinker could have changed the method signature. Since we have no
+  // right hand side mapping we have no other choice than to map the naming through. This would not
+  // be ok if we had overloads but any shrinker should put in line numbering for those.
+  private static final String mappingFoo =
+      StringUtils.unixLines("com.foo -> a:", "    void f1(int) -> f2");
+  private static final String mappingBar =
+      StringUtils.unixLines("a -> b:", "    int f2(boolean) -> f3");
+  private static final String mappingResult =
+      StringUtils.unixLines("com.foo -> b:", "    void f1(int) -> f3");
+
+  @Test
+  public void testCompose() throws Exception {
+    ClassNameMapper mappingForFoo = ClassNameMapper.mapperFromString(mappingFoo);
+    ClassNameMapper mappingForBar = ClassNameMapper.mapperFromString(mappingBar);
+    String composed = MappingComposer.compose(mappingForFoo, mappingForBar);
+    assertEquals(mappingResult, composed);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/mappingcompose/ComposeDifferentMethodWithLineNumberTest.java b/src/test/java/com/android/tools/r8/mappingcompose/ComposeDifferentMethodWithLineNumberTest.java
new file mode 100644
index 0000000..a9b4d44
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/mappingcompose/ComposeDifferentMethodWithLineNumberTest.java
@@ -0,0 +1,47 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.mappingcompose;
+
+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.naming.ClassNameMapper;
+import com.android.tools.r8.naming.MappingComposer;
+import com.android.tools.r8.utils.StringUtils;
+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 ComposeDifferentMethodWithLineNumberTest extends TestBase {
+
+  @Parameter() public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  private static final String mappingFoo =
+      StringUtils.unixLines(
+          "com.foo -> a:", "    1:1:int f1(boolean) -> f2", "    2:2:void f1(int) -> f2");
+  private static final String mappingBar =
+      StringUtils.unixLines("a -> b:", "    8:8:void f2(boolean):2:2 -> f3");
+  private static final String mappingResult =
+      StringUtils.unixLines(
+          "com.foo -> b:", "    1:1:int f1(boolean) -> f2", "    8:8:void f1(int) -> f3");
+
+  @Test
+  public void testCompose() throws Exception {
+    ClassNameMapper mappingForFoo = ClassNameMapper.mapperFromString(mappingFoo);
+    ClassNameMapper mappingForBar = ClassNameMapper.mapperFromString(mappingBar);
+    String composed = MappingComposer.compose(mappingForFoo, mappingForBar);
+    assertEquals(mappingResult, composed);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/mappingcompose/ComposeDistinctClassesTest.java b/src/test/java/com/android/tools/r8/mappingcompose/ComposeDistinctClassesTest.java
new file mode 100644
index 0000000..e226de7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/mappingcompose/ComposeDistinctClassesTest.java
@@ -0,0 +1,41 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.mappingcompose;
+
+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.naming.ClassNameMapper;
+import com.android.tools.r8.naming.MappingComposer;
+import com.android.tools.r8.utils.StringUtils;
+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 ComposeDistinctClassesTest extends TestBase {
+
+  @Parameter() public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  private static final String mappingFoo = StringUtils.unixLines("com.foo -> a:");
+  private static final String mappingBar = StringUtils.unixLines("com.bar -> b:");
+
+  @Test
+  public void testCompose() throws Exception {
+    ClassNameMapper mappingForFoo = ClassNameMapper.mapperFromString(mappingFoo);
+    ClassNameMapper mappingForBar = ClassNameMapper.mapperFromString(mappingBar);
+    String composed = MappingComposer.compose(mappingForFoo, mappingForBar);
+    assertEquals(mappingBar + mappingFoo, composed);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/mappingcompose/ComposeDuplicateClassMappingTest.java b/src/test/java/com/android/tools/r8/mappingcompose/ComposeDuplicateClassMappingTest.java
new file mode 100644
index 0000000..cd2c32d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/mappingcompose/ComposeDuplicateClassMappingTest.java
@@ -0,0 +1,48 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.mappingcompose;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.naming.MappingComposeException;
+import com.android.tools.r8.naming.MappingComposer;
+import com.android.tools.r8.utils.StringUtils;
+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 ComposeDuplicateClassMappingTest extends TestBase {
+
+  @Parameter() public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  private static final String mappingFoo = StringUtils.unixLines("com.foo -> b:");
+  private static final String mappingBar = StringUtils.unixLines("a -> b:");
+
+  @Test
+  public void testCompose() throws Exception {
+    ClassNameMapper mappingForFoo = ClassNameMapper.mapperFromString(mappingFoo);
+    ClassNameMapper mappingForBar = ClassNameMapper.mapperFromString(mappingBar);
+    MappingComposeException mappingComposeException =
+        assertThrows(
+            MappingComposeException.class,
+            () -> MappingComposer.compose(mappingForFoo, mappingForBar));
+    assertEquals(
+        "Duplicate class mapping. Both 'com.foo' and 'a' maps to 'b'.",
+        mappingComposeException.getMessage());
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/mappingcompose/ComposeFieldNameTest.java b/src/test/java/com/android/tools/r8/mappingcompose/ComposeFieldNameTest.java
new file mode 100644
index 0000000..0834ed7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/mappingcompose/ComposeFieldNameTest.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.mappingcompose;
+
+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.naming.ClassNameMapper;
+import com.android.tools.r8.naming.MappingComposer;
+import com.android.tools.r8.utils.StringUtils;
+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 ComposeFieldNameTest extends TestBase {
+
+  @Parameter() public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  private static final String mappingFoo =
+      StringUtils.unixLines("com.foo -> a:", "    int f1 -> f2");
+  private static final String mappingBar = StringUtils.unixLines("a -> b:", "    int f2 -> f3");
+  private static final String mappingResult =
+      StringUtils.unixLines("com.foo -> b:", "    int f1 -> f3");
+
+  @Test
+  public void testCompose() throws Exception {
+    ClassNameMapper mappingForFoo = ClassNameMapper.mapperFromString(mappingFoo);
+    ClassNameMapper mappingForBar = ClassNameMapper.mapperFromString(mappingBar);
+    String composed = MappingComposer.compose(mappingForFoo, mappingForBar);
+    assertEquals(mappingResult, composed);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/mappingcompose/ComposeFieldWithAmbiguousTypeTest.java b/src/test/java/com/android/tools/r8/mappingcompose/ComposeFieldWithAmbiguousTypeTest.java
new file mode 100644
index 0000000..d4fedec
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/mappingcompose/ComposeFieldWithAmbiguousTypeTest.java
@@ -0,0 +1,49 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.mappingcompose;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.naming.MappingComposeException;
+import com.android.tools.r8.naming.MappingComposer;
+import com.android.tools.r8.utils.StringUtils;
+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 ComposeFieldWithAmbiguousTypeTest extends TestBase {
+
+  @Parameter() public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  private static final String mappingFoo =
+      StringUtils.unixLines("com.foo -> a:", "    int f1 -> f2");
+  private static final String mappingBar = StringUtils.unixLines("a -> b:", "    bool f2 -> f3");
+
+  @Test
+  public void testCompose() throws Exception {
+    ClassNameMapper mappingForFoo = ClassNameMapper.mapperFromString(mappingFoo);
+    ClassNameMapper mappingForBar = ClassNameMapper.mapperFromString(mappingBar);
+    MappingComposeException mappingComposeException =
+        assertThrows(
+            MappingComposeException.class,
+            () -> MappingComposer.compose(mappingForFoo, mappingForBar));
+    assertEquals(
+        "Unable to compose field naming 'bool f2 -> f3' since the original type has changed.",
+        mappingComposeException.getMessage());
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/mappingcompose/ComposeHelpers.java b/src/test/java/com/android/tools/r8/mappingcompose/ComposeHelpers.java
new file mode 100644
index 0000000..61656a3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/mappingcompose/ComposeHelpers.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.mappingcompose;
+
+public class ComposeHelpers {
+
+  public static String doubleToSingleQuote(String str) {
+    return str.replace("\"", "'");
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/mappingcompose/ComposeInlineFollowedByLineNumberChangeTest.java b/src/test/java/com/android/tools/r8/mappingcompose/ComposeInlineFollowedByLineNumberChangeTest.java
new file mode 100644
index 0000000..79bae0b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/mappingcompose/ComposeInlineFollowedByLineNumberChangeTest.java
@@ -0,0 +1,53 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.mappingcompose;
+
+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.naming.ClassNameMapper;
+import com.android.tools.r8.naming.MappingComposer;
+import com.android.tools.r8.utils.StringUtils;
+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 ComposeInlineFollowedByLineNumberChangeTest extends TestBase {
+
+  @Parameter() public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  private static final String mappingFoo =
+      StringUtils.unixLines(
+          "com.foo -> a:",
+          "    1:1:void inlinee2():42:42 -> x",
+          "    1:1:void foo.bar.baz.inlinee1():41 -> x",
+          "    1:1:void caller():40 -> x");
+  private static final String mappingBar =
+      StringUtils.unixLines("a -> b:", "    2:2:void x():1:1 -> y");
+  private static final String mappingResult =
+      StringUtils.unixLines(
+          "com.foo -> b:",
+          "    2:2:void inlinee2():42:42 -> y",
+          "    2:2:void foo.bar.baz.inlinee1():41 -> y",
+          "    2:2:void caller():40 -> y");
+
+  @Test
+  public void testCompose() throws Exception {
+    ClassNameMapper mappingForFoo = ClassNameMapper.mapperFromString(mappingFoo);
+    ClassNameMapper mappingForBar = ClassNameMapper.mapperFromString(mappingBar);
+    String composed = MappingComposer.compose(mappingForFoo, mappingForBar);
+    assertEquals(mappingResult, composed);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/mappingcompose/ComposeMapVersionTest.java b/src/test/java/com/android/tools/r8/mappingcompose/ComposeMapVersionTest.java
new file mode 100644
index 0000000..bbc6c01
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/mappingcompose/ComposeMapVersionTest.java
@@ -0,0 +1,48 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.mappingcompose;
+
+import static com.android.tools.r8.mappingcompose.ComposeHelpers.doubleToSingleQuote;
+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.naming.ClassNameMapper;
+import com.android.tools.r8.naming.MappingComposer;
+import com.android.tools.r8.utils.StringUtils;
+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 ComposeMapVersionTest extends TestBase {
+
+  @Parameter() public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  private static final String mappingFoo =
+      StringUtils.unixLines(
+          "# { id: 'com.android.tools.r8.mapping', version: '1.0' }", "com.foo -> a:");
+  private static final String mappingBar =
+      StringUtils.unixLines("# { id: 'com.android.tools.r8.mapping', version: '2.0' }", "a -> b:");
+  private static final String mappingResult =
+      StringUtils.unixLines(
+          "# {'id':'com.android.tools.r8.mapping','version':'2.0'}", "com.foo -> b:");
+
+  @Test
+  public void testCompose() throws Exception {
+    ClassNameMapper mappingForFoo = ClassNameMapper.mapperFromString(mappingFoo);
+    ClassNameMapper mappingForBar = ClassNameMapper.mapperFromString(mappingBar);
+    String composed = MappingComposer.compose(mappingForFoo, mappingForBar);
+    assertEquals(mappingResult, doubleToSingleQuote(composed));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/mappingcompose/ComposeOutlineTest.java b/src/test/java/com/android/tools/r8/mappingcompose/ComposeOutlineTest.java
new file mode 100644
index 0000000..98f1cd5
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/mappingcompose/ComposeOutlineTest.java
@@ -0,0 +1,74 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.mappingcompose;
+
+import static com.android.tools.r8.mappingcompose.ComposeHelpers.doubleToSingleQuote;
+import static org.junit.Assert.assertNotEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.naming.MappingComposer;
+import com.android.tools.r8.utils.StringUtils;
+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 ComposeOutlineTest extends TestBase {
+
+  @Parameter() public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  private static final String mappingFoo =
+      StringUtils.unixLines(
+          "# { id: 'com.android.tools.r8.mapping', version: '2.0' }",
+          "outline.Class -> a:",
+          "    1:2:int some.inlinee():75:76 -> a",
+          "    1:2:int outline():0 -> a",
+          "    # { 'id':'com.android.tools.r8.outline' }",
+          "outline.Callsite -> x:",
+          "    4:4:int outlineCaller(int):23 -> s",
+          "    5:5:int foo.bar.baz.outlineCaller(int):98:98 -> s",
+          "    5:5:int outlineCaller(int):24 -> s",
+          "    27:27:int outlineCaller(int):0:0 -> s",
+          "    # { 'id':'com.android.tools.r8.outlineCallsite', 'positions': { '1': 4, '2': 5 } }");
+  private static final String mappingBar =
+      StringUtils.unixLines(
+          "a -> b:",
+          "    4:5:int a():1:2 -> m",
+          "x -> c:",
+          "    8:9:int s(int):4:5 -> o",
+          "    42:42:int s(int):27:27 -> o");
+  private static final String mappingResult =
+      StringUtils.unixLines(
+          "# {'id':'com.android.tools.r8.mapping','version':'2.0'}",
+          "outline.Callsite -> c:",
+          "    8:8:int outlineCaller(int):23 -> o",
+          "    9:9:int foo.bar.baz.outlineCaller(int):98:98 -> o",
+          "    9:9:int outlineCaller(int):24 -> o",
+          "    42:42:int outlineCaller(int):0:0 -> o",
+          "    # { 'id':'com.android.tools.r8.outlineCallsite', 'positions': { '4': 8, '5': 9 } }",
+          "outline.Class -> b:",
+          "    4:5:int some.inlinee():75:76 -> m",
+          "    4:5:int outline():0 -> m",
+          "    # {'id':'com.android.tools.r8.outline'}");
+
+  @Test
+  public void testCompose() throws Exception {
+    ClassNameMapper mappingForFoo = ClassNameMapper.mapperFromString(mappingFoo);
+    ClassNameMapper mappingForBar = ClassNameMapper.mapperFromString(mappingBar);
+    String composed = MappingComposer.compose(mappingForFoo, mappingForBar);
+    // TODO(b/242682464): Update this test when the link has been added to the mapping information.
+    assertNotEquals(mappingResult, doubleToSingleQuote(composed));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/mappingcompose/ComposeRewriteFrameTest.java b/src/test/java/com/android/tools/r8/mappingcompose/ComposeRewriteFrameTest.java
new file mode 100644
index 0000000..cae1f89
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/mappingcompose/ComposeRewriteFrameTest.java
@@ -0,0 +1,64 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.mappingcompose;
+
+import static com.android.tools.r8.mappingcompose.ComposeHelpers.doubleToSingleQuote;
+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.naming.ClassNameMapper;
+import com.android.tools.r8.naming.MappingComposer;
+import com.android.tools.r8.utils.StringUtils;
+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 ComposeRewriteFrameTest extends TestBase {
+
+  @Parameter() public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  private static final String mappingFoo =
+      StringUtils.unixLines(
+          "# { id: 'com.android.tools.r8.mapping', version: '2.0' }",
+          "my.CustomException -> a:",
+          "foo.Bar -> x:",
+          "    4:4:void other.Class.inlinee():23:23 -> a",
+          "    4:4:void caller(other.Class):7 -> a",
+          "    # { id: 'com.android.tools.r8.rewriteFrame', "
+              + "conditions: ['throws(La;)'], actions: ['removeInnerFrames(1)'] }");
+  private static final String mappingBar =
+      StringUtils.unixLines(
+          "# { id: 'com.android.tools.r8.mapping', version: '2.0' }",
+          "a -> b:",
+          "x -> c:",
+          "    8:8:void a(Other.Class):4:4 -> m");
+  private static final String mappingResult =
+      StringUtils.unixLines(
+          "# {'id':'com.android.tools.r8.mapping','version':'2.0'}",
+          "foo.Bar -> c:",
+          "    8:8:void other.Class.inlinee():23:23 -> m",
+          "    8:8:void caller(other.Class):7 -> m",
+          "    # {'id':'com.android.tools.r8.rewriteFrame','conditions':['throws(Lb;)'],"
+              + "'actions':['removeInnerFrames(1)']}",
+          "my.CustomException -> b:");
+
+  @Test
+  public void testCompose() throws Exception {
+    ClassNameMapper mappingForFoo = ClassNameMapper.mapperFromString(mappingFoo);
+    ClassNameMapper mappingForBar = ClassNameMapper.mapperFromString(mappingBar);
+    String composed = MappingComposer.compose(mappingForFoo, mappingForBar);
+    assertEquals(mappingResult, doubleToSingleQuote(composed));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/mappingcompose/ComposeSourceFileTest.java b/src/test/java/com/android/tools/r8/mappingcompose/ComposeSourceFileTest.java
new file mode 100644
index 0000000..9ec3ec6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/mappingcompose/ComposeSourceFileTest.java
@@ -0,0 +1,51 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.mappingcompose;
+
+import static com.android.tools.r8.mappingcompose.ComposeHelpers.doubleToSingleQuote;
+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.naming.ClassNameMapper;
+import com.android.tools.r8.naming.MappingComposer;
+import com.android.tools.r8.utils.StringUtils;
+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 ComposeSourceFileTest extends TestBase {
+
+  @Parameter() public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  private static final String mappingFoo =
+      StringUtils.unixLines("com.foo -> a:", "    # {'id':'sourceFile','fileName':'Foo.kt'}");
+  private static final String mappingBar =
+      StringUtils.unixLines(
+          "com.bar -> c:", "    # {'id':'sourceFile','fileName':'Bar.kt'}", "a -> b:");
+  private static final String mappingResult =
+      StringUtils.unixLines(
+          "com.bar -> c:",
+          "# {'id':'sourceFile','fileName':'Bar.kt'}",
+          "com.foo -> b:",
+          "# {'id':'sourceFile','fileName':'Foo.kt'}");
+
+  @Test
+  public void testCompose() throws Exception {
+    ClassNameMapper mappingForFoo = ClassNameMapper.mapperFromString(mappingFoo);
+    ClassNameMapper mappingForBar = ClassNameMapper.mapperFromString(mappingBar);
+    String composed = MappingComposer.compose(mappingForFoo, mappingForBar);
+    assertEquals(mappingResult, doubleToSingleQuote(composed));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/mappingcompose/ComposeSplitRangeStartTest.java b/src/test/java/com/android/tools/r8/mappingcompose/ComposeSplitRangeStartTest.java
new file mode 100644
index 0000000..82cb66d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/mappingcompose/ComposeSplitRangeStartTest.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.mappingcompose;
+
+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.naming.ClassNameMapper;
+import com.android.tools.r8.naming.MappingComposer;
+import com.android.tools.r8.utils.StringUtils;
+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 ComposeSplitRangeStartTest extends TestBase {
+
+  @Parameter() public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  private static final String mappingFoo =
+      StringUtils.unixLines(
+          "com.foo -> a:", "    1:5:void m():41:45 -> x", "    6:10:void m():56:60 -> x");
+  private static final String mappingBar =
+      StringUtils.unixLines(
+          "a -> b:",
+          "    11:12:void x():1:2 -> y",
+          "    13:17:void x():3:7 -> y",
+          "    18:24:void x():8:8 -> y",
+          "    25:26:void x():9:10 -> y");
+  private static final String mappingResult =
+      StringUtils.unixLines(
+          "com.foo -> b:",
+          "    11:12:void m():41:42 -> y",
+          "    13:15:void m():43:45 -> y",
+          "    16:17:void m():56:57 -> y",
+          "    18:24:void m():58:58 -> y",
+          "    25:26:void m():59:60 -> y");
+
+  @Test
+  public void testCompose() throws Exception {
+    ClassNameMapper mappingForFoo = ClassNameMapper.mapperFromString(mappingFoo);
+    ClassNameMapper mappingForBar = ClassNameMapper.mapperFromString(mappingBar);
+    String composed = MappingComposer.compose(mappingForFoo, mappingForBar);
+    assertEquals(mappingResult, composed);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/mappingcompose/ComposeSyntheticTest.java b/src/test/java/com/android/tools/r8/mappingcompose/ComposeSyntheticTest.java
new file mode 100644
index 0000000..20585e9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/mappingcompose/ComposeSyntheticTest.java
@@ -0,0 +1,71 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.mappingcompose;
+
+import static com.android.tools.r8.mappingcompose.ComposeHelpers.doubleToSingleQuote;
+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.naming.ClassNameMapper;
+import com.android.tools.r8.naming.MappingComposer;
+import com.android.tools.r8.utils.StringUtils;
+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 ComposeSyntheticTest extends TestBase {
+
+  @Parameter() public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  private static final String mappingFoo =
+      StringUtils.unixLines(
+          "# { id: 'com.android.tools.r8.mapping', version: '1.0' }",
+          "com.foo -> a:",
+          "# { id: 'com.android.tools.r8.synthesized' }",
+          "    int f -> a",
+          "    # { id: 'com.android.tools.r8.synthesized' }",
+          "    void m() -> b",
+          "    # { id: 'com.android.tools.r8.synthesized' }");
+  private static final String mappingBar =
+      StringUtils.unixLines(
+          "# { id: 'com.android.tools.r8.mapping', version: '1.0' }",
+          "a -> b:",
+          "    int a -> b",
+          "com.bar -> c:",
+          "# { id: 'com.android.tools.r8.synthesized' }",
+          "    void bar() -> a",
+          "    # { id: 'com.android.tools.r8.synthesized' }");
+  private static final String mappingResult =
+      StringUtils.unixLines(
+          "# {'id':'com.android.tools.r8.mapping','version':'1.0'}",
+          "com.bar -> c:",
+          "# {'id':'com.android.tools.r8.synthesized'}",
+          "    void bar() -> a",
+          "    # {'id':'com.android.tools.r8.synthesized'}",
+          "com.foo -> b:",
+          "# {'id':'com.android.tools.r8.synthesized'}",
+          "    int f -> b",
+          // TODO(b/242673239): When fixed, we should emit synthetized info here as well.
+          "    void m() -> b",
+          "    # {'id':'com.android.tools.r8.synthesized'}");
+
+  @Test
+  public void testCompose() throws Exception {
+    ClassNameMapper mappingForFoo = ClassNameMapper.mapperFromString(mappingFoo);
+    ClassNameMapper mappingForBar = ClassNameMapper.mapperFromString(mappingBar);
+    String composed = MappingComposer.compose(mappingForFoo, mappingForBar);
+    assertEquals(mappingResult, doubleToSingleQuote(composed));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveInterfaceBridgeWithSubTypeTest.java b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveInterfaceBridgeWithSubTypeTest.java
index 25674a2..4551dbe 100644
--- a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveInterfaceBridgeWithSubTypeTest.java
+++ b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveInterfaceBridgeWithSubTypeTest.java
@@ -56,7 +56,7 @@
         .enableNoVerticalClassMergingAnnotations()
         .addHorizontallyMergedClassesInspector(
             HorizontallyMergedClassesInspector::assertNoClassesMerged)
-        .noMinification()
+        .addDontObfuscate()
         .run(parameters.getRuntime(), newMainTypeName)
         .assertSuccessWithOutputLines("B::foo")
         .inspect(
diff --git a/src/test/java/com/android/tools/r8/naming/ClassNameMinifierOriginalClassNameTest.java b/src/test/java/com/android/tools/r8/naming/ClassNameMinifierOriginalClassNameTest.java
index 779eb8a..bd76317 100644
--- a/src/test/java/com/android/tools/r8/naming/ClassNameMinifierOriginalClassNameTest.java
+++ b/src/test/java/com/android/tools/r8/naming/ClassNameMinifierOriginalClassNameTest.java
@@ -74,7 +74,7 @@
         .addProgramClasses(Main.class)
         .setMinApi(parameters.getApiLevel())
         .addKeepMainRule(Main.class)
-        .noMinification()
+        .addDontObfuscate()
         .addClasspathClasses(A.class, B.class)
         .addApplyMapping(libraryCompileResult.getProguardMap())
         .compile()
@@ -94,7 +94,7 @@
                 .addProgramClasses(MainWithReferenceToNotMapped.class)
                 .setMinApi(parameters.getApiLevel())
                 .addKeepMainRule(MainWithReferenceToNotMapped.class)
-                .noMinification()
+                .addDontObfuscate()
                 .addClasspathClasses(A.class, B.class)
                 .addApplyMapping(libraryCompileResult.getProguardMap())
                 .allowDiagnosticWarningMessages()
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterDevirtualizationTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterDevirtualizationTest.java
index 5b13f6b..efb32a2 100644
--- a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterDevirtualizationTest.java
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterDevirtualizationTest.java
@@ -127,7 +127,7 @@
 
     testForR8(parameters.getBackend())
         .noTreeShaking()
-        .noMinification()
+        .addDontObfuscate()
         .addProgramClasses(PROGRAM_CLASSES)
         .addApplyMapping(libraryResult.getProguardMap())
         .addClasspathClasses(CLASSPATH_CLASSES)
@@ -160,7 +160,7 @@
 
     testForR8(parameters.getBackend())
         .noTreeShaking()
-        .noMinification()
+        .addDontObfuscate()
         .addProgramClasses(PROGRAM_CLASSES)
         .addApplyMapping(libraryResult.getProguardMap())
         .addClasspathClasses(CLASSPATH_CLASSES)
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterHorizontalMergingFieldTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterHorizontalMergingFieldTest.java
index 98ead74..3311270 100644
--- a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterHorizontalMergingFieldTest.java
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterHorizontalMergingFieldTest.java
@@ -104,7 +104,7 @@
 
     testForR8(parameters.getBackend())
         .noTreeShaking()
-        .noMinification()
+        .addDontObfuscate()
         .addProgramClasses(PROGRAM_CLASSES)
         .addApplyMapping(libraryResult.getProguardMap())
         .addLibraryClasses(LIBRARY_CLASSES)
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterHorizontalMergingMethodTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterHorizontalMergingMethodTest.java
index e101caa..ac2e989 100644
--- a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterHorizontalMergingMethodTest.java
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterHorizontalMergingMethodTest.java
@@ -110,7 +110,7 @@
 
     testForR8(backend)
         .noTreeShaking()
-        .noMinification()
+        .addDontObfuscate()
         .addProgramClasses(PROGRAM_CLASSES)
         .addApplyMapping(libraryResult.getProguardMap())
         .addLibraryClasses(LIBRARY_CLASSES)
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterVerticalMergingFieldTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterVerticalMergingFieldTest.java
index a7a51fd..0fe35cb 100644
--- a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterVerticalMergingFieldTest.java
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterVerticalMergingFieldTest.java
@@ -90,7 +90,7 @@
 
     testForR8(backend)
         .noTreeShaking()
-        .noMinification()
+        .addDontObfuscate()
         .addProgramClasses(PROGRAM_CLASSES)
         .addApplyMapping(libraryResult.getProguardMap())
         .addLibraryClasses(LIBRARY_CLASSES)
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterVerticalMergingMethodTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterVerticalMergingMethodTest.java
index 198fcde..609465f 100644
--- a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterVerticalMergingMethodTest.java
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterVerticalMergingMethodTest.java
@@ -135,7 +135,7 @@
       throws CompilationFailedException {
     return testForR8(staticTemp, backend)
         .noTreeShaking()
-        .noMinification()
+        .addDontObfuscate()
         .addProgramClasses(PROGRAM_CLASSES)
         .addApplyMapping(proguardMap)
         .addLibraryClasses(LIBRARY_CLASSES)
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingInterfaceInvokeTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingInterfaceInvokeTest.java
index b4b6807..8c7e804 100644
--- a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingInterfaceInvokeTest.java
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingInterfaceInvokeTest.java
@@ -73,7 +73,7 @@
     testForR8(parameters.getBackend())
         .addClasspathClasses(classPathClasses)
         .addProgramClasses(TestApp.class)
-        .noMinification()
+        .addDontObfuscate()
         .noTreeShaking()
         .addApplyMapping(libraryResult.getProguardMap())
         .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingVirtualInvokeTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingVirtualInvokeTest.java
index 84a7f62..ff9f9b4 100644
--- a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingVirtualInvokeTest.java
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingVirtualInvokeTest.java
@@ -157,7 +157,7 @@
     libraryCompileResult.writeToZip(outPath);
     testForR8(parameters.getBackend())
         .noTreeShaking()
-        .noMinification()
+        .addDontObfuscate()
         .addProgramClasses(PROGRAM_CLASSES)
         .addApplyMapping(libraryCompileResult.getProguardMap())
         .addClasspathClasses(LIBRARY_CLASSES)
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/shared/NameClashTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/shared/NameClashTest.java
index 3466e21..5d1c231 100644
--- a/src/test/java/com/android/tools/r8/naming/applymapping/shared/NameClashTest.java
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/shared/NameClashTest.java
@@ -139,7 +139,7 @@
         .addKeepMainRule(MAIN)
         .addKeepRules("-applymapping " + mappingFile)
         .noTreeShaking()
-        .noMinification()
+        .addDontObfuscate()
         .compile()
         .run(MAIN)
         .assertSuccessWithOutput(EXPECTED_OUTPUT);
diff --git a/src/test/java/com/android/tools/r8/naming/arraytypes/ArrayTypesTest.java b/src/test/java/com/android/tools/r8/naming/arraytypes/ArrayTypesTest.java
index 8100e16..031571e 100644
--- a/src/test/java/com/android/tools/r8/naming/arraytypes/ArrayTypesTest.java
+++ b/src/test/java/com/android/tools/r8/naming/arraytypes/ArrayTypesTest.java
@@ -102,7 +102,7 @@
         .addProgramClassFileData(generateTestClass())
         .addKeepMainRule(Main.class)
         .addKeepRules("-applymapping " + mappingFile.toAbsolutePath())
-        .noMinification()
+        .addDontObfuscate()
         .noTreeShaking()
         .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), Main.class)
diff --git a/src/test/java/com/android/tools/r8/naming/signature/SignatureOfMergedClassesTest.java b/src/test/java/com/android/tools/r8/naming/signature/SignatureOfMergedClassesTest.java
index 7511e28..5f58326 100644
--- a/src/test/java/com/android/tools/r8/naming/signature/SignatureOfMergedClassesTest.java
+++ b/src/test/java/com/android/tools/r8/naming/signature/SignatureOfMergedClassesTest.java
@@ -61,7 +61,7 @@
         .addKeepClassRules(InterfaceToKeep.class, ImplI.class, K.class)
         .addKeepAttributes("Signature, InnerClasses, EnclosingMethod, *Annotation*")
         .setMinApi(parameters.getApiLevel())
-        .noMinification()
+        .addDontObfuscate()
         .addOptionsModification(
             internalOptions -> {
               internalOptions.enableUnusedInterfaceRemoval = false;
@@ -92,7 +92,7 @@
         .addKeepClassRules(InterfaceToKeep.class)
         .addKeepAttributes("Signature, InnerClasses, EnclosingMethod, *Annotation*")
         .setMinApi(parameters.getApiLevel())
-        .noMinification()
+        .addDontObfuscate()
         .addOptionsModification(
             internalOptions -> {
               internalOptions.enableUnusedInterfaceRemoval = false;
diff --git a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/CheckNotZeroMethodWithArgumentRemovalTest.java b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/CheckNotZeroMethodWithArgumentRemovalTest.java
index 3685b5c..20b82f0 100644
--- a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/CheckNotZeroMethodWithArgumentRemovalTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/CheckNotZeroMethodWithArgumentRemovalTest.java
@@ -39,7 +39,7 @@
         .addEnumUnboxingInspector(inspector -> inspector.assertUnboxed(MyEnum.class))
         .enableInliningAnnotations()
         // TODO(b/173398086): uniqueMethodWithName() does not work with argument removal.
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(
diff --git a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/ConsistentMethodRenamingWithArgumentRemovalAndStaticizingTest.java b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/ConsistentMethodRenamingWithArgumentRemovalAndStaticizingTest.java
new file mode 100644
index 0000000..e8daac4
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/ConsistentMethodRenamingWithArgumentRemovalAndStaticizingTest.java
@@ -0,0 +1,78 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.optimize.argumentpropagation;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import 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;
+
+/** Reproduction of b/241426917. */
+@RunWith(Parameterized.class)
+public class ConsistentMethodRenamingWithArgumentRemovalAndStaticizingTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(Main.class)
+        .enableInliningAnnotations()
+        .enableNeverClassInliningAnnotations()
+        .enableNoHorizontalClassMergingAnnotations()
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("A.m()", "B.m()");
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      String used = System.currentTimeMillis() > 0 ? "A.m()" : null;
+      new A().m(used);
+
+      String unused = null;
+      B.m(unused);
+    }
+  }
+
+  // To force A and B into the same strongly connected component.
+  static class Parent {}
+
+  @NeverClassInline
+  @NoHorizontalClassMerging
+  static class A extends Parent {
+
+    // Will have its receiver argument removed (i.e., the method is staticized).
+    @NeverInline
+    void m(String used) {
+      System.out.println(used);
+    }
+  }
+
+  @NoHorizontalClassMerging
+  static class B extends Parent {
+
+    // Will have its first argument removed.
+    @NeverInline
+    static void m(String unused) {
+      System.out.println("B.m()");
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/ConstantReturnAfterArgumentPropagationTest.java b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/ConstantReturnAfterArgumentPropagationTest.java
index ba07988..3e410db 100644
--- a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/ConstantReturnAfterArgumentPropagationTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/ConstantReturnAfterArgumentPropagationTest.java
@@ -38,7 +38,7 @@
         .addKeepMainRule(Main.class)
         .enableInliningAnnotations()
         // TODO(b/173398086): uniqueMethodWithName() does not work with argument removal.
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(
diff --git a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/ConstantUnboxedEnumArgumentTest.java b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/ConstantUnboxedEnumArgumentTest.java
index 7c4f463..562c3b5 100644
--- a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/ConstantUnboxedEnumArgumentTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/ConstantUnboxedEnumArgumentTest.java
@@ -41,7 +41,7 @@
         .addEnumUnboxingInspector(inspector -> inspector.assertUnboxed(MyEnum.class))
         .enableInliningAnnotations()
         // TODO(b/173398086): uniqueMethodWithName() does not work with argument removal.
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(
diff --git a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/EffectivelyFinalCompanionMethodsTest.java b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/EffectivelyFinalCompanionMethodsTest.java
index 67af262..86ab638 100644
--- a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/EffectivelyFinalCompanionMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/EffectivelyFinalCompanionMethodsTest.java
@@ -42,7 +42,7 @@
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .enableNoHorizontalClassMergingAnnotations()
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(
diff --git a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/MixedArgumentRemovalAndEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/MixedArgumentRemovalAndEnumUnboxingTest.java
index 71d2f1c..65c3ce8 100644
--- a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/MixedArgumentRemovalAndEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/MixedArgumentRemovalAndEnumUnboxingTest.java
@@ -41,7 +41,7 @@
         .addEnumUnboxingInspector(inspector -> inspector.assertUnboxed(MyEnum.class))
         .enableInliningAnnotations()
         // TODO(b/173398086): uniqueMethodWithName() does not work with argument removal.
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(
diff --git a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/MonomorphicMethodWithUnusedReturnValueTest.java b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/MonomorphicMethodWithUnusedReturnValueTest.java
index d7130ef..0df2eff 100644
--- a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/MonomorphicMethodWithUnusedReturnValueTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/MonomorphicMethodWithUnusedReturnValueTest.java
@@ -38,7 +38,7 @@
         .addKeepMainRule(Main.class)
         .enableInliningAnnotations()
         // TODO(b/173398086): uniqueMethodWithName() does not work with proto changes.
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(
diff --git a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/ParameterTypeStrengtheningTest.java b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/ParameterTypeStrengtheningTest.java
index 91abde9..42d8535 100644
--- a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/ParameterTypeStrengtheningTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/ParameterTypeStrengtheningTest.java
@@ -42,7 +42,7 @@
         .enableNeverClassInliningAnnotations()
         .enableNoHorizontalClassMergingAnnotations()
         // TODO(b/173398086): uniqueMethodWithName() does not work with argument changes.
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(
diff --git a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/PolymorphicMethodWithUnusedReturnValueTest.java b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/PolymorphicMethodWithUnusedReturnValueTest.java
index e8007c3..fcf44bb 100644
--- a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/PolymorphicMethodWithUnusedReturnValueTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/PolymorphicMethodWithUnusedReturnValueTest.java
@@ -40,7 +40,7 @@
         .enableNoHorizontalClassMergingAnnotations()
         .enableNoVerticalClassMergingAnnotations()
         // TODO(b/173398086): uniqueMethodWithName() does not work with proto changes.
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(
diff --git a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/ReturnTypeStrengtheningTest.java b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/ReturnTypeStrengtheningTest.java
index 89dccdb..b1faa74 100644
--- a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/ReturnTypeStrengtheningTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/ReturnTypeStrengtheningTest.java
@@ -44,7 +44,7 @@
         .enableInliningAnnotations()
         .enableNoVerticalClassMergingAnnotations()
         // TODO(b/173398086): uniqueMethodWithName() does not work with argument changes.
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(
diff --git a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/StaticMethodWithConstantArgumentTest.java b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/StaticMethodWithConstantArgumentTest.java
index 9b1429f..cee9ab5 100644
--- a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/StaticMethodWithConstantArgumentTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/StaticMethodWithConstantArgumentTest.java
@@ -41,7 +41,7 @@
         .addKeepMainRule(Main.class)
         .enableInliningAnnotations()
         // TODO(b/173398086): uniqueMethodWithName() does not work with argument removal.
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(
diff --git a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/StaticMethodWithConstantArgumentThroughCallChainTest.java b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/StaticMethodWithConstantArgumentThroughCallChainTest.java
index e33fe2a..1764b30 100644
--- a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/StaticMethodWithConstantArgumentThroughCallChainTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/StaticMethodWithConstantArgumentThroughCallChainTest.java
@@ -41,7 +41,7 @@
         .addKeepMainRule(Main.class)
         .enableInliningAnnotations()
         // TODO(b/173398086): uniqueMethodWithName() does not work with argument removal.
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(
diff --git a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/UnboxedEnumUserInMethodWithConstantArgumentTest.java b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/UnboxedEnumUserInMethodWithConstantArgumentTest.java
index 9a991c2..dd93e33 100644
--- a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/UnboxedEnumUserInMethodWithConstantArgumentTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/UnboxedEnumUserInMethodWithConstantArgumentTest.java
@@ -41,7 +41,7 @@
         .addEnumUnboxingInspector(inspector -> inspector.assertUnboxed(MyEnum.class))
         .enableInliningAnnotations()
         // TODO(b/173398086): uniqueMethodWithName() does not work with argument removal.
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(
diff --git a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/UpwardsArgumentPropagationToResolvedMethodTest.java b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/UpwardsArgumentPropagationToResolvedMethodTest.java
index dd1d2d2..993ca6b 100644
--- a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/UpwardsArgumentPropagationToResolvedMethodTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/UpwardsArgumentPropagationToResolvedMethodTest.java
@@ -46,7 +46,7 @@
         .enableNeverClassInliningAnnotations()
         .enableNoHorizontalClassMergingAnnotations()
         // TODO(b/173398086): uniqueMethodWithName() does not work with argument removal.
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(
diff --git a/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithKeptMethodTest.java b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithKeptMethodTest.java
index 5f30d86..12d62c9 100644
--- a/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithKeptMethodTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithKeptMethodTest.java
@@ -41,7 +41,7 @@
         .enableInliningAnnotations()
         .enableNoHorizontalClassMergingAnnotations()
         // TODO(b/173398086): uniqueMethodWithName() does not work with proto changes.
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(
diff --git a/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithLibraryOverrideTest.java b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithLibraryOverrideTest.java
index 31a766e..1e6a4e5 100644
--- a/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithLibraryOverrideTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithLibraryOverrideTest.java
@@ -40,7 +40,7 @@
         .addKeepMainRule(Main.class)
         .enableInliningAnnotations()
         // TODO(b/173398086): uniqueMethodWithName() does not work with proto changes.
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(
diff --git a/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithParameterAnnotationsTest.java b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithParameterAnnotationsTest.java
index 37bfc73..e4507b5 100644
--- a/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithParameterAnnotationsTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithParameterAnnotationsTest.java
@@ -47,7 +47,7 @@
         .enableInliningAnnotations()
         .enableNoHorizontalClassMergingAnnotations()
         // TODO(b/173398086): uniqueMethodWithName() does not work with proto changes.
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(
diff --git a/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithVirtualMethodCollisionTest.java b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithVirtualMethodCollisionTest.java
index 0e63441..de0dfe3 100644
--- a/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithVirtualMethodCollisionTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithVirtualMethodCollisionTest.java
@@ -41,7 +41,7 @@
         .enableInliningAnnotations()
         .enableNoVerticalClassMergingAnnotations()
         // TODO(b/173398086): uniqueMethodWithName() does not work with proto changes.
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(
diff --git a/src/test/java/com/android/tools/r8/regress/Regress181837660.java b/src/test/java/com/android/tools/r8/regress/Regress181837660.java
index d1275c8..0ce8797 100644
--- a/src/test/java/com/android/tools/r8/regress/Regress181837660.java
+++ b/src/test/java/com/android/tools/r8/regress/Regress181837660.java
@@ -98,7 +98,7 @@
     testBuilder
         // Link against android.jar that contains ReflectiveOperationException.
         .addLibraryFiles(parameters.getDefaultAndroidJarAbove(AndroidApiLevel.K))
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel());
   }
 
diff --git a/src/test/java/com/android/tools/r8/regress/Regress241478253.java b/src/test/java/com/android/tools/r8/regress/Regress241478253.java
new file mode 100644
index 0000000..8a11afd
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/regress/Regress241478253.java
@@ -0,0 +1,65 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.regress;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class Regress241478253 extends TestBase {
+
+  static final String EXPECTED = "In the Bar";
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withDexRuntimes().withAllApiLevels().build();
+  }
+
+  public Regress241478253(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClasses(Foo.class, Bar.class)
+        .addKeepMainRule(Foo.class)
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), Foo.class)
+        .assertSuccessWithOutputLines(EXPECTED);
+    testForR8(parameters.getBackend())
+        .debug()
+        .addProgramClasses(Foo.class, Bar.class)
+        .addKeepMainRule(Foo.class)
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), Foo.class)
+        .assertSuccessWithOutputLines(EXPECTED);
+  }
+
+  public static class Foo {
+
+    @SuppressWarnings("unchecked")
+    public static void main(String[] args) {
+      try {
+        Class<Bar> impl =
+            (Class<Bar>) Class.forName("com.android.tools.r8.regress.Regress241478253$Bar");
+        impl.getDeclaredConstructor().newInstance();
+      } catch (Exception ignored) {
+        throw new RuntimeException(ignored);
+      }
+    }
+  }
+
+  public static class Bar {
+    public Bar() {
+      System.out.println("In the Bar");
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/regress/b151964517/ConstStringWithMonitorTest.java b/src/test/java/com/android/tools/r8/regress/b151964517/ConstStringWithMonitorTest.java
index c37953b..31fcbef 100644
--- a/src/test/java/com/android/tools/r8/regress/b151964517/ConstStringWithMonitorTest.java
+++ b/src/test/java/com/android/tools/r8/regress/b151964517/ConstStringWithMonitorTest.java
@@ -27,7 +27,7 @@
   public void regress() throws Exception {
     testForR8(parameters.getBackend())
         .addInnerClasses(ConstStringWithMonitorTest.class)
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .allowAccessModification()
         .addKeepMainRule(TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/regress/b158429654/InliningNonAccessible.java b/src/test/java/com/android/tools/r8/regress/b158429654/InliningNonAccessible.java
index dfed133..9cebb80 100644
--- a/src/test/java/com/android/tools/r8/regress/b158429654/InliningNonAccessible.java
+++ b/src/test/java/com/android/tools/r8/regress/b158429654/InliningNonAccessible.java
@@ -32,7 +32,7 @@
         .addProgramClasses(OuterAbstract.class, OuterImpl.class, InnerClass.class)
         .addProgramClasses(Main.class)
         .addKeepMainRule(Main.class)
-        .noMinification()
+        .addDontObfuscate()
         .run(parameters.getRuntime(), Main.class)
         .assertSuccessWithOutputLines("42");
   }
diff --git a/src/test/java/com/android/tools/r8/regress/b158432019/StaticizerSyntheticUseTest.java b/src/test/java/com/android/tools/r8/regress/b158432019/StaticizerSyntheticUseTest.java
index e07f8f1..8fde692 100644
--- a/src/test/java/com/android/tools/r8/regress/b158432019/StaticizerSyntheticUseTest.java
+++ b/src/test/java/com/android/tools/r8/regress/b158432019/StaticizerSyntheticUseTest.java
@@ -42,7 +42,7 @@
         .addProgramClasses(Singleton.class, Main.class, Sam.class, A.class)
         .addKeepClassAndMembersRules(Main.class)
         .setMinApi(parameters.getApiLevel())
-        .noMinification()
+        .addDontObfuscate()
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .run(parameters.getRuntime(), Main.class)
diff --git a/src/test/java/com/android/tools/r8/regress/b63935662/Regress63935662.java b/src/test/java/com/android/tools/r8/regress/b63935662/Regress63935662.java
index ced781f..a3ce92b 100644
--- a/src/test/java/com/android/tools/r8/regress/b63935662/Regress63935662.java
+++ b/src/test/java/com/android/tools/r8/regress/b63935662/Regress63935662.java
@@ -36,7 +36,7 @@
     testBuilder
         .addKeepRuleFiles()
         .allowAccessModification()
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .run(parameters.getRuntime(), mainClass)
diff --git a/src/test/java/com/android/tools/r8/regress/b69825683/Regress69825683Test.java b/src/test/java/com/android/tools/r8/regress/b69825683/Regress69825683Test.java
index eb1e0bf..e0a469c 100644
--- a/src/test/java/com/android/tools/r8/regress/b69825683/Regress69825683Test.java
+++ b/src/test/java/com/android/tools/r8/regress/b69825683/Regress69825683Test.java
@@ -55,7 +55,7 @@
                 "  synthetic void <init>(...);",
                 "}")
             .addOptionsModification(options -> options.enableClassInlining = false)
-            .noMinification()
+            .addDontObfuscate()
             .setMinApi(parameters.getApiLevel())
             .run(parameters.getRuntime(), outer)
             // Run code to check that the constructor with synthetic class as argument is present.
@@ -82,7 +82,7 @@
                 "-assumemayhavesideeffects class " + clazz.getName() + " {",
                 "  void <init>(...);",
                 "}")
-            .noMinification()
+            .addDontObfuscate()
             .addOptionsModification(o -> o.enableClassInlining = false)
             .setMinApi(parameters.getApiLevel())
             // Run code to check that the constructor with synthetic class as argument is present.
diff --git a/src/test/java/com/android/tools/r8/regress/b76025099/B76025099.java b/src/test/java/com/android/tools/r8/regress/b76025099/B76025099.java
index e179440..79eadb7 100644
--- a/src/test/java/com/android/tools/r8/regress/b76025099/B76025099.java
+++ b/src/test/java/com/android/tools/r8/regress/b76025099/B76025099.java
@@ -62,7 +62,7 @@
         testForProguard()
             .addProgramFiles(ToolHelper.getClassFilesForTestPackage(Main.class.getPackage()))
             .addKeepMainRule(Main.class)
-            .noMinification()
+            .addDontObfuscate()
             .compile();
 
     if (parameters.isDexRuntime()) {
@@ -86,7 +86,7 @@
         .addProgramFiles(ToolHelper.getClassFilesForTestPackage(Main.class.getPackage()))
         .addKeepMainRule(Main.class)
         .enableNoVerticalClassMergingAnnotations()
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(this::verifyFieldAccess)
diff --git a/src/test/java/com/android/tools/r8/regress/b78493232/Regress78493232_WithPhi.java b/src/test/java/com/android/tools/r8/regress/b78493232/Regress78493232_WithPhi.java
index 7028257..b2b9dad 100644
--- a/src/test/java/com/android/tools/r8/regress/b78493232/Regress78493232_WithPhi.java
+++ b/src/test/java/com/android/tools/r8/regress/b78493232/Regress78493232_WithPhi.java
@@ -90,7 +90,7 @@
             .addProgramClassFileData(CLASS_BYTES)
             .allowDiagnosticWarningMessages()
             .treeShaking(treeShake)
-            .noMinification()
+            .addDontObfuscate()
             .setMinApi(parameters.getApiLevel())
             .addOptionsModification(options -> options.testing.readInputStackMaps = false)
             .addKeepMainRule(MAIN)
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageDontObfuscateTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageDontObfuscateTest.java
index 39b3d43..658822d 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageDontObfuscateTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageDontObfuscateTest.java
@@ -61,7 +61,7 @@
         .apply(this::configureRepackaging)
         .addInliningAnnotations()
         .addDontWarn(RepackageDontObfuscateTest.class)
-        .noMinification()
+        .addDontObfuscate()
         .compile()
         .inspect(
             inspector -> {
@@ -106,7 +106,7 @@
         .addKeepMainRule(Main.class)
         .enableInliningAnnotations()
         .apply(this::configureRepackaging)
-        .noMinification()
+        .addDontObfuscate()
         .compile();
   }
 
diff --git a/src/test/java/com/android/tools/r8/resolution/SingleTargetExecutionTest.java b/src/test/java/com/android/tools/r8/resolution/SingleTargetExecutionTest.java
index 4726fe4..12b9676 100644
--- a/src/test/java/com/android/tools/r8/resolution/SingleTargetExecutionTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/SingleTargetExecutionTest.java
@@ -69,7 +69,7 @@
   @Test
   public void testR8() throws Exception {
     testForR8(parameters.getBackend())
-        .noMinification()
+        .addDontObfuscate()
         .addProgramClasses(CLASSES)
         .addProgramClassFileData(ASM_CLASSES)
         .addKeepMainRule(Main.class)
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/ProgramAndLibraryDefinitionTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/ProgramAndLibraryDefinitionTest.java
index 8494f39..e76a5b6 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/ProgramAndLibraryDefinitionTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/ProgramAndLibraryDefinitionTest.java
@@ -72,7 +72,7 @@
             .addDontWarn(A.class, B.class)
             .addKeepMainRule(Main.class)
             .addOptionsModification(options -> options.loadAllClassDefinitions = true)
-            .noMinification()
+            .addDontObfuscate()
             .allowUnusedDontWarnPatterns();
     byte[] libraryA = getAFromClassTestParam(this.aInLibrary);
     addIfNotNull(libraryA, testBuilder::addLibraryClassFileData);
diff --git a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionInSameFileRetraceTests.java b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionInSameFileRetraceTests.java
index 3d3331b..8eb9f47 100644
--- a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionInSameFileRetraceTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionInSameFileRetraceTests.java
@@ -99,7 +99,7 @@
         .setMode(CompilationMode.RELEASE)
         .addKeepMainRule(MAIN)
         .allowDiagnosticWarningMessages()
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .assertAllWarningMessagesMatch(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
diff --git a/src/test/java/com/android/tools/r8/rewrite/ScriptEngineTest.java b/src/test/java/com/android/tools/r8/rewrite/ScriptEngineTest.java
index 33394dd..8a6035c 100644
--- a/src/test/java/com/android/tools/r8/rewrite/ScriptEngineTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/ScriptEngineTest.java
@@ -84,7 +84,7 @@
               }
             })
         // TODO(b/136633154): This should work both with and without -dontobfuscate.
-        .noMinification()
+        .addDontObfuscate()
         // TODO(b/136633154): This should work both with and without -dontshrink.
         .noTreeShaking()
         .compile()
diff --git a/src/test/java/com/android/tools/r8/rewrite/ServiceLoaderRewritingTest.java b/src/test/java/com/android/tools/r8/rewrite/ServiceLoaderRewritingTest.java
index cd7964f..76fe6b6 100644
--- a/src/test/java/com/android/tools/r8/rewrite/ServiceLoaderRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/ServiceLoaderRewritingTest.java
@@ -282,7 +282,7 @@
             .addKeepMainRule(EscapingRunner.class)
             .enableInliningAnnotations()
             .setMinApi(parameters.getApiLevel())
-            .noMinification()
+            .addDontObfuscate()
             .addDataEntryResources(
                 DataEntryResource.fromBytes(
                     StringUtils.lines(ServiceImpl.class.getTypeName()).getBytes(),
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/RemoveAssertionsTest.java b/src/test/java/com/android/tools/r8/rewrite/assertions/RemoveAssertionsTest.java
index 837b449..e5e3b29 100644
--- a/src/test/java/com/android/tools/r8/rewrite/assertions/RemoveAssertionsTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/RemoveAssertionsTest.java
@@ -190,7 +190,7 @@
         .addKeepMainRule(ClassWithAssertions.class)
         .addOptionsModification(o -> o.inlinerOptions().enableInlining = false)
         .allowAccessModification()
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(minApi)
         .compile();
   }
@@ -202,7 +202,7 @@
         .addProgramClasses(ClassWithAssertions.class)
         .debug()
         .noTreeShaking()
-        .noMinification()
+        .addDontObfuscate()
         .addAssertionsConfiguration(transformation)
         .compile();
   }
@@ -226,7 +226,7 @@
         .setMinApi(AndroidApiLevel.B)
         .debug()
         .noTreeShaking()
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(minApi)
         .compile();
   }
@@ -355,7 +355,7 @@
             .debug()
             .setMinApi(minApi)
             .noTreeShaking()
-            .noMinification()
+            .addDontObfuscate()
             .compile()
             .writeToZip();
 
diff --git a/src/test/java/com/android/tools/r8/rewrite/logarguments/LogArgumentsTest.java b/src/test/java/com/android/tools/r8/rewrite/logarguments/LogArgumentsTest.java
index 3e69d0b..8f37d3d 100644
--- a/src/test/java/com/android/tools/r8/rewrite/logarguments/LogArgumentsTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/logarguments/LogArgumentsTest.java
@@ -36,7 +36,7 @@
             .addOptionsModification(
                 options -> options.logArgumentsFilter = ImmutableList.of(qualifiedMethodName))
             .assumeAllMethodsMayHaveSideEffects()
-            .noMinification()
+            .addDontObfuscate()
             .noTreeShaking()
             .run(TestStatic.class)
             .getStdOut();
@@ -57,7 +57,7 @@
             .addOptionsModification(
                 options -> options.logArgumentsFilter = ImmutableList.of(qualifiedMethodName))
             .assumeAllMethodsMayHaveSideEffects()
-            .noMinification()
+            .addDontObfuscate()
             .noTreeShaking()
             .run(TestInstance.class)
             .getStdOut();
diff --git a/src/test/java/com/android/tools/r8/rewrite/staticvalues/inlibraries/StaticLibraryValuesChangeTest.java b/src/test/java/com/android/tools/r8/rewrite/staticvalues/inlibraries/StaticLibraryValuesChangeTest.java
index 16691d8..dc1d864 100644
--- a/src/test/java/com/android/tools/r8/rewrite/staticvalues/inlibraries/StaticLibraryValuesChangeTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/staticvalues/inlibraries/StaticLibraryValuesChangeTest.java
@@ -83,11 +83,12 @@
         .addProgramClasses(TestMain.class)
         .addLibraryFiles(ToolHelper.getDefaultAndroidJar())
         // Compile TestMain with R8 using the second version of LibraryClass as library.
-        .addLibraryProvider(PreloadedClassFileProvider.fromClassData(
-            DescriptorUtils.javaTypeToDescriptor(LibraryClass.class.getName()),
-            compileTimeLibrary.buildClasses().get(0)))
+        .addLibraryProvider(
+            PreloadedClassFileProvider.fromClassData(
+                DescriptorUtils.javaTypeToDescriptor(LibraryClass.class.getName()),
+                compileTimeLibrary.buildClasses().get(0)))
         .noTreeShaking()
-        .noMinification()
+        .addDontObfuscate()
         .compile()
         // Merge the compiled TestMain with the runtime version of LibraryClass.
         .addRunClasspathFiles(lib)
diff --git a/src/test/java/com/android/tools/r8/shaking/FunctionTest.java b/src/test/java/com/android/tools/r8/shaking/FunctionTest.java
index 39169a1..eda629d 100644
--- a/src/test/java/com/android/tools/r8/shaking/FunctionTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/FunctionTest.java
@@ -43,7 +43,7 @@
     testForR8(parameters.getBackend())
         .addKeepMainRule(TestClass.class)
         .noTreeShaking()
-        .noMinification()
+        .addDontObfuscate()
         .enableInliningAnnotations()
         .addInnerClasses(FunctionTest.class)
         .run(parameters.getRuntime(), TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/shaking/ParameterTypeTest.java b/src/test/java/com/android/tools/r8/shaking/ParameterTypeTest.java
index eeb91af..e38943c 100644
--- a/src/test/java/com/android/tools/r8/shaking/ParameterTypeTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ParameterTypeTest.java
@@ -314,7 +314,7 @@
               options.inlinerOptions().enableInlining = false;
               options.callSiteOptimizationOptions().setEnabled(enableArgumentPropagation);
             })
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(
diff --git a/src/test/java/com/android/tools/r8/shaking/addconfigurationdebugging/ConfigurationDebuggingTest.java b/src/test/java/com/android/tools/r8/shaking/addconfigurationdebugging/ConfigurationDebuggingTest.java
index c7630ba..ac46b3f 100644
--- a/src/test/java/com/android/tools/r8/shaking/addconfigurationdebugging/ConfigurationDebuggingTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/addconfigurationdebugging/ConfigurationDebuggingTest.java
@@ -109,7 +109,7 @@
             .addProgramClasses(BaseClass.class, UninstantiatedClass.class, TestClass.class)
             .addKeepRules("-addconfigurationdebugging")
             .addKeepRules("-keep class **.TestClass { <init>(); }")
-            .noMinification()
+            .addDontObfuscate()
             .setMinApi(parameters.getApiLevel())
             .compile()
             .inspect(this::inspect)
diff --git a/src/test/java/com/android/tools/r8/shaking/annotations/AnnotationsOnTargetedMethodTest.java b/src/test/java/com/android/tools/r8/shaking/annotations/AnnotationsOnTargetedMethodTest.java
index d737c41..35dace2 100644
--- a/src/test/java/com/android/tools/r8/shaking/annotations/AnnotationsOnTargetedMethodTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/annotations/AnnotationsOnTargetedMethodTest.java
@@ -90,7 +90,7 @@
         .enableNeverClassInliningAnnotations()
         .enableNoHorizontalClassMergingAnnotations()
         .enableNoVerticalClassMergingAnnotations()
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), TestClass.class)
         .assertSuccessWithOutput(
diff --git a/src/test/java/com/android/tools/r8/shaking/annotations/SerializedNameAlternateTest.java b/src/test/java/com/android/tools/r8/shaking/annotations/SerializedNameAlternateTest.java
index de259e5..7d2173f 100644
--- a/src/test/java/com/android/tools/r8/shaking/annotations/SerializedNameAlternateTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/annotations/SerializedNameAlternateTest.java
@@ -91,7 +91,7 @@
                 "  @" + SerializedName.class.getTypeName() + " <fields>;",
                 "}")
             .setMinApi(parameters.getApiLevel())
-            .noMinification()
+            .addDontObfuscate()
             .run(parameters.getRuntime(), Main.class);
     checkRunResult(result);
   }
@@ -107,7 +107,7 @@
             .addKeepMainRule(Main.class)
             .addKeepClassRules(Foo.class)
             .addKeepClassRules(SerializedName.class)
-            .noMinification()
+            .addDontObfuscate()
             .addKeepRules("-dontwarn")
             .run(parameters.getRuntime(), Main.class);
     checkRunResult(result);
diff --git a/src/test/java/com/android/tools/r8/shaking/array/DeadArrayLengthTest.java b/src/test/java/com/android/tools/r8/shaking/array/DeadArrayLengthTest.java
index 03d5aa6..a4ba893 100644
--- a/src/test/java/com/android/tools/r8/shaking/array/DeadArrayLengthTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/array/DeadArrayLengthTest.java
@@ -78,7 +78,7 @@
         .addKeepMainRule(MAIN)
         .enableInliningAnnotations()
         .enableMemberValuePropagationAnnotations()
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), MAIN)
         .assertSuccessWithOutput(EXPECTED_OUTPUT)
diff --git a/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsPropagationTest.java b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsPropagationTest.java
index b73fc3e..5d95ca0 100644
--- a/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsPropagationTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsPropagationTest.java
@@ -153,7 +153,7 @@
             options ->
                 options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableInliningAnnotations()
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), MAIN)
         .assertSuccessWithOutput(config.expectedOutput(enableHorizontalClassMerging));
diff --git a/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsPropagationWithSuperCallTest.java b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsPropagationWithSuperCallTest.java
index fa8d22b..a97890a 100644
--- a/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsPropagationWithSuperCallTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsPropagationWithSuperCallTest.java
@@ -76,7 +76,7 @@
         .addInnerClasses(AssumenosideeffectsPropagationWithSuperCallTest.class)
         .addKeepMainRule(MAIN)
         .addKeepRules(config.getKeepRules())
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), MAIN)
         .assertSuccessWithOutput(config.expectedOutput(parameters));
diff --git a/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsPropagationWithoutMatchingDefinitionTest.java b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsPropagationWithoutMatchingDefinitionTest.java
index e4edf08..ad79ff7 100644
--- a/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsPropagationWithoutMatchingDefinitionTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsPropagationWithoutMatchingDefinitionTest.java
@@ -52,7 +52,7 @@
             "-assumenosideeffects class * implements " + LoggerInterface.class.getTypeName() + " {",
             "  *** debug(...);",
             "}")
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getRuntime())
         .run(parameters.getRuntime(), MAIN)
         .assertSuccessWithOutput(OUTPUT_WITHOUT_LOGGING)
diff --git a/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsVisibleMethodsTest.java b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsVisibleMethodsTest.java
index 6b053ee..73151e0 100644
--- a/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsVisibleMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsVisibleMethodsTest.java
@@ -156,7 +156,7 @@
         .addProgramClasses(ProgramBase.class, ProgramSub.class, MAIN)
         .addKeepMainRule(MAIN)
         .addKeepRules(config.getKeepRule())
-        .noMinification()
+        .addDontObfuscate()
         .enableNoVerticalClassMergingAnnotations()
         .enableNeverClassInliningAnnotations()
         .enableInliningAnnotations()
diff --git a/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsWithMultipleTargetsTest.java b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsWithMultipleTargetsTest.java
index daaacb2..41311d2 100644
--- a/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsWithMultipleTargetsTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsWithMultipleTargetsTest.java
@@ -98,7 +98,7 @@
         .enableInliningAnnotations()
         .addKeepMainRule(MAIN)
         .addKeepRules(config.getKeepRule())
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getRuntime())
         .run(parameters.getRuntime(), MAIN)
         .assertSuccessWithOutput(TestConfig.OUTPUT_WITHOUT_INFO)
diff --git a/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsWithoutMatchingDefinitionTest.java b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsWithoutMatchingDefinitionTest.java
index 010af23..0bd97f8 100644
--- a/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsWithoutMatchingDefinitionTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsWithoutMatchingDefinitionTest.java
@@ -114,7 +114,7 @@
         .addProgramClasses(MAIN)
         .addKeepMainRule(MAIN)
         .addKeepRules(config.getKeepRule())
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getRuntime())
         .compile()
         .addRunClasspathFiles(parameters.isDexRuntime() ? libDexPath : libJarPath)
diff --git a/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsWithoutReturnValueTest.java b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsWithoutReturnValueTest.java
index 3596b42..352a53c 100644
--- a/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsWithoutReturnValueTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsWithoutReturnValueTest.java
@@ -73,7 +73,7 @@
         .enableInliningAnnotations()
         .addKeepMainRule(MAIN)
         .addKeepRules(config.getKeepRules())
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getRuntime())
         .compile()
         .inspect(this::verifyDebugMethodIsRemoved)
diff --git a/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/StringBuildersAfterAssumenosideeffectsTest.java b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/StringBuildersAfterAssumenosideeffectsTest.java
index b80febc..ca28f14 100644
--- a/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/StringBuildersAfterAssumenosideeffectsTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/StringBuildersAfterAssumenosideeffectsTest.java
@@ -50,7 +50,7 @@
             "-assumenosideeffects class * implements " + TestLogger.class.getTypeName() + " {",
             "  void info(...);",
             "}")
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), MAIN)
         .assertSuccessWithOutput(EXPECTED)
diff --git a/src/test/java/com/android/tools/r8/shaking/assumevalues/AssumevaluesWithMultipleTargetsTest.java b/src/test/java/com/android/tools/r8/shaking/assumevalues/AssumevaluesWithMultipleTargetsTest.java
index eb205d0..a6d7d67 100644
--- a/src/test/java/com/android/tools/r8/shaking/assumevalues/AssumevaluesWithMultipleTargetsTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/assumevalues/AssumevaluesWithMultipleTargetsTest.java
@@ -159,7 +159,7 @@
         .enableInliningAnnotations()
         .addKeepMainRule(MAIN)
         .addKeepRules(config.getKeepRule())
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getRuntime())
         .run(parameters.getRuntime(), MAIN)
         .assertSuccessWithOutput(TestConfig.OUTPUT_WITH_FULL_REPLACEMENT)
diff --git a/src/test/java/com/android/tools/r8/shaking/attributes/InnerClassesSimpleTest.java b/src/test/java/com/android/tools/r8/shaking/attributes/InnerClassesSimpleTest.java
index 0474186..41de889 100644
--- a/src/test/java/com/android/tools/r8/shaking/attributes/InnerClassesSimpleTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/attributes/InnerClassesSimpleTest.java
@@ -41,7 +41,7 @@
             .addKeepMainRule(Main.class)
             .addKeepPackageNamesRule(getClass().getPackage())
             .noTreeShaking()
-            .applyIf(!minify, TestShrinkerBuilder::noMinification)
+            .applyIf(!minify, TestShrinkerBuilder::addDontObfuscate)
             .compile()
             .writeToZip();
     testForR8(parameters.getBackend())
diff --git a/src/test/java/com/android/tools/r8/shaking/b136698023/LibraryBridgeTest.java b/src/test/java/com/android/tools/r8/shaking/b136698023/LibraryBridgeTest.java
index baba02c..45286b6 100644
--- a/src/test/java/com/android/tools/r8/shaking/b136698023/LibraryBridgeTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/b136698023/LibraryBridgeTest.java
@@ -101,7 +101,7 @@
         .addProgramClasses(ICloneable.class, BaseOrdinaryClone.class, MainWithOrdinaryClone.class)
         .addKeepClassAndMembersRules(ICloneable.class)
         .addKeepMainRule(MainWithOrdinaryClone.class)
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), MainWithOrdinaryClone.class)
         .assertSuccessWithOutputThatMatches(containsString(BaseOrdinaryClone.class.getTypeName()));
@@ -114,7 +114,7 @@
         .addProgramClasses(XCloneable.class, Model.class, Main.class, Base.class)
         .addKeepClassAndMembersRules(XCloneable.class, Base.class)
         .addKeepMainRule(Main.class)
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), Main.class)
         .assertSuccessWithOutputThatMatches(containsString(Model.class.getTypeName()));
@@ -127,7 +127,7 @@
         .addProgramClasses(XCloneable.class, ModelImpl.class, MainImpl.class, Base.class)
         .addKeepClassAndMembersRules(XCloneable.class, Base.class)
         .addKeepMainRule(MainImpl.class)
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), MainImpl.class)
         .assertSuccessWithOutputThatMatches(containsString(Model.class.getTypeName()));
@@ -149,7 +149,7 @@
         .addKeepClassAndMembersRules(XCloneable.class)
         .addKeepMainRule(Main.class)
         .setMinApi(parameters.getApiLevel())
-        .noMinification()
+        .addDontObfuscate()
         .compile()
         .addRunClasspathFiles(library.writeToZip())
         .run(parameters.getRuntime(), Main.class)
diff --git a/src/test/java/com/android/tools/r8/shaking/b136698023/ProgramInterfaceNotImplementedTest.java b/src/test/java/com/android/tools/r8/shaking/b136698023/ProgramInterfaceNotImplementedTest.java
index 0d41b96..970dd1e 100644
--- a/src/test/java/com/android/tools/r8/shaking/b136698023/ProgramInterfaceNotImplementedTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/b136698023/ProgramInterfaceNotImplementedTest.java
@@ -127,7 +127,7 @@
         .addProgramClasses(I.class, clazz, main)
         .addKeepClassAndMembersRules(I.class)
         .addKeepMainRule(main)
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getRuntime())
         .run(parameters.getRuntime(), main)
         .assertSuccessWithOutputThatMatches(containsString(clazz.getTypeName()))
@@ -155,7 +155,7 @@
         .addKeepClassAndMembersRules(I.class)
         .addKeepMainRule(MainD.class)
         .setMinApi(parameters.getRuntime())
-        .noMinification()
+        .addDontObfuscate()
         .compile()
         .addRunClasspathFiles(library.writeToZip())
         .run(parameters.getRuntime(), MainD.class)
diff --git a/src/test/java/com/android/tools/r8/shaking/b169045091/B169045091.java b/src/test/java/com/android/tools/r8/shaking/b169045091/B169045091.java
index e8f8091..fdec859 100644
--- a/src/test/java/com/android/tools/r8/shaking/b169045091/B169045091.java
+++ b/src/test/java/com/android/tools/r8/shaking/b169045091/B169045091.java
@@ -59,7 +59,7 @@
         .enableInliningAnnotations()
         .enableNoVerticalClassMergingAnnotations()
         .setMinApi(parameters.getApiLevel())
-        .noMinification()
+        .addDontObfuscate()
         .compile()
         .run(parameters.getRuntime(), TestClass.class)
         .assertSuccessWithOutputLines("Hello world!");
diff --git a/src/test/java/com/android/tools/r8/shaking/clinit/InterfaceInitializedByImplementationTest.java b/src/test/java/com/android/tools/r8/shaking/clinit/InterfaceInitializedByImplementationTest.java
index dcb962b..eecee11 100644
--- a/src/test/java/com/android/tools/r8/shaking/clinit/InterfaceInitializedByImplementationTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/clinit/InterfaceInitializedByImplementationTest.java
@@ -46,7 +46,7 @@
         .enableInliningAnnotations()
         .enableNoMethodStaticizingAnnotations()
         .setMinApi(parameters.getApiLevel())
-        .noMinification()
+        .addDontObfuscate()
         .compile()
         .inspect(this::inspect)
         .run(parameters.getRuntime(), TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/shaking/defaultmethods/DefaultMethodsTest.java b/src/test/java/com/android/tools/r8/shaking/defaultmethods/DefaultMethodsTest.java
index 0a54cf8..ae783ee 100644
--- a/src/test/java/com/android/tools/r8/shaking/defaultmethods/DefaultMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/defaultmethods/DefaultMethodsTest.java
@@ -51,7 +51,7 @@
         .setMinApi(parameters.getApiLevel())
         .addKeepMainRule(TestClass.class)
         .addKeepRules(additionalKeepRules)
-        .noMinification()
+        .addDontObfuscate()
         .compile()
         .inspect(inspection);
   }
diff --git a/src/test/java/com/android/tools/r8/shaking/definitelynull/DefinitelyNullTest.java b/src/test/java/com/android/tools/r8/shaking/definitelynull/DefinitelyNullTest.java
index c736dc1..8431fcb 100644
--- a/src/test/java/com/android/tools/r8/shaking/definitelynull/DefinitelyNullTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/definitelynull/DefinitelyNullTest.java
@@ -49,7 +49,7 @@
     }
     testForR8(parameters.getBackend())
         // Disable minification so that A still refers to A given on the classpath below.
-        .noMinification()
+        .addDontObfuscate()
         .addProgramClasses(TestClass.class, A.class)
         .addKeepMainRule(TestClass.class)
         .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
index e00615a..48f0372 100644
--- a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
@@ -82,7 +82,7 @@
       throws Exception {
     CodeInspector inspector =
         testForR8Compat(parameters.getBackend(), forceProguardCompatibility)
-            .noMinification()
+            .addDontObfuscate()
             .allowAccessModification()
             .addProgramClasses(mainClass, mentionedClass)
             .addKeepMainRule(mainClass)
diff --git a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/keepinterface/CompatKeepInterfaceAsInstantiatedTest.java b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/keepinterface/CompatKeepInterfaceAsInstantiatedTest.java
index 38a6b4f..c68e841 100644
--- a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/keepinterface/CompatKeepInterfaceAsInstantiatedTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/keepinterface/CompatKeepInterfaceAsInstantiatedTest.java
@@ -130,7 +130,7 @@
         .applyIf(
             builder.isProguardTestBuilder(),
             b -> b.addDontWarn(CompatKeepInterfaceAsInstantiatedTest.class))
-        .noMinification()
+        .addDontObfuscate()
         .addProgramClasses(main, Foo.class)
         .addKeepMainRule(main)
         .compile()
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedTypeBaseTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedTypeBaseTest.java
index a482eee..385f32f 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedTypeBaseTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedTypeBaseTest.java
@@ -104,7 +104,7 @@
             getConditionForProguardIfRule(),
             "-keep class " + Unused.class.getTypeName(),
             getAdditionalKeepRules())
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .apply(this::configure)
         .run(parameters.getRuntime(), getTestClass())
diff --git a/src/test/java/com/android/tools/r8/shaking/methods/interfaces/DefaultInterfaceMethodsTest.java b/src/test/java/com/android/tools/r8/shaking/methods/interfaces/DefaultInterfaceMethodsTest.java
index f75f7bd..aa1fe71 100644
--- a/src/test/java/com/android/tools/r8/shaking/methods/interfaces/DefaultInterfaceMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/methods/interfaces/DefaultInterfaceMethodsTest.java
@@ -72,7 +72,7 @@
             .addKeepMethodRules(J.class, "void foo()")
             .addOptionsModification(
                 internalOptions -> internalOptions.enableVerticalClassMerging = false)
-            .noMinification()
+            .addDontObfuscate()
             .compile();
     // TODO(b/144269679): We should be able to compile and run this.
     testForR8(parameters.getBackend())
diff --git a/src/test/java/com/android/tools/r8/shaking/methods/interfaces/InterfaceDefaultMethodKeptTest.java b/src/test/java/com/android/tools/r8/shaking/methods/interfaces/InterfaceDefaultMethodKeptTest.java
index b7b517b..91f4153 100644
--- a/src/test/java/com/android/tools/r8/shaking/methods/interfaces/InterfaceDefaultMethodKeptTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/methods/interfaces/InterfaceDefaultMethodKeptTest.java
@@ -95,7 +95,7 @@
             .setMinApi(parameters.getApiLevel())
             .addKeepMethodRules(A.class, "void <init>()", "void foo()")
             .addKeepMethodRules(B.class, "void <init>()", "void foo()")
-            .noMinification()
+            .addDontObfuscate()
             .compile()
             .inspect(
                 codeInspector -> {
diff --git a/src/test/java/com/android/tools/r8/shaking/methods/interfaces/MultipleTargetTest.java b/src/test/java/com/android/tools/r8/shaking/methods/interfaces/MultipleTargetTest.java
index 7e11e46..1fdd5d1 100644
--- a/src/test/java/com/android/tools/r8/shaking/methods/interfaces/MultipleTargetTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/methods/interfaces/MultipleTargetTest.java
@@ -54,7 +54,7 @@
             .addProgramClasses(Top.class, Left.class, Right.class, Bottom.class)
             .setMinApi(parameters.getApiLevel())
             .addKeepMethodRules(Bottom.class, "java.lang.String name()")
-            .noMinification()
+            .addDontObfuscate()
             .compile();
     // TODO(b/144269679): We should be able to compile and run this.
     testForR8(parameters.getBackend())
diff --git a/src/test/java/com/android/tools/r8/shaking/modifiers/SyntheticAndBridgeModifiersTest.java b/src/test/java/com/android/tools/r8/shaking/modifiers/SyntheticAndBridgeModifiersTest.java
index 4ef106c..6570f3f 100644
--- a/src/test/java/com/android/tools/r8/shaking/modifiers/SyntheticAndBridgeModifiersTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/modifiers/SyntheticAndBridgeModifiersTest.java
@@ -27,28 +27,30 @@
     testForProguard()
         .addProgramClasses(CLASSES)
         .addKeepRules(keepRules)
-        .noMinification()
+        .addDontObfuscate()
         .compile()
         .disassemble()
-        .inspect(inspector -> {
-          ClassSubject clazz =
-              inspector.clazz(SyntheticAndBridgeModifiersTestClass.TestClass.class);
-          assertThat(clazz, isPresent());
-          classConsumer.accept(clazz);
-        });
+        .inspect(
+            inspector -> {
+              ClassSubject clazz =
+                  inspector.clazz(SyntheticAndBridgeModifiersTestClass.TestClass.class);
+              assertThat(clazz, isPresent());
+              classConsumer.accept(clazz);
+            });
 
     testForR8(Backend.DEX)
         .addProgramClasses(CLASSES)
         .addKeepRules(keepRules)
-        .noMinification()
+        .addDontObfuscate()
         .compile()
         .disassemble()
-        .inspect(inspector -> {
-          ClassSubject clazz =
-              inspector.clazz(SyntheticAndBridgeModifiersTestClass.TestClass.class);
-          assertThat(clazz, isPresent());
-          classConsumer.accept(clazz);
-        });
+        .inspect(
+            inspector -> {
+              ClassSubject clazz =
+                  inspector.clazz(SyntheticAndBridgeModifiersTestClass.TestClass.class);
+              assertThat(clazz, isPresent());
+              classConsumer.accept(clazz);
+            });
   }
 
   private long methodsWithNameStartingWith(ClassSubject clazz, String prefix) {
diff --git a/src/test/java/com/android/tools/r8/shaking/negatedrules/NegatedKeepRulesTest.java b/src/test/java/com/android/tools/r8/shaking/negatedrules/NegatedKeepRulesTest.java
index 0468d27..57e37fe 100644
--- a/src/test/java/com/android/tools/r8/shaking/negatedrules/NegatedKeepRulesTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/negatedrules/NegatedKeepRulesTest.java
@@ -333,7 +333,7 @@
     return testBuilder
         .addProgramClasses(A.class, B.class, C.class, D.class, FooBar.class, BarBar.class)
         .setMinApi(AndroidApiLevel.B)
-        .noMinification()
+        .addDontObfuscate()
         .compile();
   }
 
diff --git a/src/test/java/com/android/tools/r8/shaking/negatedrules/NegatedMethodArgumentTest.java b/src/test/java/com/android/tools/r8/shaking/negatedrules/NegatedMethodArgumentTest.java
index b4d6576..2fabb47 100644
--- a/src/test/java/com/android/tools/r8/shaking/negatedrules/NegatedMethodArgumentTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/negatedrules/NegatedMethodArgumentTest.java
@@ -76,7 +76,7 @@
         .addKeepMainRule(TestClass.class)
         .addKeepRules("-keep class * { void setX(!%); }")
         .addKeepRules("-dontwarn **.NegatedMethodArgumentTest")
-        .noMinification()
+        .addDontObfuscate()
         .run(parameters.getRuntime(), TestClass.class)
         .inspect(
             inspector -> {
@@ -94,7 +94,7 @@
         .addKeepMainRule(TestClass.class)
         .addKeepRules("-keep class * { void setX(!**.*P1); }")
         .addKeepRules("-dontwarn **.NegatedMethodArgumentTest")
-        .noMinification()
+        .addDontObfuscate()
         .run(parameters.getRuntime(), TestClass.class)
         .inspect(
             inspector -> {
diff --git a/src/test/java/com/android/tools/r8/shaking/proxy/ProxiesTest.java b/src/test/java/com/android/tools/r8/shaking/proxy/ProxiesTest.java
index 047a109..75b74e6 100644
--- a/src/test/java/com/android/tools/r8/shaking/proxy/ProxiesTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/proxy/ProxiesTest.java
@@ -67,7 +67,7 @@
               o.enableDevirtualization = false;
             })
         .enableAlwaysInliningAnnotations()
-        .noMinification()
+        .addDontObfuscate()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(inspection)
diff --git a/src/test/java/com/android/tools/r8/softverification/TestInstanceFieldWithOtherMembers.java b/src/test/java/com/android/tools/r8/softverification/TestInstanceFieldWithOtherMembers.java
new file mode 100644
index 0000000..b13476d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/softverification/TestInstanceFieldWithOtherMembers.java
@@ -0,0 +1,186 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.softverification;
+
+public class TestInstanceFieldWithOtherMembers {
+
+  public static String run() {
+    return run(null);
+  }
+
+  public static String run(MissingClass missingClass) {
+    if (System.currentTimeMillis() == 0) {
+      System.out.println(missingClass.instanceField);
+    }
+    if (System.currentTimeMillis() == 0) {
+      System.out.println(missingClass.instanceField);
+    }
+    String currentString = "foobar";
+    for (int i = 0; i < 10; i++) {
+      currentString = "foobar" + (i + currentString.length());
+    }
+    return currentString;
+  }
+
+  public static String run1(MissingClass missingClass) {
+    if (System.currentTimeMillis() == 0) {
+      missingClass.instanceMethod();
+      return "foo";
+    }
+    return "bar";
+  }
+
+  public static String run2(MissingClass missingClass) {
+    if (System.currentTimeMillis() == 0) {
+      missingClass.instanceMethod();
+      return "foo";
+    }
+    return "bar";
+  }
+
+  public static String run3(MissingClass missingClass) {
+    if (System.currentTimeMillis() == 0) {
+      missingClass.instanceMethod();
+      return "foo";
+    }
+    return "bar";
+  }
+
+  public static String run4(MissingClass missingClass) {
+    if (System.currentTimeMillis() == 0) {
+      missingClass.instanceMethod();
+      return "foo";
+    }
+    return "bar";
+  }
+
+  public static String run5(MissingClass missingClass) {
+    if (System.currentTimeMillis() == 0) {
+      missingClass.instanceMethod();
+      return "foo";
+    }
+    return "bar";
+  }
+
+  public static String run6(MissingClass missingClass) {
+    if (System.currentTimeMillis() == 0) {
+      missingClass.instanceMethod();
+      return "foo";
+    }
+    return "bar";
+  }
+
+  public static String run7(MissingClass missingClass) {
+    if (System.currentTimeMillis() == 0) {
+      missingClass.instanceMethod();
+      return "foo";
+    }
+    return "bar";
+  }
+
+  public static String run8(MissingClass missingClass) {
+    if (System.currentTimeMillis() == 0) {
+      missingClass.instanceMethod();
+      return "foo";
+    }
+    return "bar";
+  }
+
+  public static String run9(MissingClass missingClass) {
+    if (System.currentTimeMillis() == 0) {
+      missingClass.instanceMethod();
+      return "foo";
+    }
+    return "bar";
+  }
+
+  public static String run10(MissingClass missingClass) {
+    if (System.currentTimeMillis() == 0) {
+      missingClass.instanceMethod();
+      return "foo";
+    }
+    return "bar";
+  }
+
+  public static String run11(MissingClass missingClass) {
+    if (System.currentTimeMillis() == 0) {
+      missingClass.instanceMethod();
+      return "foo";
+    }
+    return "bar";
+  }
+
+  public static String run12(MissingClass missingClass) {
+    if (System.currentTimeMillis() == 0) {
+      missingClass.instanceMethod();
+      return "foo";
+    }
+    return "bar";
+  }
+
+  public static String run13(MissingClass missingClass) {
+    if (System.currentTimeMillis() == 0) {
+      missingClass.instanceMethod();
+      return "foo";
+    }
+    return "bar";
+  }
+
+  public static String run14(MissingClass missingClass) {
+    if (System.currentTimeMillis() == 0) {
+      missingClass.instanceMethod();
+      return "foo";
+    }
+    return "bar";
+  }
+
+  public static String run15(MissingClass missingClass) {
+    if (System.currentTimeMillis() == 0) {
+      missingClass.instanceMethod();
+      return "foo";
+    }
+    return "bar";
+  }
+
+  public static String run16(MissingClass missingClass) {
+    if (System.currentTimeMillis() == 0) {
+      missingClass.instanceMethod();
+      return "foo";
+    }
+    return "bar";
+  }
+
+  public static String run17(MissingClass missingClass) {
+    if (System.currentTimeMillis() == 0) {
+      missingClass.instanceMethod();
+      return "foo";
+    }
+    return "bar";
+  }
+
+  public static String run18(MissingClass missingClass) {
+    if (System.currentTimeMillis() == 0) {
+      missingClass.instanceMethod();
+      return "foo";
+    }
+    return "bar";
+  }
+
+  public static String run19(MissingClass missingClass) {
+    if (System.currentTimeMillis() == 0) {
+      missingClass.instanceMethod();
+      return "foo";
+    }
+    return "bar";
+  }
+
+  public static String run20(MissingClass missingClass) {
+    if (System.currentTimeMillis() == 0) {
+      missingClass.instanceMethod();
+      return "foo";
+    }
+    return "bar";
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/softverification/TestRunner.java b/src/test/java/com/android/tools/r8/softverification/TestRunner.java
index dbe5ab1..a29ad21 100644
--- a/src/test/java/com/android/tools/r8/softverification/TestRunner.java
+++ b/src/test/java/com/android/tools/r8/softverification/TestRunner.java
@@ -41,12 +41,18 @@
     measure.start("StaticField");
     TestStaticField.run();
     sb.append(measure.stop());
+    measure.start("StaticFieldWithOtherMembers");
+    TestStaticFieldWithOtherMembers.run();
+    sb.append(measure.stop());
     measure.start("StaticMethod");
     TestStaticMethod.run();
     sb.append(measure.stop());
     measure.start("InstanceField");
     TestInstanceField.run();
     sb.append(measure.stop());
+    measure.start("InstanceFieldWithOtherMembers");
+    TestInstanceFieldWithOtherMembers.run();
+    sb.append(measure.stop());
     measure.start("HashCode");
     TestHashCode.run();
     sb.append(measure.stop());
diff --git a/src/test/java/com/android/tools/r8/softverification/TestRunnerBuilder.java b/src/test/java/com/android/tools/r8/softverification/TestRunnerBuilder.java
index c3f91db..c842e42 100644
--- a/src/test/java/com/android/tools/r8/softverification/TestRunnerBuilder.java
+++ b/src/test/java/com/android/tools/r8/softverification/TestRunnerBuilder.java
@@ -72,9 +72,10 @@
         .build();
   }
 
-  private static final Path ANDROID_STUDIO_LIB_PATH = Paths.get("PATH_TO_PROJECT/libs/library.jar");
+  private static final Path ANDROID_STUDIO_LIB_PATH =
+      Paths.get("path-to-project/app/libs/library.jar");
 
-  private static final int COUNT = 800;
+  private static final int COUNT = 400;
 
   private static final List<Class<?>> referenceClasses =
       ImmutableList.of(
@@ -86,8 +87,10 @@
           TestTypeReference.class,
           TestNewInstance.class,
           TestStaticField.class,
+          TestStaticFieldWithOtherMembers.class,
           TestStaticMethod.class,
           TestInstanceField.class,
+          TestInstanceFieldWithOtherMembers.class,
           TestInstanceMethod.class,
           TestHashCode.class,
           TestTryCatch.class);
diff --git a/src/test/java/com/android/tools/r8/softverification/TestStaticFieldWithOtherMembers.java b/src/test/java/com/android/tools/r8/softverification/TestStaticFieldWithOtherMembers.java
new file mode 100644
index 0000000..be3d6b0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/softverification/TestStaticFieldWithOtherMembers.java
@@ -0,0 +1,182 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.softverification;
+
+public class TestStaticFieldWithOtherMembers {
+
+  public static String run() {
+    if (System.currentTimeMillis() == 0) {
+      System.out.println(MissingClass.staticField);
+    }
+    if (System.currentTimeMillis() == 0) {
+      System.out.println(MissingClass.staticField);
+    }
+    String currentString = "foobar";
+    for (int i = 0; i < 10; i++) {
+      currentString = "foobar" + (i + currentString.length());
+    }
+    return currentString;
+  }
+
+  public static String run1(MissingClass missingClass) {
+    if (System.currentTimeMillis() == 0) {
+      missingClass.instanceMethod();
+      return "foo";
+    }
+    return "bar";
+  }
+
+  public static String run2(MissingClass missingClass) {
+    if (System.currentTimeMillis() == 0) {
+      missingClass.instanceMethod();
+      return "foo";
+    }
+    return "bar";
+  }
+
+  public static String run3(MissingClass missingClass) {
+    if (System.currentTimeMillis() == 0) {
+      missingClass.instanceMethod();
+      return "foo";
+    }
+    return "bar";
+  }
+
+  public static String run4(MissingClass missingClass) {
+    if (System.currentTimeMillis() == 0) {
+      missingClass.instanceMethod();
+      return "foo";
+    }
+    return "bar";
+  }
+
+  public static String run5(MissingClass missingClass) {
+    if (System.currentTimeMillis() == 0) {
+      missingClass.instanceMethod();
+      return "foo";
+    }
+    return "bar";
+  }
+
+  public static String run6(MissingClass missingClass) {
+    if (System.currentTimeMillis() == 0) {
+      missingClass.instanceMethod();
+      return "foo";
+    }
+    return "bar";
+  }
+
+  public static String run7(MissingClass missingClass) {
+    if (System.currentTimeMillis() == 0) {
+      missingClass.instanceMethod();
+      return "foo";
+    }
+    return "bar";
+  }
+
+  public static String run8(MissingClass missingClass) {
+    if (System.currentTimeMillis() == 0) {
+      missingClass.instanceMethod();
+      return "foo";
+    }
+    return "bar";
+  }
+
+  public static String run9(MissingClass missingClass) {
+    if (System.currentTimeMillis() == 0) {
+      missingClass.instanceMethod();
+      return "foo";
+    }
+    return "bar";
+  }
+
+  public static String run10(MissingClass missingClass) {
+    if (System.currentTimeMillis() == 0) {
+      missingClass.instanceMethod();
+      return "foo";
+    }
+    return "bar";
+  }
+
+  public static String run11(MissingClass missingClass) {
+    if (System.currentTimeMillis() == 0) {
+      missingClass.instanceMethod();
+      return "foo";
+    }
+    return "bar";
+  }
+
+  public static String run12(MissingClass missingClass) {
+    if (System.currentTimeMillis() == 0) {
+      missingClass.instanceMethod();
+      return "foo";
+    }
+    return "bar";
+  }
+
+  public static String run13(MissingClass missingClass) {
+    if (System.currentTimeMillis() == 0) {
+      missingClass.instanceMethod();
+      return "foo";
+    }
+    return "bar";
+  }
+
+  public static String run14(MissingClass missingClass) {
+    if (System.currentTimeMillis() == 0) {
+      missingClass.instanceMethod();
+      return "foo";
+    }
+    return "bar";
+  }
+
+  public static String run15(MissingClass missingClass) {
+    if (System.currentTimeMillis() == 0) {
+      missingClass.instanceMethod();
+      return "foo";
+    }
+    return "bar";
+  }
+
+  public static String run16(MissingClass missingClass) {
+    if (System.currentTimeMillis() == 0) {
+      missingClass.instanceMethod();
+      return "foo";
+    }
+    return "bar";
+  }
+
+  public static String run17(MissingClass missingClass) {
+    if (System.currentTimeMillis() == 0) {
+      missingClass.instanceMethod();
+      return "foo";
+    }
+    return "bar";
+  }
+
+  public static String run18(MissingClass missingClass) {
+    if (System.currentTimeMillis() == 0) {
+      missingClass.instanceMethod();
+      return "foo";
+    }
+    return "bar";
+  }
+
+  public static String run19(MissingClass missingClass) {
+    if (System.currentTimeMillis() == 0) {
+      missingClass.instanceMethod();
+      return "foo";
+    }
+    return "bar";
+  }
+
+  public static String run20(MissingClass missingClass) {
+    if (System.currentTimeMillis() == 0) {
+      missingClass.instanceMethod();
+      return "foo";
+    }
+    return "bar";
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/startup/MinimalStartupDexTest.java b/src/test/java/com/android/tools/r8/startup/MinimalStartupDexTest.java
index a433265..35df0b6 100644
--- a/src/test/java/com/android/tools/r8/startup/MinimalStartupDexTest.java
+++ b/src/test/java/com/android/tools/r8/startup/MinimalStartupDexTest.java
@@ -15,7 +15,6 @@
 import com.android.tools.r8.experimental.startup.StartupItem;
 import com.android.tools.r8.references.ClassReference;
 import com.android.tools.r8.references.MethodReference;
-import com.android.tools.r8.startup.StartupSyntheticPlacementTest.Main;
 import com.android.tools.r8.startup.utils.StartupTestingUtils;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -49,7 +48,8 @@
     List<StartupItem<ClassReference, MethodReference, ?>> startupList = new ArrayList<>();
     testForD8(parameters.getBackend())
         .addInnerClasses(getClass())
-        .apply(StartupTestingUtils.enableStartupInstrumentationUsingLogcat(parameters))
+        .apply(
+            StartupTestingUtils.enableStartupInstrumentationForOriginalAppUsingLogcat(parameters))
         .release()
         .setMinApi(parameters.getApiLevel())
         .compile()
diff --git a/src/test/java/com/android/tools/r8/startup/StartupInstrumentationTest.java b/src/test/java/com/android/tools/r8/startup/StartupInstrumentationTest.java
index 07f4487..bc0292f 100644
--- a/src/test/java/com/android/tools/r8/startup/StartupInstrumentationTest.java
+++ b/src/test/java/com/android/tools/r8/startup/StartupInstrumentationTest.java
@@ -53,8 +53,8 @@
         .addInnerClasses(getClass())
         .applyIf(
             logcat,
-            StartupTestingUtils.enableStartupInstrumentationUsingLogcat(parameters),
-            StartupTestingUtils.enableStartupInstrumentationUsingFile(parameters))
+            StartupTestingUtils.enableStartupInstrumentationForOriginalAppUsingLogcat(parameters),
+            StartupTestingUtils.enableStartupInstrumentationForOriginalAppUsingFile(parameters))
         .release()
         .setMinApi(parameters.getApiLevel())
         .compile()
diff --git a/src/test/java/com/android/tools/r8/startup/StartupSyntheticPlacementTest.java b/src/test/java/com/android/tools/r8/startup/StartupSyntheticPlacementTest.java
index 0cce865..179810b 100644
--- a/src/test/java/com/android/tools/r8/startup/StartupSyntheticPlacementTest.java
+++ b/src/test/java/com/android/tools/r8/startup/StartupSyntheticPlacementTest.java
@@ -11,12 +11,16 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
+import com.android.tools.r8.D8TestCompileResult;
+import com.android.tools.r8.R8TestCompileResult;
 import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestCompilerBuilder;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.experimental.startup.StartupClass;
 import com.android.tools.r8.experimental.startup.StartupItem;
 import com.android.tools.r8.experimental.startup.StartupMethod;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.ir.desugar.LambdaClass;
 import com.android.tools.r8.references.ClassReference;
 import com.android.tools.r8.references.MethodReference;
 import com.android.tools.r8.references.Reference;
@@ -26,8 +30,11 @@
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.MethodReferenceUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
 import com.google.common.collect.ImmutableList;
+import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
@@ -64,35 +71,89 @@
   }
 
   @Test
-  public void test() throws Exception {
+  public void testLayoutUsingD8() throws Exception {
+    // First build the app using R8.
+    R8TestCompileResult r8CompileResult =
+        testForR8(parameters.getBackend())
+            .addInnerClasses(getClass())
+            .addKeepMainRule(Main.class)
+            .addKeepClassAndMembersRules(A.class, B.class, C.class)
+            .addDontOptimize()
+            .setMinApi(parameters.getApiLevel())
+            .compile();
+
+    // Verify that the build works.
+    r8CompileResult
+        .run(parameters.getRuntime(), Main.class, Boolean.toString(useLambda))
+        .assertSuccessWithOutputLines(getExpectedOutput());
+
+    Path optimizedApp = r8CompileResult.writeToZip();
+
+    // Then instrument the app to generate a startup list for the minified app.
     List<StartupItem<ClassReference, MethodReference, ?>> startupList = new ArrayList<>();
     testForD8(parameters.getBackend())
-        .addInnerClasses(getClass())
-        .apply(StartupTestingUtils.enableStartupInstrumentationUsingLogcat(parameters))
+        .addProgramFiles(optimizedApp)
+        .apply(
+            StartupTestingUtils.enableStartupInstrumentationForOptimizedAppUsingLogcat(parameters))
         .release()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .addRunClasspathFiles(StartupTestingUtils.getAndroidUtilLog(temp))
         .run(parameters.getRuntime(), Main.class, Boolean.toString(useLambda))
         .apply(StartupTestingUtils.removeStartupListFromStdout(startupList::add))
-        .assertSuccessWithOutputLines(getExpectedOutput());
-    assertEquals(getExpectedStartupList(), startupList);
+        .assertSuccessWithOutputLines(getExpectedOutput())
+        .apply(
+            runResult ->
+                assertEquals(
+                    getExpectedStartupList(r8CompileResult.inspector(), false), startupList));
 
+    // Finally rebuild the minified app using D8 and the startup list.
+    testForD8(parameters.getBackend())
+        .addProgramFiles(optimizedApp)
+        .apply(
+            testBuilder ->
+                configureStartupOptions(testBuilder, r8CompileResult.inspector(), startupList))
+        .release()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .inspectMultiDex(
+            r8CompileResult.writeProguardMap(), this::inspectPrimaryDex, this::inspectSecondaryDex)
+        .run(parameters.getRuntime(), Main.class, Boolean.toString(useLambda))
+        .assertSuccessWithOutputLines(getExpectedOutput());
+  }
+
+  @Test
+  public void testLayoutUsingR8() throws Exception {
+    // First generate a startup list for the original app.
+    List<StartupItem<ClassReference, MethodReference, ?>> startupList = new ArrayList<>();
+    D8TestCompileResult instrumentationCompileResult =
+        testForD8(parameters.getBackend())
+            .addInnerClasses(getClass())
+            .apply(
+                StartupTestingUtils.enableStartupInstrumentationForOriginalAppUsingLogcat(
+                    parameters))
+            .release()
+            .setMinApi(parameters.getApiLevel())
+            .compile();
+
+    instrumentationCompileResult
+        .addRunClasspathFiles(StartupTestingUtils.getAndroidUtilLog(temp))
+        .run(parameters.getRuntime(), Main.class, Boolean.toString(useLambda))
+        .apply(StartupTestingUtils.removeStartupListFromStdout(startupList::add))
+        .assertSuccessWithOutputLines(getExpectedOutput())
+        .apply(
+            runResult ->
+                assertEquals(getExpectedStartupList(runResult.inspector(), true), startupList));
+
+    // Then build the app using the startup list that is based on original names.
     testForR8(parameters.getBackend())
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addKeepClassAndMembersRules(A.class, B.class, C.class)
-        .addOptionsModification(
-            options -> {
-              options
-                  .getStartupOptions()
-                  .setEnableMinimalStartupDex(enableMinimalStartupDex)
-                  .setEnableStartupCompletenessCheckForTesting(enableStartupCompletenessCheck);
-              options
-                  .getTestingOptions()
-                  .setMixedSectionLayoutStrategyInspector(getMixedSectionLayoutInspector());
-            })
-        .apply(testBuilder -> StartupTestingUtils.setStartupConfiguration(testBuilder, startupList))
+        .apply(
+            testBuilder ->
+                configureStartupOptions(
+                    testBuilder, instrumentationCompileResult.inspector(), startupList))
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspectMultiDex(this::inspectPrimaryDex, this::inspectSecondaryDex)
@@ -103,12 +164,32 @@
             runResult -> runResult.assertSuccessWithOutputLines(getExpectedOutput()));
   }
 
+  private void configureStartupOptions(
+      TestCompilerBuilder<?, ?, ?, ?, ?> testBuilder,
+      CodeInspector inspector,
+      List<StartupItem<ClassReference, MethodReference, ?>> startupList) {
+    testBuilder
+        .addOptionsModification(
+            options -> {
+              options
+                  .getStartupOptions()
+                  .setEnableMinimalStartupDex(enableMinimalStartupDex)
+                  .setEnableStartupCompletenessCheckForTesting(enableStartupCompletenessCheck);
+              options
+                  .getTestingOptions()
+                  .setMixedSectionLayoutStrategyInspector(
+                      getMixedSectionLayoutInspector(inspector));
+            })
+        .apply(ignore -> StartupTestingUtils.setStartupConfiguration(testBuilder, startupList));
+  }
+
   private List<String> getExpectedOutput() {
     return ImmutableList.of("A", "B", "C");
   }
 
-  private List<StartupItem<ClassReference, MethodReference, ?>> getExpectedStartupList()
-      throws NoSuchMethodException {
+  @SuppressWarnings("unchecked")
+  private List<StartupItem<ClassReference, MethodReference, ?>> getExpectedStartupList(
+      CodeInspector inspector, boolean isStartupListForOriginalApp) throws NoSuchMethodException {
     ImmutableList.Builder<StartupItem<ClassReference, MethodReference, ?>> builder =
         ImmutableList.builder();
     builder.add(
@@ -132,11 +213,56 @@
                 Reference.methodFromMethod(B.class.getDeclaredMethod("b", boolean.class)))
             .build());
     if (useLambda) {
+      if (isStartupListForOriginalApp) {
+        builder.add(
+            StartupClass.referenceBuilder()
+                .setClassReference(Reference.classFromClass(B.class))
+                .setSynthetic()
+                .build());
+      } else {
+        ClassSubject bClassSubject = inspector.clazz(B.class);
+
+        MethodSubject syntheticLambdaAccessorMethod =
+            bClassSubject.uniqueMethodThatMatches(
+                method ->
+                    method
+                        .getOriginalName()
+                        .startsWith(LambdaClass.R8_LAMBDA_ACCESSOR_METHOD_PREFIX));
+        assertThat(syntheticLambdaAccessorMethod, isPresent());
+
+        ClassSubject externalSyntheticLambdaClassSubject =
+            inspector.clazz(getSyntheticLambdaClassReference());
+        assertThat(externalSyntheticLambdaClassSubject, isPresent());
+
+        ClassReference externalSyntheticLambdaClassReference =
+            externalSyntheticLambdaClassSubject.getFinalReference();
+
+        builder.add(
+            StartupClass.referenceBuilder()
+                .setClassReference(externalSyntheticLambdaClassReference)
+                .build(),
+            StartupMethod.referenceBuilder()
+                .setMethodReference(
+                    MethodReferenceUtils.instanceConstructor(externalSyntheticLambdaClassReference))
+                .build(),
+            StartupMethod.referenceBuilder()
+                .setMethodReference(
+                    Reference.method(
+                        externalSyntheticLambdaClassReference,
+                        "accept",
+                        ImmutableList.of(Reference.classFromClass(Object.class)),
+                        null))
+                .build(),
+            StartupMethod.referenceBuilder()
+                .setMethodReference(
+                    Reference.method(
+                        Reference.classFromClass(B.class),
+                        syntheticLambdaAccessorMethod.getFinalName(),
+                        ImmutableList.of(Reference.classFromClass(Object.class)),
+                        null))
+                .build());
+      }
       builder.add(
-          StartupClass.referenceBuilder()
-              .setClassReference(Reference.classFromClass(B.class))
-              .setSynthetic()
-              .build(),
           StartupMethod.referenceBuilder()
               .setMethodReference(
                   Reference.methodFromMethod(B.class.getDeclaredMethod("lambda$b$0", Object.class)))
@@ -152,7 +278,10 @@
     return builder.build();
   }
 
-  private List<ClassReference> getExpectedClassDataLayout(int virtualFile) {
+  private List<ClassReference> getExpectedClassDataLayout(
+      CodeInspector inspector, int virtualFile) {
+    ClassSubject syntheticLambdaClassSubject = inspector.clazz(getSyntheticLambdaClassReference());
+
     // The synthetic lambda should only be placed alongside its synthetic context (B) if it is used.
     // Otherwise, it should be last, or in the second dex file if compiling with minimal startup.
     ImmutableList.Builder<ClassReference> layoutBuilder = ImmutableList.builder();
@@ -162,23 +291,24 @@
           Reference.classFromClass(A.class),
           Reference.classFromClass(B.class));
       if (useLambda) {
-        layoutBuilder.add(getSyntheticLambdaClassReference());
+        layoutBuilder.add(syntheticLambdaClassSubject.getFinalReference());
       }
       layoutBuilder.add(Reference.classFromClass(C.class));
     }
     if (!useLambda) {
       if (!enableMinimalStartupDex || virtualFile == 1) {
-        layoutBuilder.add(getSyntheticLambdaClassReference());
+        layoutBuilder.add(syntheticLambdaClassSubject.getFinalReference());
       }
     }
     return layoutBuilder.build();
   }
 
-  private MixedSectionLayoutInspector getMixedSectionLayoutInspector() {
+  private MixedSectionLayoutInspector getMixedSectionLayoutInspector(CodeInspector inspector) {
     return new MixedSectionLayoutInspector() {
       @Override
       public void inspectClassDataLayout(int virtualFile, Collection<DexProgramClass> layout) {
-        assertThat(layout, isEqualToClassDataLayout(getExpectedClassDataLayout(virtualFile)));
+        assertThat(
+            layout, isEqualToClassDataLayout(getExpectedClassDataLayout(inspector, virtualFile)));
       }
     };
   }
diff --git a/src/test/java/com/android/tools/r8/startup/StartupSyntheticWithoutContextTest.java b/src/test/java/com/android/tools/r8/startup/StartupSyntheticWithoutContextTest.java
index 13c7ca7..b9ceea2 100644
--- a/src/test/java/com/android/tools/r8/startup/StartupSyntheticWithoutContextTest.java
+++ b/src/test/java/com/android/tools/r8/startup/StartupSyntheticWithoutContextTest.java
@@ -64,7 +64,8 @@
     List<StartupItem<ClassReference, MethodReference, ?>> startupList = new ArrayList<>();
     testForD8(parameters.getBackend())
         .addInnerClasses(getClass())
-        .apply(StartupTestingUtils.enableStartupInstrumentationUsingLogcat(parameters))
+        .apply(
+            StartupTestingUtils.enableStartupInstrumentationForOriginalAppUsingLogcat(parameters))
         .release()
         .setMinApi(parameters.getApiLevel())
         .compile()
diff --git a/src/test/java/com/android/tools/r8/startup/utils/StartupTestingUtils.java b/src/test/java/com/android/tools/r8/startup/utils/StartupTestingUtils.java
index ae31fe8..ec7e59f 100644
--- a/src/test/java/com/android/tools/r8/startup/utils/StartupTestingUtils.java
+++ b/src/test/java/com/android/tools/r8/startup/utils/StartupTestingUtils.java
@@ -10,14 +10,15 @@
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.D8TestBuilder;
 import com.android.tools.r8.D8TestRunResult;
-import com.android.tools.r8.R8TestBuilder;
+import com.android.tools.r8.StartupProfileProvider;
 import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestCompilerBuilder;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ThrowableConsumer;
-import com.android.tools.r8.experimental.startup.StartupConfiguration;
 import com.android.tools.r8.experimental.startup.StartupConfigurationParser;
 import com.android.tools.r8.experimental.startup.StartupItem;
-import com.android.tools.r8.experimental.startup.StartupOptions;
+import com.android.tools.r8.experimental.startup.StartupProfile;
+import com.android.tools.r8.experimental.startup.instrumentation.StartupInstrumentationOptions;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
@@ -40,25 +41,54 @@
 
   private static String startupInstrumentationTag = "startup";
 
-  public static ThrowableConsumer<D8TestBuilder> enableStartupInstrumentationUsingFile(
-      TestParameters parameters) {
-    return testBuilder -> enableStartupInstrumentation(testBuilder, parameters, false);
+  private enum AppVariant {
+    ORIGINAL,
+    OPTIMIZED;
+
+    boolean isOriginal() {
+      return this == ORIGINAL;
+    }
   }
 
-  public static ThrowableConsumer<D8TestBuilder> enableStartupInstrumentationUsingLogcat(
-      TestParameters parameters) {
-    return testBuilder -> enableStartupInstrumentation(testBuilder, parameters, true);
+  public static ThrowableConsumer<D8TestBuilder>
+      enableStartupInstrumentationForOriginalAppUsingFile(TestParameters parameters) {
+    return testBuilder ->
+        enableStartupInstrumentation(testBuilder, parameters, AppVariant.ORIGINAL, false);
+  }
+
+  public static ThrowableConsumer<D8TestBuilder>
+      enableStartupInstrumentationForOriginalAppUsingLogcat(TestParameters parameters) {
+    return testBuilder ->
+        enableStartupInstrumentation(testBuilder, parameters, AppVariant.ORIGINAL, true);
+  }
+
+  public static ThrowableConsumer<D8TestBuilder>
+      enableStartupInstrumentationForOptimizedAppUsingFile(TestParameters parameters) {
+    return testBuilder ->
+        enableStartupInstrumentation(testBuilder, parameters, AppVariant.OPTIMIZED, false);
+  }
+
+  public static ThrowableConsumer<D8TestBuilder>
+      enableStartupInstrumentationForOptimizedAppUsingLogcat(TestParameters parameters) {
+    return testBuilder ->
+        enableStartupInstrumentation(testBuilder, parameters, AppVariant.OPTIMIZED, true);
   }
 
   private static void enableStartupInstrumentation(
-      D8TestBuilder testBuilder, TestParameters parameters, boolean logcat) throws IOException {
+      D8TestBuilder testBuilder, TestParameters parameters, AppVariant appVariant, boolean logcat)
+      throws IOException {
     testBuilder
         .addOptionsModification(
             options -> {
-              StartupOptions startupOptions =
-                  options.getStartupOptions().setEnableStartupInstrumentation();
+              StartupInstrumentationOptions startupInstrumentationOptions =
+                  options
+                      .getStartupInstrumentationOptions()
+                      .setEnableStartupInstrumentation()
+                      .setEnableGeneralizationOfSyntheticsToSyntheticContext(
+                          appVariant.isOriginal());
               if (logcat) {
-                startupOptions.setStartupInstrumentationTag(startupInstrumentationTag);
+                startupInstrumentationOptions.setStartupInstrumentationTag(
+                    startupInstrumentationTag);
               }
             })
         .addLibraryFiles(parameters.getDefaultRuntimeLibrary())
@@ -113,13 +143,13 @@
   }
 
   public static void setStartupConfiguration(
-      R8TestBuilder<?> testBuilder,
+      TestCompilerBuilder<?, ?, ?, ?, ?> testBuilder,
       List<StartupItem<ClassReference, MethodReference, ?>> startupItems) {
     testBuilder.addOptionsModification(
         options -> {
           DexItemFactory dexItemFactory = options.dexItemFactory();
-          StartupConfiguration startupConfiguration =
-              StartupConfiguration.builder()
+          StartupProfile startupProfile =
+              StartupProfile.builder()
                   .apply(
                       builder ->
                           startupItems.forEach(
@@ -127,7 +157,8 @@
                                   builder.addStartupItem(
                                       convertStartupItemToDex(startupItem, dexItemFactory))))
                   .build();
-          options.getStartupOptions().setStartupConfiguration(startupConfiguration);
+          StartupProfileProvider startupProfileProvider = startupProfile::serializeToString;
+          options.getStartupOptions().setStartupProfileProvider(startupProfileProvider);
         });
   }
 
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeMatchers.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeMatchers.java
index 50a77fe..db36bbc 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeMatchers.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeMatchers.java
@@ -4,8 +4,8 @@
 
 package com.android.tools.r8.utils.codeinspector;
 
-import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.references.FieldReference;
 import java.util.Arrays;
 import java.util.List;
 import java.util.function.Predicate;
@@ -20,7 +20,10 @@
     if (!targetSubject.isPresent()) {
       throw new IllegalArgumentException();
     }
-    DexField target = targetSubject.getField().getReference();
+    return accessesField(targetSubject.getField().getReference().asFieldReference());
+  }
+
+  public static Matcher<MethodSubject> accessesField(FieldReference target) {
     return new TypeSafeMatcher<MethodSubject>() {
       @Override
       protected boolean matchesSafely(MethodSubject subject) {
@@ -35,7 +38,7 @@
 
       @Override
       public void describeTo(Description description) {
-        description.appendText("accesses field `" + target.toSourceString() + "`");
+        description.appendText("accesses field `" + target.toString() + "`");
       }
 
       @Override
@@ -232,7 +235,8 @@
     };
   }
 
-  public static Predicate<InstructionSubject> isFieldAccessWithTarget(DexField target) {
-    return instruction -> instruction.isFieldAccess() && instruction.getField() == target;
+  public static Predicate<InstructionSubject> isFieldAccessWithTarget(FieldReference target) {
+    return instruction ->
+        instruction.isFieldAccess() && instruction.getField().asFieldReference().equals(target);
   }
 }
diff --git a/third_party/android_jar/lib-master.tar.gz.sha1 b/third_party/android_jar/lib-master.tar.gz.sha1
new file mode 100644
index 0000000..717d805
--- /dev/null
+++ b/third_party/android_jar/lib-master.tar.gz.sha1
@@ -0,0 +1 @@
+a759a6727431a73b4f461ed40aa5bfbc79ebc172
\ No newline at end of file
diff --git a/third_party/bundletool/bundletool-1.11.0.tar.gz.sha1 b/third_party/bundletool/bundletool-1.11.0.tar.gz.sha1
new file mode 100644
index 0000000..15286b5
--- /dev/null
+++ b/third_party/bundletool/bundletool-1.11.0.tar.gz.sha1
@@ -0,0 +1 @@
+0df683c4962e31e12db4bc264d3ed99a9b91d31c
\ No newline at end of file
diff --git a/third_party/openjdk/desugar_jdk_libs_11.tar.gz.sha1 b/third_party/openjdk/desugar_jdk_libs_11.tar.gz.sha1
index 0f2a679..6d54f29 100644
--- a/third_party/openjdk/desugar_jdk_libs_11.tar.gz.sha1
+++ b/third_party/openjdk/desugar_jdk_libs_11.tar.gz.sha1
@@ -1 +1 @@
-cec0d3cf2655ac9c77410ddf87a7b4e5900858a5
\ No newline at end of file
+cbd86c3d046d227e71e8157742b3a1f2e4abcd8e
\ No newline at end of file
diff --git a/tools/linux/README.art-versions b/tools/linux/README.art-versions
index 02e81cd..94d78e1 100644
--- a/tools/linux/README.art-versions
+++ b/tools/linux/README.art-versions
@@ -42,6 +42,29 @@
   <continue with repo sync as above>
 
 
+art-master (Currently Android U)
+--------------------------------
+Build from master, build spec included. Art at 31b98e9ce8fbb56c3507c8e7718881efa1ce9f63.
+
+repo sync -cq -j24
+source build/envsetup.sh
+lunch aosp_redfin-userdebug
+m -j48
+m -j48 build-art
+m -j48 test-art-host
+repo manifest -r -o build_spec.xml
+
+Collected into tools/linux/host/art-master. The "host" path element is checked
+by the script for running Art.
+
+  cd <r8 checkout>
+  scripts/update-host-art.sh \
+     --android-checkout <...>/android/tm-dev \
+     --art-dir host/art-master \
+     --android-product redfin
+
+(cd tools/linux/host; upload_to_google_storage.py -a --bucket r8-deps art-master)
+
 art-13 (Android T)
 ------------------
 Build from tm-dev commit 442e1091f39417c692d91609af05e58af60d8e2b.
diff --git a/tools/linux/host/art-master.tar.gz.sha1 b/tools/linux/host/art-master.tar.gz.sha1
new file mode 100644
index 0000000..096aa8c
--- /dev/null
+++ b/tools/linux/host/art-master.tar.gz.sha1
@@ -0,0 +1 @@
+7c795b752ba1edce6c3e9e11d837d31eb78043a3
\ No newline at end of file
diff --git a/tools/r8_release.py b/tools/r8_release.py
index 56828c0..f7f0c15 100755
--- a/tools/r8_release.py
+++ b/tools/r8_release.py
@@ -389,9 +389,9 @@
         result = re.search(version_match_regexp, line)
         if result:
           match_count = match_count + 1
-      if match_count != 3:
+      if match_count != 4:
         print(("Could not find the previous -dev release string to replace in " +
-            "METADATA. Expected to find is mentioned 3 times. Please update %s " +
+            "METADATA. Expected to find is mentioned 4 times. Please update %s " +
             "manually and run again with options --google3 " +
             "--use-existing-work-branch.") % metadata_path)
         sys.exit(1)
diff --git a/tools/run_on_app_dump.py b/tools/run_on_app_dump.py
index 4f22dc5..4d19948 100755
--- a/tools/run_on_app_dump.py
+++ b/tools/run_on_app_dump.py
@@ -54,6 +54,7 @@
       'skip_recompilation': False,
       'compiler_properties': [],
       'internal': False,
+      'golem_duration': None,
     }
     # This below does not work in python3
     defaults.update(fields.items())
@@ -362,6 +363,7 @@
     'url': 'https://github.com/android/compose-samples',
     'revision': '779cf9e187b8ee2c6b620b2abb4524719b3f10f8',
     'folder': 'android/compose-samples/crane',
+    'golem_duration': 240
   }),
   # TODO(b/173167253): Check if monkey testing works.
   App({
@@ -1062,6 +1064,13 @@
       print_indented(
           'StandardBenchmark(name, [Metric.RunTimeRaw, Metric.CodeSize]);',
           indentation + 4)
+      if app.golem_duration != None:
+        print_indented(
+            'final timeout = const Duration(seconds: %s);' % app.golem_duration,
+            indentation)
+        print_indented(
+            'ExecutionManagement.addTimeoutConstraint'
+            '(timeout, benchmark: benchmark);', indentation)
       app_gz = os.path.join(utils.OPENSOURCE_DUMPS_DIR, app.folder + '.tar.gz')
       name = 'appResource'
       add_golem_resource(indentation, app_gz, name)
diff --git a/tools/startup/adb_utils.py b/tools/startup/adb_utils.py
index 2369aa7..60a7ee3 100755
--- a/tools/startup/adb_utils.py
+++ b/tools/startup/adb_utils.py
@@ -14,6 +14,7 @@
 
 sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
 
+import profile_utils
 import utils
 
 DEVNULL=subprocess.DEVNULL
@@ -56,6 +57,24 @@
   cmd = create_adb_cmd('shell am broadcast -a %s %s' % (action, component), device_id)
   return subprocess.check_output(cmd).decode('utf-8').strip().splitlines()
 
+def build_apks_from_bundle(bundle, output, overwrite=False):
+  print('Building %s' % bundle)
+  cmd = [
+      'java', '-jar', utils.BUNDLETOOL_JAR,
+      'build-apks',
+      '--bundle=%s' % bundle,
+      '--output=%s' % output]
+  if overwrite:
+    cmd.append('--overwrite')
+  subprocess.check_call(cmd, stdout=DEVNULL, stderr=DEVNULL)
+
+def capture_screen(target, device_id=None):
+  print('Taking screenshot to %s' % target)
+  tmp = '/sdcard/screencap.png'
+  cmd = create_adb_cmd('shell screencap -p %s' % tmp, device_id)
+  subprocess.check_call(cmd, stdout=DEVNULL, stderr=DEVNULL)
+  pull(tmp, target, device_id)
+
 def create_adb_cmd(arguments, device_id=None):
   assert isinstance(arguments, list) or isinstance(arguments, str)
   cmd = ['adb']
@@ -135,8 +154,7 @@
   with utils.TempDir() as temp:
     source = get_profile_path(app_id)
     target = os.path.join(temp, 'primary.prof')
-    cmd = create_adb_cmd('pull %s %s' % (source, target), device_id)
-    subprocess.check_call(cmd, stdout=DEVNULL, stderr=DEVNULL)
+    pull(source, target, device_id)
     with open(target, 'rb') as f:
       return f.read()
 
@@ -182,38 +200,13 @@
 def get_classes_and_methods_from_app_profile(app_id, device_id=None):
   apk_path = get_apk_path(app_id, device_id)
   profile_path = get_profile_path(app_id)
-
-  # Generates a list of class and method descriptors, prefixed with one or more
-  # flags 'H' (hot), 'S' (startup), 'P' (post startup).
-  #
-  # Example:
-  #
-  # HSPLandroidx/compose/runtime/ComposerImpl;->updateValue(Ljava/lang/Object;)V
-  # HSPLandroidx/compose/runtime/ComposerImpl;->updatedNodeCount(I)I
-  # HLandroidx/compose/runtime/ComposerImpl;->validateNodeExpected()V
-  # PLandroidx/compose/runtime/CompositionImpl;->applyChanges()V
-  # HLandroidx/compose/runtime/ComposerKt;->findLocation(Ljava/util/List;I)I
-  # Landroidx/compose/runtime/ComposerImpl;
-  #
-  # See also https://developer.android.com/studio/profile/baselineprofiles.
   cmd = create_adb_cmd(
     'shell profman --dump-classes-and-methods'
     ' --profile-file=%s --apk=%s --dex-location=%s'
         % (profile_path, apk_path, apk_path), device_id)
   stdout = subprocess.check_output(cmd).decode('utf-8').strip()
   lines = stdout.splitlines()
-  classes_and_methods = []
-  flags_to_name = { 'H': 'hot', 'S': 'startup', 'P': 'post_startup' }
-  for line in lines:
-    flags = { 'hot': False, 'startup': False, 'post_startup': False }
-    while line[0] in flags_to_name:
-      flag_abbreviation = line[0]
-      flag_name = flags_to_name.get(flag_abbreviation)
-      flags[flag_name] = True
-      line = line[1:]
-    assert line.startswith('L')
-    classes_and_methods.append({ 'descriptor': line, 'flags': flags })
-  return classes_and_methods
+  return profile_utils.parse_art_profile(lines)
 
 def get_screen_off_timeout(device_id=None):
   cmd = create_adb_cmd(
@@ -229,6 +222,33 @@
   stdout = subprocess.check_output(cmd).decode('utf-8')
   assert 'Success' in stdout
 
+def install_apks(apks, device_id=None, max_attempts=3):
+  print('Installing %s' % apks)
+  cmd = [
+      'java', '-jar', utils.BUNDLETOOL_JAR,
+      'install-apks',
+      '--apks=%s' % apks]
+  if device_id is not None:
+    cmd.append('--device-id=%s' % device_id)
+  for i in range(max_attempts):
+    process_result = subprocess.run(cmd, capture_output=True)
+    stdout = process_result.stdout.decode('utf-8')
+    stderr = process_result.stderr.decode('utf-8')
+    if process_result.returncode == 0:
+      return
+    print('Failed to install %s' % apks)
+    print('Stdout: %s' % stdout)
+    print('Stderr: %s' % stderr)
+    print('Retrying...')
+  raise Exception('Unable to install apks in %s attempts' % max_attempts)
+
+def install_bundle(bundle, device_id=None):
+  print('Installing %s' % bundle)
+  with utils.TempDir() as temp:
+    apks = os.path.join(temp, 'Bundle.apks')
+    build_apks_from_bundle(bundle, apks)
+    install_apks(apks, device_id)
+
 def install_profile(app_id, device_id=None):
   # This assumes that the profileinstaller library has been added to the app,
   # https://developer.android.com/jetpack/androidx/releases/profileinstaller.
@@ -267,7 +287,7 @@
       total_time_str = line.removeprefix('TotalTime: ')
       assert total_time_str.isdigit()
       result['total_time'] = int(total_time_str)
-  assert not wait_for_activity_to_launch or 'total_time' in result
+  assert not wait_for_activity_to_launch or 'total_time' in result, lines
   return result
 
 def navigate_to_home_screen(device_id=None):
@@ -288,6 +308,10 @@
   }
   return teardown_options
 
+def pull(source, target, device_id=None):
+  cmd = create_adb_cmd('pull %s %s' % (source, target), device_id)
+  subprocess.check_call(cmd, stdout=DEVNULL, stderr=DEVNULL)
+
 def root(device_id=None):
   cmd = create_adb_cmd('root', device_id)
   subprocess.check_call(cmd, stdout=DEVNULL, stderr=DEVNULL)
@@ -364,6 +388,8 @@
 
 def parse_options(argv):
   result = argparse.ArgumentParser(description='Run adb utils.')
+  result.add_argument('--capture-screen',
+                      help='Capture screen to given file')
   result.add_argument('--device-id',
                       help='Device id (e.g., emulator-5554).')
   result.add_argument('--device-pin',
@@ -385,6 +411,8 @@
 
 def main(argv):
   (options, args) = parse_options(argv)
+  if options.capture_screen:
+    capture_screen(options.capture_screen, options.device_id)
   if options.ensure_screen_off:
     ensure_screen_off(options.device_id)
   elif options.get_screen_state:
diff --git a/tools/startup/generate_startup_descriptors.py b/tools/startup/generate_startup_descriptors.py
index d4a33d0..ee3f27a 100755
--- a/tools/startup/generate_startup_descriptors.py
+++ b/tools/startup/generate_startup_descriptors.py
@@ -4,15 +4,22 @@
 # BSD-style license that can be found in the LICENSE file.
 
 import adb_utils
+import profile_utils
 
 import argparse
 import os
 import sys
 import time
 
-def extend_startup_descriptors(startup_descriptors, iteration, options):
+class Device:
+
+  def __init__(self, device_id, device_pin):
+    self.device_id = device_id
+    self.device_pin = device_pin
+
+def extend_startup_descriptors(startup_descriptors, iteration, device, options):
   (logcat, profile, profile_classes_and_methods) = \
-      generate_startup_profile(options)
+      generate_startup_profile(device, options)
   if options.logcat:
     write_tmp_logcat(logcat, iteration, options)
     current_startup_descriptors = get_r8_startup_descriptors_from_logcat(
@@ -22,47 +29,48 @@
     write_tmp_profile_classes_and_methods(
         profile_classes_and_methods, iteration, options)
     current_startup_descriptors = \
-        transform_classes_and_methods_to_r8_startup_descriptors(
-            profile_classes_and_methods, options)
+        profile_utils.transform_art_profile_to_r8_startup_list(
+            profile_classes_and_methods)
   write_tmp_startup_descriptors(current_startup_descriptors, iteration, options)
   new_startup_descriptors = add_r8_startup_descriptors(
       startup_descriptors, current_startup_descriptors)
-  number_of_new_startup_descriptors = len(new_startup_descriptors) - len(startup_descriptors)
+  number_of_new_startup_descriptors = \
+      len(new_startup_descriptors) - len(startup_descriptors)
   if options.out is not None:
     print(
         'Found %i new startup descriptors in iteration %i'
             % (number_of_new_startup_descriptors, iteration + 1))
   return new_startup_descriptors
 
-def generate_startup_profile(options):
+def generate_startup_profile(device, options):
   logcat = None
   profile = None
   profile_classes_and_methods = None
   if options.use_existing_profile:
     # Verify presence of profile.
-    adb_utils.check_app_has_profile_data(options.app_id, options.device_id)
-    profile = adb_utils.get_profile_data(options.app_id, options.device_id)
+    adb_utils.check_app_has_profile_data(options.app_id, device.device_id)
+    profile = adb_utils.get_profile_data(options.app_id, device.device_id)
     profile_classes_and_methods = \
         adb_utils.get_classes_and_methods_from_app_profile(
-            options.app_id, options.device_id)
+            options.app_id, device.device_id)
   else:
     # Unlock device.
     tear_down_options = adb_utils.prepare_for_interaction_with_device(
-        options.device_id, options.device_pin)
+        device.device_id, device.device_pin)
 
     logcat_process = None
     if options.logcat:
       # Clear logcat and start capturing logcat.
-      adb_utils.clear_logcat(options.device_id)
+      adb_utils.clear_logcat(device.device_id)
       logcat_process = adb_utils.start_logcat(
-          options.device_id, format='tag', filter='r8:I ActivityTaskManager:I *:S')
+          device.device_id, format='tag', filter='r8:I ActivityTaskManager:I *:S')
     else:
       # Clear existing profile data.
-      adb_utils.clear_profile_data(options.app_id, options.device_id)
+      adb_utils.clear_profile_data(options.app_id, device.device_id)
 
     # Launch activity to generate startup profile on device.
     adb_utils.launch_activity(
-        options.app_id, options.main_activity, options.device_id)
+        options.app_id, options.main_activity, device.device_id)
 
     # Wait for activity startup.
     time.sleep(options.startup_duration)
@@ -72,16 +80,16 @@
       logcat = adb_utils.stop_logcat(logcat_process)
     else:
       # Capture startup profile.
-      adb_utils.capture_app_profile_data(options.app_id, options.device_id)
-      profile = adb_utils.get_profile_data(options.app_id, options.device_id)
+      adb_utils.capture_app_profile_data(options.app_id, device.device_id)
+      profile = adb_utils.get_profile_data(options.app_id, device.device_id)
       profile_classes_and_methods = \
           adb_utils.get_classes_and_methods_from_app_profile(
-              options.app_id, options.device_id)
+              options.app_id, device.device_id)
 
     # Shutdown app.
-    adb_utils.stop_app(options.app_id, options.device_id)
+    adb_utils.stop_app(options.app_id, device.device_id)
     adb_utils.teardown_after_interaction_with_device(
-        tear_down_options, options.device_id)
+        tear_down_options, device.device_id)
 
   return (logcat, profile, profile_classes_and_methods)
 
@@ -161,16 +169,6 @@
 def report_unrecognized_logcat_line(line):
   print('Unrecognized line in logcat: %s' % line)
 
-def transform_classes_and_methods_to_r8_startup_descriptors(
-    classes_and_methods, options):
-  startup_descriptors = []
-  for class_or_method in classes_and_methods:
-    descriptor = class_or_method.get('descriptor')
-    flags = class_or_method.get('flags')
-    if should_include_startup_descriptor(descriptor, flags, options):
-      startup_descriptors.append(descriptor)
-  return startup_descriptors
-
 def add_r8_startup_descriptors(old_startup_descriptors, startup_descriptors_to_add):
   new_startup_descriptors = {}
   if len(old_startup_descriptors) == 0:
@@ -252,15 +250,14 @@
 def write_tmp_profile_classes_and_methods(
     profile_classes_and_methods, iteration, options):
   def item_to_string(item):
-    descriptor = item.get('descriptor')
-    flags = item.get('flags')
+    (descriptor, flags) = item
     return '%s%s%s%s' % (
         'H' if flags.get('hot') else '',
         'S' if flags.get('startup') else '',
         'P' if flags.get('post_startup') else '',
         descriptor)
   write_tmp_textual_artifact(
-      profile_classes_and_methods,
+      profile_classes_and_methods.items(),
       iteration,
       options,
       'profile.txt',
@@ -296,14 +293,20 @@
   result = argparse.ArgumentParser(
       description='Generate a perfetto trace file.')
   result.add_argument('--apk',
-                      help='Path to the APK')
+                      help='Path to the .apk')
+  result.add_argument('--apks',
+                      help='Path to the .apks')
   result.add_argument('--app-id',
                       help='The application ID of interest',
                       required=True)
+  result.add_argument('--bundle',
+                      help='Path to the .aab')
   result.add_argument('--device-id',
-                      help='Device id (e.g., emulator-5554).')
+                      help='Device id (e.g., emulator-5554).',
+                      action='append')
   result.add_argument('--device-pin',
-                      help='Device pin code (e.g., 1234)')
+                      help='Device pin code (e.g., 1234)',
+                      action='append')
   result.add_argument('--logcat',
                       action='store_true',
                       default=False)
@@ -348,24 +351,52 @@
                       action='store_true',
                       default=False)
   options, args = result.parse_known_args(argv)
+
+  # Read the device pins.
+  device_pins = options.device_pin or []
+  del options.device_pin
+
+  # Convert the device ids and pins into a list of devices.
+  options.devices = []
+  if options.device_id is None:
+    # Assume a single device is attached.
+    options.devices.append(
+        Device(None, device_pins[0] if len(device_pins) > 0 else None))
+  else:
+    for i in range(len(options.device_id)):
+      device_id = options.device_id[i]
+      device_pin = device_pins[i] if i < len(device_pins) else None
+      options.devices.append(Device(device_id, device_pin))
+  del options.device_id
+
+  paths = [
+      path for path in [options.apk, options.apks, options.bundle]
+      if path is not None]
+  assert len(paths) <= 1, 'Expected at most one .apk, .apks, or .aab file.'
   assert options.main_activity is not None or options.use_existing_profile, \
       'Argument --main-activity is required except when running with ' \
       '--use-existing-profile.'
+
   return options, args
 
-def main(argv):
-  (options, args) = parse_options(argv)
-  adb_utils.root(options.device_id)
+def run_on_device(device, options, startup_descriptors):
+  adb_utils.root(device.device_id)
   if options.apk:
-    adb_utils.uninstall(options.app_id, options.device_id)
-    adb_utils.install(options.apk, options.device_id)
-  startup_descriptors = {}
+    adb_utils.uninstall(options.app_id, device.device_id)
+    adb_utils.install(options.apk, device.device_id)
+  elif options.apks:
+    adb_utils.uninstall(options.app_id, device.device_id)
+    adb_utils.install_apks(options.apks, device.device_id)
+  elif options.bundle:
+    adb_utils.uninstall(options.app_id, device.device_id)
+    adb_utils.install_bundle(options.bundle, device.device_id)
   if options.until_stable:
     iteration = 0
     stable_iterations = 0
     while True:
       old_startup_descriptors = startup_descriptors
-      startup_descriptors = extend_startup_descriptors(old_startup_descriptors, iteration, options)
+      startup_descriptors = extend_startup_descriptors(
+          old_startup_descriptors, iteration, device, options)
       diff = len(startup_descriptors) - len(old_startup_descriptors)
       if diff == 0:
         stable_iterations = stable_iterations + 1
@@ -376,7 +407,15 @@
       iteration = iteration + 1
   else:
     for iteration in range(options.iterations):
-      startup_descriptors = extend_startup_descriptors(startup_descriptors, iteration, options)
+      startup_descriptors = extend_startup_descriptors(
+          startup_descriptors, iteration, device, options)
+  return startup_descriptors
+
+def main(argv):
+  (options, args) = parse_options(argv)
+  startup_descriptors = {}
+  for device in options.devices:
+    startup_descriptors = run_on_device(device, options, startup_descriptors)
   if options.out is not None:
     with open(options.out, 'w') as f:
       for startup_descriptor, flags in startup_descriptors.items():
diff --git a/tools/startup/measure_startup.py b/tools/startup/measure_startup.py
index 04076ec..e00797c 100755
--- a/tools/startup/measure_startup.py
+++ b/tools/startup/measure_startup.py
@@ -47,15 +47,15 @@
       teardown_options['previous_screen_off_timeout'],
       options.device_id)
 
-def run_all(apk, options, tmp_dir):
+def run_all(apk_or_apks, options, tmp_dir):
   # Launch app while collecting information.
   data_total = {}
   for iteration in range(1, options.iterations + 1):
     print('Starting iteration %i' % iteration)
     out_dir = os.path.join(options.out_dir, str(iteration))
-    teardown_options = setup_for_run(apk, out_dir, options)
+    teardown_options = setup_for_run(apk_or_apks, out_dir, options)
     data = run(out_dir, options, tmp_dir)
-    teardown_for_run(options, teardown_options)
+    teardown_for_run(out_dir, options, teardown_options)
     add_data(data_total, data)
     print('Result:')
     print(data)
@@ -76,12 +76,17 @@
     data_summary['%s_med' % key] = statistics.median(value)
   return data_summary
 
-def setup_for_run(apk, out_dir, options):
+def setup_for_run(apk_or_apks, out_dir, options):
   adb_utils.root(options.device_id)
 
   print('Installing')
   adb_utils.uninstall(options.app_id, options.device_id)
-  adb_utils.install(apk, options.device_id)
+  if apk_or_apks['apk']:
+    adb_utils.install(apk_or_apks['apk'], options.device_id)
+  else:
+    assert apk_or_apks['apks']
+    adb_utils.install_apks(apk_or_apks['apks'], options.device_id)
+
   os.makedirs(out_dir, exist_ok=True)
 
   # AOT compile.
@@ -119,9 +124,13 @@
   adb_utils.drop_caches(options.device_id)
   return teardown_options
 
-def teardown_for_run(options, teardown_options):
+def teardown_for_run(out_dir, options, teardown_options):
   assert adb_utils.get_screen_state(options.device_id).is_on_and_unlocked()
 
+  if options.capture_screen:
+    target = os.path.join(out_dir, 'screen.png')
+    adb_utils.capture_screen(target, options.device_id)
+
   if options.cooldown > 0:
     adb_utils.teardown_after_interaction_with_device(
         teardown_options, options.device_id)
@@ -246,8 +255,15 @@
                       default=False,
                       action='store_true')
   result.add_argument('--apk',
-                      help='Path to the APK',
-                      required=True)
+                      help='Path to the .apk')
+  result.add_argument('--apks',
+                      help='Path to the .apks')
+  result.add_argument('--bundle',
+                      help='Path to the .aab')
+  result.add_argument('--capture-screen',
+                      help='Take a screenshot after each test',
+                      default=False,
+                      action='store_true')
   result.add_argument('--cooldown',
                       help='Seconds to wait before running each iteration',
                       default=0,
@@ -282,8 +298,23 @@
                       type=int)
   options, args = result.parse_known_args(argv)
   setattr(options, 'perfetto', not options.no_perfetto)
+
+  paths = [
+      path for path in [options.apk, options.apks, options.bundle]
+      if path is not None]
+  assert len(paths) == 1, 'Expected exactly one .apk, .apks, or .aab file.'
+
+  # Build .apks file up front to avoid building the bundle upon each install.
+  if options.bundle:
+    os.makedirs(options.out_dir, exist_ok=True)
+    options.apks = os.path.join(options.out_dir, 'Bundle.apks')
+    adb_utils.build_apks_from_bundle(
+        options.bundle, options.apks, overwrite=True)
+    del options.bundle
+
   # Profile is only used with --aot.
   assert options.aot or not options.baseline_profile
+
   return options, args
 
 def global_setup(options):
@@ -308,10 +339,13 @@
 def main(argv):
   (options, args) = parse_options(argv)
   with utils.TempDir() as tmp_dir:
-    apk = apk_utils.add_baseline_profile_to_apk(
-        options.apk, options.baseline_profile, tmp_dir)
+    apk_or_apks = { 'apk': options.apk, 'apks': options.apks }
+    if options.baseline_profile:
+      assert not options.apks, 'Unimplemented'
+      apk_or_apks['apk'] = apk_utils.add_baseline_profile_to_apk(
+          options.apk, options.baseline_profile, tmp_dir)
     teardown_options = global_setup(options)
-    run_all(apk, options, tmp_dir)
+    run_all(apk_or_apks, options, tmp_dir)
     global_teardown(options, teardown_options)
 
 if __name__ == '__main__':
diff --git a/tools/startup/profile_utils.py b/tools/startup/profile_utils.py
new file mode 100755
index 0000000..0e69397
--- /dev/null
+++ b/tools/startup/profile_utils.py
@@ -0,0 +1,105 @@
+#!/usr/bin/env python3
+# Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+import argparse
+import sys
+
+COMPANION_CLASS_SUFFIX = '$-CC'
+EXTERNAL_SYNTHETIC_SUFFIX = '$$ExternalSynthetic'
+SYNTHETIC_PREFIX = 'S'
+
+# Parses a list of class and method descriptors, prefixed with one or more flags
+# 'H' (hot), 'S' (startup), 'P' (post startup).
+#
+# Example:
+#
+# HSPLandroidx/compose/runtime/ComposerImpl;->updateValue(Ljava/lang/Object;)V
+# HSPLandroidx/compose/runtime/ComposerImpl;->updatedNodeCount(I)I
+# HLandroidx/compose/runtime/ComposerImpl;->validateNodeExpected()V
+# PLandroidx/compose/runtime/CompositionImpl;->applyChanges()V
+# HLandroidx/compose/runtime/ComposerKt;->findLocation(Ljava/util/List;I)I
+# Landroidx/compose/runtime/ComposerImpl;
+#
+# See also https://developer.android.com/studio/profile/baselineprofiles.
+def parse_art_profile(lines):
+  art_profile = {}
+  flags_to_name = { 'H': 'hot', 'S': 'startup', 'P': 'post_startup' }
+  for line in lines:
+    line = line.strip()
+    if not line:
+      continue
+    flags = { 'hot': False, 'startup': False, 'post_startup': False }
+    while line[0] in flags_to_name:
+      flag_abbreviation = line[0]
+      flag_name = flags_to_name.get(flag_abbreviation)
+      flags[flag_name] = True
+      line = line[1:]
+    assert line.startswith('L')
+    descriptor = line
+    art_profile[descriptor] = flags
+  return art_profile
+
+def transform_art_profile_to_r8_startup_list(art_profile):
+  r8_startup_list = {}
+  for startup_descriptor, flags in art_profile.items():
+    transformed_startup_descriptor = transform_synthetic_descriptor(
+        startup_descriptor)
+    r8_startup_list[transformed_startup_descriptor] = {
+      'conditional_startup': False,
+      'post_startup': flags['post_startup'],
+      'startup': flags['startup']
+    }
+  return r8_startup_list
+
+def transform_synthetic_descriptor(descriptor):
+  companion_class_index = descriptor.find(COMPANION_CLASS_SUFFIX)
+  if companion_class_index >= 0:
+    return SYNTHETIC_PREFIX + descriptor[0:companion_class_index] + ';'
+  external_synthetic_index = descriptor.find(EXTERNAL_SYNTHETIC_SUFFIX)
+  if external_synthetic_index >= 0:
+    return SYNTHETIC_PREFIX + descriptor[0:external_synthetic_index] + ';'
+  return descriptor
+
+def filter_r8_startup_list(r8_startup_list, options):
+  filtered_r8_startup_list = {}
+  for startup_descriptor, flags in r8_startup_list.items():
+    if not options.include_post_startup \
+        and flags.get('post_startup') \
+        and not flags.get('startup'):
+      continue
+    filtered_r8_startup_list[startup_descriptor] = flags
+  return filtered_r8_startup_list
+
+def parse_options(argv):
+  result = argparse.ArgumentParser(
+      description='Utilities for converting an ART profile into an R8 startup '
+                  'list.')
+  result.add_argument('--art-profile', help='Path to the ART profile')
+  result.add_argument('--include-post-startup',
+                      help='Include post startup classes and methods in the R8 '
+                           'startup list',
+                      action='store_true',
+                      default=False)
+  result.add_argument('--out', help='Where to store the R8 startup list')
+  options, args = result.parse_known_args(argv)
+  return options, args
+
+def main(argv):
+  (options, args) = parse_options(argv)
+  with open(options.art_profile, 'r') as f:
+    art_profile = parse_art_profile(f.read().splitlines())
+  r8_startup_list = transform_art_profile_to_r8_startup_list(art_profile)
+  filtered_r8_startup_list = filter_r8_startup_list(r8_startup_list, options)
+  if options.out is not None:
+    with open(options.out, 'w') as f:
+      for startup_descriptor, flags in filtered_r8_startup_list.items():
+        f.write(startup_descriptor)
+        f.write('\n')
+  else:
+    for startup_descriptor, flags in filtered_r8_startup_list.items():
+      print(startup_descriptor)
+
+if __name__ == '__main__':
+  sys.exit(main(sys.argv[1:]))
diff --git a/tools/utils.py b/tools/utils.py
index ecd172c..9bd518f 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -23,6 +23,8 @@
 TOOLS_DIR = defines.TOOLS_DIR
 REPO_ROOT = defines.REPO_ROOT
 THIRD_PARTY = defines.THIRD_PARTY
+BUNDLETOOL_JAR_DIR = os.path.join(THIRD_PARTY, 'bundletool/bundletool-1.11.0')
+BUNDLETOOL_JAR = os.path.join(BUNDLETOOL_JAR_DIR, 'bundletool-all-1.11.0.jar')
 ANDROID_SDK = os.path.join(THIRD_PARTY, 'android_sdk')
 MEMORY_USE_TMP_FILE = 'memory_use.tmp'
 DEX_SEGMENTS_RESULT_PATTERN = re.compile('- ([^:]+): ([0-9]+)')