Merge commit '1ebd2af288f7ff4ab8f22a569907a72cd8d4fe2f' into dev-release
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index d0cc5af..79d8121 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -67,10 +67,10 @@
 import com.android.tools.r8.optimize.MemberRebindingAnalysis;
 import com.android.tools.r8.optimize.MemberRebindingIdentityLens;
 import com.android.tools.r8.optimize.MemberRebindingIdentityLensFactory;
-import com.android.tools.r8.optimize.RedundantBridgeRemover;
 import com.android.tools.r8.optimize.bridgehoisting.BridgeHoisting;
 import com.android.tools.r8.optimize.interfaces.analysis.CfOpenClosedInterfacesAnalysis;
 import com.android.tools.r8.optimize.proto.ProtoNormalizer;
+import com.android.tools.r8.optimize.redundantbridgeremoval.RedundantBridgeRemover;
 import com.android.tools.r8.origin.CommandLineOrigin;
 import com.android.tools.r8.profile.art.ArtProfileCompletenessChecker;
 import com.android.tools.r8.profile.rewriting.ProfileCollectionAdditions;
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 97ceb6e..a664d9d 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -747,7 +747,7 @@
                   dataResourceConsumer.accept(adapted, options.reporter);
                 } else {
                   options.reporter.warning(
-                      new StringDiagnostic("Resource '" + adapted.getName() + "' already exists."));
+                      new StringDiagnostic("Resource '" + file.getName() + "' already exists."));
                 }
                 options.reporter.failIfPendingErrors();
               }
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 f30986c..f9a5279 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -475,9 +475,9 @@
     return isInstanceInitializer() || willBeInlinedIntoInstanceInitializer(dexItemFactory);
   }
 
-  public boolean isDefaultInitializer() {
+  public boolean isDefaultInstanceInitializer() {
     checkIfObsolete();
-    return isInstanceInitializer() && getReference().proto.parameters.isEmpty();
+    return isInstanceInitializer() && getParameters().isEmpty();
   }
 
   public boolean isClassInitializer() {
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 13c5ab8..4d6f31a 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -602,6 +602,9 @@
   public final DexType javaUtilSetType = createStaticallyKnownType("Ljava/util/Set;");
 
   public final DexType androidAppActivity = createStaticallyKnownType("Landroid/app/Activity;");
+  public final DexType androidAppFragment = createStaticallyKnownType("Landroid/app/Fragment;");
+  public final DexType androidAppZygotePreload =
+      createStaticallyKnownType("Landroid/app/ZygotePreload;");
   public final DexType androidOsBuildType = createStaticallyKnownType("Landroid/os/Build;");
   public final DexType androidOsBuildVersionType =
       createStaticallyKnownType("Landroid/os/Build$VERSION;");
@@ -1182,7 +1185,7 @@
   // android.content.ContentProviderClient
   public class AndroidContentContentProviderClientMembers extends LibraryMembers {
     public final DexMethod release =
-        createMethod(androidContentContentProviderClientType, createProto(voidType), "release");
+        createMethod(androidContentContentProviderClientType, createProto(booleanType), "release");
     public final DexMethod close =
         createMethod(androidContentContentProviderClientType, createProto(voidType), "close");
   }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerAnalysis.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerAnalysis.java
index bba2edd..9117bcc 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerAnalysis.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerAnalysis.java
@@ -39,6 +39,34 @@
       AppView<? extends AppInfoWithClassHierarchy> appView,
       IRCodeProvider codeProvider,
       MergeGroup group,
+      InstanceInitializer instanceInitializer) {
+    if (instanceInitializer.isAbsent()) {
+      InstanceInitializerDescription.Builder builder =
+          InstanceInitializerDescription.builder(appView, instanceInitializer.getReference());
+      DexMethod invokedConstructor =
+          instanceInitializer
+              .getReference()
+              .withHolder(group.getSuperType(), appView.dexItemFactory());
+      List<InstanceFieldInitializationInfo> invokedConstructorArguments = new ArrayList<>();
+      for (int argumentIndex = 1;
+          argumentIndex < invokedConstructor.getNumberOfArguments(false);
+          argumentIndex++) {
+        invokedConstructorArguments.add(
+            appView
+                .instanceFieldInitializationInfoFactory()
+                .createArgumentInitializationInfo(argumentIndex));
+      }
+      builder.addInvokeConstructor(invokedConstructor, invokedConstructorArguments);
+      return builder.build();
+    } else {
+      return analyze(appView, codeProvider, group, instanceInitializer.asPresent().getMethod());
+    }
+  }
+
+  public static InstanceInitializerDescription analyze(
+      AppView<? extends AppInfoWithClassHierarchy> appView,
+      IRCodeProvider codeProvider,
+      MergeGroup group,
       ProgramMethod instanceInitializer) {
     InstanceInitializerDescription.Builder builder =
         InstanceInitializerDescription.builder(appView, instanceInitializer);
@@ -179,4 +207,65 @@
   private static InstanceInitializerDescription invalid() {
     return null;
   }
+
+  public abstract static class InstanceInitializer {
+
+    public abstract DexMethod getReference();
+
+    public abstract boolean isAbsent();
+
+    public abstract PresentInstanceInitializer asPresent();
+  }
+
+  public static class AbsentInstanceInitializer extends InstanceInitializer {
+
+    private final DexMethod methodReference;
+
+    public AbsentInstanceInitializer(DexMethod methodReference) {
+      this.methodReference = methodReference;
+    }
+
+    @Override
+    public DexMethod getReference() {
+      return methodReference;
+    }
+
+    @Override
+    public boolean isAbsent() {
+      return true;
+    }
+
+    @Override
+    public PresentInstanceInitializer asPresent() {
+      return null;
+    }
+  }
+
+  public static class PresentInstanceInitializer extends InstanceInitializer {
+
+    private final ProgramMethod method;
+
+    public PresentInstanceInitializer(ProgramMethod method) {
+      this.method = method;
+    }
+
+    public ProgramMethod getMethod() {
+      return method;
+    }
+
+    @Override
+    public DexMethod getReference() {
+      return method.getReference();
+    }
+
+    @Override
+    public boolean isAbsent() {
+      return false;
+    }
+
+    @Override
+    public PresentInstanceInitializer asPresent() {
+      return this;
+    }
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerDescription.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerDescription.java
index 894c017..52a00c1 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerDescription.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerDescription.java
@@ -57,6 +57,11 @@
   }
 
   public static Builder builder(
+      AppView<? extends AppInfoWithClassHierarchy> appView, DexMethod instanceInitializer) {
+    return new Builder(appView.dexItemFactory(), instanceInitializer);
+  }
+
+  public static Builder builder(
       AppView<? extends AppInfoWithClassHierarchy> appView, ProgramMethod instanceInitializer) {
     return new Builder(appView.dexItemFactory(), instanceInitializer);
   }
@@ -120,15 +125,19 @@
     private DexMethod parentConstructor;
     private List<InstanceFieldInitializationInfo> parentConstructorArguments;
 
-    Builder(DexItemFactory dexItemFactory, ProgramMethod method) {
+    Builder(DexItemFactory dexItemFactory, DexMethod methodReference) {
       this.dexItemFactory = dexItemFactory;
       this.relaxedParameters =
-          method
+          methodReference
               .getParameters()
               .map(
                   parameter -> parameter.isPrimitiveType() ? parameter : dexItemFactory.objectType);
     }
 
+    Builder(DexItemFactory dexItemFactory, ProgramMethod method) {
+      this(dexItemFactory, method.getReference());
+    }
+
     public void addInstancePut(DexField field, InstanceFieldInitializationInfo value) {
       if (parentConstructor == null) {
         instanceFieldAssignmentsPre.put(field, value);
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDefaultInterfaceMethodCollisions.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDefaultInterfaceMethodCollisions.java
index deb0e02..294d87d 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDefaultInterfaceMethodCollisions.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDefaultInterfaceMethodCollisions.java
@@ -282,8 +282,7 @@
         MapUtils.newIdentityHashMap(
             builder ->
                 Iterables.filter(groups, MergeGroup::isClassGroup)
-                    .forEach(
-                        group -> group.forEach(clazz -> builder.accept(clazz.getType(), group))));
+                    .forEach(group -> group.forEach(clazz -> builder.put(clazz.getType(), group))));
 
     // Copy the map from classes to their inherited default methods.
     Map<DexType, Map<DexMethodSignature, Set<DexMethod>>>
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInstanceInitializerMerging.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInstanceInitializerMerging.java
index 02b6bbd..4c07501 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInstanceInitializerMerging.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInstanceInitializerMerging.java
@@ -4,38 +4,47 @@
 
 package com.android.tools.r8.horizontalclassmerging.policies;
 
+import static com.android.tools.r8.utils.MapUtils.ignoreKey;
+
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexMethodSignature;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.graph.MethodAccessInfoCollection;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.horizontalclassmerging.ClassInstanceFieldsMerger;
 import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
 import com.android.tools.r8.horizontalclassmerging.IRCodeProvider;
 import com.android.tools.r8.horizontalclassmerging.InstanceInitializerAnalysis;
+import com.android.tools.r8.horizontalclassmerging.InstanceInitializerAnalysis.AbsentInstanceInitializer;
+import com.android.tools.r8.horizontalclassmerging.InstanceInitializerAnalysis.InstanceInitializer;
+import com.android.tools.r8.horizontalclassmerging.InstanceInitializerAnalysis.PresentInstanceInitializer;
 import com.android.tools.r8.horizontalclassmerging.InstanceInitializerDescription;
 import com.android.tools.r8.horizontalclassmerging.MergeGroup;
-import com.android.tools.r8.horizontalclassmerging.MultiClassPolicy;
+import com.android.tools.r8.horizontalclassmerging.MultiClassPolicyWithPreprocessing;
+import com.android.tools.r8.utils.IterableUtils;
 import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.MapUtils;
 import com.android.tools.r8.utils.SetUtils;
 import com.android.tools.r8.utils.collections.BidirectionalManyToOneHashMap;
 import com.android.tools.r8.utils.collections.MutableBidirectionalManyToOneMap;
-import com.android.tools.r8.utils.collections.ProgramMethodMap;
-import com.google.common.collect.Iterators;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.IdentityHashMap;
-import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Optional;
 import java.util.Set;
+import java.util.concurrent.ExecutorService;
 import java.util.function.Function;
 
 /**
@@ -46,7 +55,8 @@
  * <p>This policy requires that all instance initializers with the same signature (relaxed, by
  * converting references types to java.lang.Object) have the same behavior.
  */
-public class NoInstanceInitializerMerging extends MultiClassPolicy {
+public class NoInstanceInitializerMerging
+    extends MultiClassPolicyWithPreprocessing<Map<DexProgramClass, Set<DexMethod>>> {
 
   private final AppView<? extends AppInfoWithClassHierarchy> appView;
   private final IRCodeProvider codeProvider;
@@ -61,7 +71,53 @@
   }
 
   @Override
-  public Collection<MergeGroup> apply(MergeGroup group) {
+  public Map<DexProgramClass, Set<DexMethod>> preprocess(
+      Collection<MergeGroup> groups, ExecutorService executorService) {
+    if (!appView.options().canHaveNonReboundConstructorInvoke()) {
+      return Collections.emptyMap();
+    }
+
+    if (appView.hasLiveness()) {
+      DexItemFactory dexItemFactory = appView.dexItemFactory();
+      MethodAccessInfoCollection methodAccessInfoCollection =
+          appView.appInfoWithLiveness().getMethodAccessInfoCollection();
+
+      // Compute a mapping for the merge candidates to efficiently determine if a given type is a
+      // merge candidate.
+      Map<DexType, DexProgramClass> mergeCandidates =
+          MapUtils.newImmutableMap(
+              builder ->
+                  IterableUtils.flatten(groups)
+                      .forEach(
+                          mergeCandidate -> builder.put(mergeCandidate.getType(), mergeCandidate)));
+
+      // Compute a mapping from merge candidates to the constructors that have been removed by
+      // constructor shrinking.
+      return MapUtils.newIdentityHashMap(
+          builder ->
+              methodAccessInfoCollection.forEachDirectInvoke(
+                  (method, contexts) -> {
+                    DexProgramClass mergeCandidateHolder =
+                        mergeCandidates.get(method.getHolderType());
+                    if (mergeCandidateHolder != null
+                        && method.isInstanceInitializer(dexItemFactory)
+                        && mergeCandidateHolder.getMethodCollection().getMethod(method) == null) {
+                      builder
+                          .computeIfAbsent(
+                              mergeCandidateHolder, ignoreKey(Sets::newIdentityHashSet))
+                          .add(method);
+                    }
+                  }));
+    }
+
+    // Constructor shrinking is disabled when shrinking is disabled.
+    assert !appView.options().isShrinking();
+    return Collections.emptyMap();
+  }
+
+  @Override
+  public Collection<MergeGroup> apply(
+      MergeGroup group, Map<DexProgramClass, Set<DexMethod>> absentInstanceInitializers) {
     assert !group.hasTarget();
     assert !group.hasInstanceFieldMap();
 
@@ -76,7 +132,10 @@
     // reference parameters are converted to java.lang.Object), to ensure that merging will result
     // in a simple renaming (specifically, we must not need to append null arguments to constructor
     // calls due to constructor collisions).
-    group.removeIf(this::hasMultipleInstanceInitializersWithSameRelaxedSignature);
+    group.removeIf(
+        clazz ->
+            hasMultipleInstanceInitializersWithSameRelaxedSignature(
+                clazz, absentInstanceInitializers));
 
     if (group.isEmpty()) {
       return Collections.emptyList();
@@ -87,14 +146,14 @@
     group.selectTarget(appView);
     group.selectInstanceFieldMap(appView);
 
-    Map<MergeGroup, Map<DexMethodSignature, ProgramMethod>> newGroups = new LinkedHashMap<>();
+    Map<MergeGroup, Map<DexMethodSignature, InstanceInitializer>> newGroups = new LinkedHashMap<>();
 
     // Caching of instance initializer descriptions, which are used to determine equivalence.
     // TODO(b/181846319): Make this cache available to the instance initializer merger so that we
     //  don't reanalyze instance initializers.
-    ProgramMethodMap<Optional<InstanceInitializerDescription>> instanceInitializerDescriptions =
-        ProgramMethodMap.create();
-    Function<ProgramMethod, Optional<InstanceInitializerDescription>>
+    Map<DexMethod, Optional<InstanceInitializerDescription>> instanceInitializerDescriptions =
+        new IdentityHashMap<>();
+    Function<InstanceInitializer, Optional<InstanceInitializerDescription>>
         instanceInitializerDescriptionProvider =
             instanceInitializer ->
                 getOrComputeInstanceInitializerDescription(
@@ -104,11 +163,12 @@
     // collisions.
     for (DexProgramClass clazz : group) {
       MergeGroup newGroup = null;
-      Map<DexMethodSignature, ProgramMethod> classInstanceInitializers =
-          getInstanceInitializersByRelaxedSignature(clazz);
-      for (Entry<MergeGroup, Map<DexMethodSignature, ProgramMethod>> entry : newGroups.entrySet()) {
+      Map<DexMethodSignature, InstanceInitializer> classInstanceInitializers =
+          getInstanceInitializersByRelaxedSignature(clazz, absentInstanceInitializers);
+      for (Entry<MergeGroup, Map<DexMethodSignature, InstanceInitializer>> entry :
+          newGroups.entrySet()) {
         MergeGroup candidateGroup = entry.getKey();
-        Map<DexMethodSignature, ProgramMethod> groupInstanceInitializers = entry.getValue();
+        Map<DexMethodSignature, InstanceInitializer> groupInstanceInitializers = entry.getValue();
         if (canAddClassToGroup(
             classInstanceInitializers,
             groupInstanceInitializers,
@@ -132,14 +192,16 @@
   }
 
   private boolean canAddClassToGroup(
-      Map<DexMethodSignature, ProgramMethod> classInstanceInitializers,
-      Map<DexMethodSignature, ProgramMethod> groupInstanceInitializers,
-      Function<ProgramMethod, Optional<InstanceInitializerDescription>>
+      Map<DexMethodSignature, InstanceInitializer> classInstanceInitializers,
+      Map<DexMethodSignature, InstanceInitializer> groupInstanceInitializers,
+      Function<InstanceInitializer, Optional<InstanceInitializerDescription>>
           instanceInitializerDescriptionProvider) {
-    for (Entry<DexMethodSignature, ProgramMethod> entry : classInstanceInitializers.entrySet()) {
+    for (Entry<DexMethodSignature, InstanceInitializer> entry :
+        classInstanceInitializers.entrySet()) {
       DexMethodSignature relaxedSignature = entry.getKey();
-      ProgramMethod classInstanceInitializer = entry.getValue();
-      ProgramMethod groupInstanceInitializer = groupInstanceInitializers.get(relaxedSignature);
+      InstanceInitializer classInstanceInitializer = entry.getValue();
+      InstanceInitializer groupInstanceInitializer =
+          groupInstanceInitializers.get(relaxedSignature);
       if (groupInstanceInitializer == null) {
         continue;
       }
@@ -160,31 +222,41 @@
     return true;
   }
 
-  private boolean hasMultipleInstanceInitializersWithSameRelaxedSignature(DexProgramClass clazz) {
-    Iterator<ProgramMethod> instanceInitializers = clazz.programInstanceInitializers().iterator();
-    if (!instanceInitializers.hasNext()) {
-      // No instance initializers.
+  private boolean hasMultipleInstanceInitializersWithSameRelaxedSignature(
+      DexProgramClass clazz, Map<DexProgramClass, Set<DexMethod>> absentInstanceInitializers) {
+    Set<DexMethod> instanceInitializerReferences =
+        SetUtils.unionIdentityHashSet(
+            SetUtils.newIdentityHashSet(
+                IterableUtils.transform(
+                    clazz.programInstanceInitializers(), ProgramMethod::getReference)),
+            absentInstanceInitializers.getOrDefault(clazz, Collections.emptySet()));
+    if (instanceInitializerReferences.size() <= 1) {
       return false;
     }
 
-    ProgramMethod first = instanceInitializers.next();
-    if (!instanceInitializers.hasNext()) {
-      // Only a single instance initializer.
-      return false;
-    }
-
-    Set<DexMethod> seen = SetUtils.newIdentityHashSet(getRelaxedSignature(first));
-    return Iterators.any(
-        instanceInitializers,
-        instanceInitializer -> !seen.add(getRelaxedSignature(instanceInitializer)));
+    Set<DexMethod> seen = SetUtils.newIdentityHashSet();
+    return Iterables.any(
+        instanceInitializerReferences,
+        instanceInitializerReference ->
+            !seen.add(getRelaxedSignature(instanceInitializerReference)));
   }
 
-  private Map<DexMethodSignature, ProgramMethod> getInstanceInitializersByRelaxedSignature(
-      DexProgramClass clazz) {
-    Map<DexMethodSignature, ProgramMethod> result = new HashMap<>();
-    for (ProgramMethod instanceInitializer : clazz.programInstanceInitializers()) {
-      DexMethodSignature relaxedSignature = getRelaxedSignature(instanceInitializer).getSignature();
-      ProgramMethod previous = result.put(relaxedSignature, instanceInitializer);
+  private Map<DexMethodSignature, InstanceInitializer> getInstanceInitializersByRelaxedSignature(
+      DexProgramClass clazz, Map<DexProgramClass, Set<DexMethod>> absentInstanceInitializers) {
+    Map<DexMethodSignature, InstanceInitializer> result = new HashMap<>();
+    for (ProgramMethod presentInstanceInitializer : clazz.programInstanceInitializers()) {
+      DexMethodSignature relaxedSignature =
+          getRelaxedSignature(presentInstanceInitializer).getSignature();
+      InstanceInitializer previous =
+          result.put(relaxedSignature, new PresentInstanceInitializer(presentInstanceInitializer));
+      assert previous == null;
+    }
+    for (DexMethod absentInstanceInitializer :
+        absentInstanceInitializers.getOrDefault(clazz, Collections.emptySet())) {
+      DexMethodSignature relaxedSignature =
+          getRelaxedSignature(absentInstanceInitializer).getSignature();
+      InstanceInitializer previous =
+          result.put(relaxedSignature, new AbsentInstanceInitializer(absentInstanceInitializer));
       assert previous == null;
     }
     return result;
@@ -192,10 +264,10 @@
 
   private Optional<InstanceInitializerDescription> getOrComputeInstanceInitializerDescription(
       MergeGroup group,
-      ProgramMethod instanceInitializer,
-      ProgramMethodMap<Optional<InstanceInitializerDescription>> instanceInitializerDescriptions) {
+      InstanceInitializer instanceInitializer,
+      Map<DexMethod, Optional<InstanceInitializerDescription>> instanceInitializerDescriptions) {
     return instanceInitializerDescriptions.computeIfAbsent(
-        instanceInitializer,
+        instanceInitializer.getReference(),
         key -> {
           InstanceInitializerDescription instanceInitializerDescription =
               InstanceInitializerAnalysis.analyze(
@@ -205,15 +277,20 @@
   }
 
   private DexMethod getRelaxedSignature(ProgramMethod instanceInitializer) {
+    return getRelaxedSignature(instanceInitializer.getReference());
+  }
+
+  private DexMethod getRelaxedSignature(DexMethod instanceInitializerReference) {
     DexType objectType = appView.dexItemFactory().objectType;
-    DexTypeList parameters = instanceInitializer.getParameters();
+    DexTypeList parameters = instanceInitializerReference.getParameters();
     DexTypeList relaxedParameters =
         parameters.map(parameter -> parameter.isPrimitiveType() ? parameter : objectType);
     return parameters != relaxedParameters
         ? appView
             .dexItemFactory()
-            .createInstanceInitializer(instanceInitializer.getHolderType(), relaxedParameters)
-        : instanceInitializer.getReference();
+            .createInstanceInitializer(
+                instanceInitializerReference.getHolderType(), relaxedParameters)
+        : instanceInitializerReference;
   }
 
   private void setInstanceFieldMaps(Iterable<MergeGroup> newGroups, MergeGroup group) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java
index 637026f..d871616 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java
@@ -11,7 +11,6 @@
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.ProgramField;
 import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadataProvider;
-import com.android.tools.r8.ir.analysis.fieldaccess.readbeforewrite.FieldReadBeforeWriteAnalysis;
 import com.android.tools.r8.ir.code.FieldInstruction;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.Instruction;
@@ -70,7 +69,6 @@
       IRCode code,
       BytecodeMetadataProvider.Builder bytecodeMetadataProviderBuilder,
       OptimizationFeedback feedback,
-      FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis,
       MethodProcessor methodProcessor) {
     if (!methodProcessor.isPrimaryMethodProcessor()) {
       return;
@@ -87,8 +85,7 @@
             appView.appInfo().resolveField(fieldInstruction.getField()).getProgramField();
         if (field != null) {
           if (fieldAssignmentTracker != null) {
-            fieldAssignmentTracker.recordFieldAccess(
-                fieldInstruction, field, fieldReadBeforeWriteAnalysis);
+            fieldAssignmentTracker.recordFieldAccess(fieldInstruction, field, code.context());
           }
           if (fieldBitAccessAnalysis != null) {
             fieldBitAccessAnalysis.recordFieldAccess(
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
index dc90a85..0db5825 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
@@ -19,7 +19,6 @@
 import com.android.tools.r8.graph.ProgramField;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.horizontalclassmerging.HorizontalClassMergerUtils;
-import com.android.tools.r8.ir.analysis.fieldaccess.readbeforewrite.FieldReadBeforeWriteAnalysis;
 import com.android.tools.r8.ir.analysis.fieldaccess.state.ConcreteArrayTypeFieldState;
 import com.android.tools.r8.ir.analysis.fieldaccess.state.ConcreteClassTypeFieldState;
 import com.android.tools.r8.ir.analysis.fieldaccess.state.ConcretePrimitiveTypeFieldState;
@@ -35,8 +34,6 @@
 import com.android.tools.r8.ir.analysis.value.SingleValue;
 import com.android.tools.r8.ir.analysis.value.UnknownValue;
 import com.android.tools.r8.ir.code.FieldInstruction;
-import com.android.tools.r8.ir.code.FieldPut;
-import com.android.tools.r8.ir.code.InstancePut;
 import com.android.tools.r8.ir.code.InvokeDirect;
 import com.android.tools.r8.ir.code.NewInstance;
 import com.android.tools.r8.ir.code.Value;
@@ -182,23 +179,16 @@
         });
   }
 
-  void recordFieldAccess(
-      FieldInstruction instruction,
-      ProgramField field,
-      FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis) {
+  void recordFieldAccess(FieldInstruction instruction, ProgramField field, ProgramMethod context) {
     if (instruction.isFieldPut()) {
-      recordFieldPut(instruction.asFieldPut(), field, fieldReadBeforeWriteAnalysis);
+      recordFieldPut(field, instruction.value(), context);
     }
   }
 
-  private void recordFieldPut(
-      FieldPut fieldPut,
-      ProgramField field,
-      FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis) {
+  private void recordFieldPut(ProgramField field, Value value, ProgramMethod context) {
     // For now only attempt to prove that fields are definitely null. In order to prove a single
     // value for fields that are not definitely null, we need to prove that the given field is never
     // read before it is written.
-    Value value = fieldPut.value();
     AbstractValue abstractValue =
         value.isZero() ? abstractValueFactory.createZeroValue() : AbstractValue.unknown();
     fieldStates.compute(
@@ -213,8 +203,12 @@
               return ConcretePrimitiveTypeFieldState.create(abstractValue);
             }
             assert fieldType.isClassType();
-            return ConcreteClassTypeFieldState.create(
-                abstractValue, getDynamicType(fieldPut, field, fieldReadBeforeWriteAnalysis));
+            DynamicType dynamicType =
+                WideningUtils.widenDynamicNonReceiverType(
+                    appView,
+                    value.getDynamicType(appView).withNullability(Nullability.maybeNull()),
+                    field.getType());
+            return ConcreteClassTypeFieldState.create(abstractValue, dynamicType);
           }
 
           if (fieldState.isUnknown()) {
@@ -237,31 +231,10 @@
 
           ConcreteClassTypeFieldState classFieldState = fieldState.asClass();
           return classFieldState.mutableJoin(
-              appView,
-              abstractValue,
-              getDynamicType(fieldPut, field, fieldReadBeforeWriteAnalysis),
-              field);
+              appView, abstractValue, value.getDynamicType(appView), field);
         });
   }
 
-  private DynamicType getDynamicType(
-      FieldPut fieldPut,
-      ProgramField field,
-      FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis) {
-    DynamicTypeWithUpperBound dynamicType = fieldPut.value().getDynamicType(appView);
-    if (fieldPut.isInstancePut()) {
-      InstancePut instancePut = fieldPut.asInstancePut();
-      if (fieldReadBeforeWriteAnalysis.isInstanceFieldMaybeReadBeforeInstruction(
-          instancePut.object(), field.getDefinition(), instancePut)) {
-        dynamicType = dynamicType.withNullability(Nullability.maybeNull());
-      }
-    } else if (fieldReadBeforeWriteAnalysis.isStaticFieldMaybeReadBeforeInstruction(
-        field.getDefinition(), fieldPut.asStaticPut())) {
-      dynamicType = dynamicType.withNullability(Nullability.maybeNull());
-    }
-    return WideningUtils.widenDynamicNonReceiverType(appView, dynamicType, field.getType());
-  }
-
   void recordAllocationSite(NewInstance instruction, DexProgramClass clazz, ProgramMethod context) {
     Map<DexEncodedField, AbstractValue> abstractInstanceFieldValuesForClass =
         abstractInstanceFieldValues.get(clazz);
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/readbeforewrite/FieldReadBeforeWriteAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/readbeforewrite/FieldReadBeforeWriteAnalysis.java
deleted file mode 100644
index fb6bae0..0000000
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/readbeforewrite/FieldReadBeforeWriteAnalysis.java
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.ir.analysis.fieldaccess.readbeforewrite;
-
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.code.IRCode;
-import com.android.tools.r8.ir.code.Instruction;
-import com.android.tools.r8.ir.code.Value;
-
-public abstract class FieldReadBeforeWriteAnalysis {
-
-  public static FieldReadBeforeWriteAnalysis create(
-      AppView<?> appView, IRCode code, ProgramMethod context) {
-    if (appView.hasLiveness()) {
-      return new FieldReadBeforeWriteAnalysisImpl(appView.withLiveness(), code, context);
-    }
-    return trivial();
-  }
-
-  public static TrivialFieldReadBeforeWriteAnalysis trivial() {
-    return new TrivialFieldReadBeforeWriteAnalysis();
-  }
-
-  public abstract boolean isInstanceFieldMaybeReadBeforeInstruction(
-      Value receiver, DexEncodedField field, Instruction instruction);
-
-  public abstract boolean isStaticFieldMaybeReadBeforeInstruction(
-      DexEncodedField field, Instruction instruction);
-}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/readbeforewrite/FieldReadBeforeWriteAnalysisImpl.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/readbeforewrite/FieldReadBeforeWriteAnalysisImpl.java
deleted file mode 100644
index 7e7ac89..0000000
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/readbeforewrite/FieldReadBeforeWriteAnalysisImpl.java
+++ /dev/null
@@ -1,209 +0,0 @@
-// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.ir.analysis.fieldaccess.readbeforewrite;
-
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.analysis.fieldvalueanalysis.AbstractFieldSet;
-import com.android.tools.r8.ir.analysis.fieldvalueanalysis.ConcreteMutableFieldSet;
-import com.android.tools.r8.ir.analysis.fieldvalueanalysis.EmptyFieldSet;
-import com.android.tools.r8.ir.analysis.fieldvalueanalysis.KnownFieldSet;
-import com.android.tools.r8.ir.analysis.fieldvalueanalysis.UnknownFieldSet;
-import com.android.tools.r8.ir.code.BasicBlock;
-import com.android.tools.r8.ir.code.IRCode;
-import com.android.tools.r8.ir.code.Instruction;
-import com.android.tools.r8.ir.code.InstructionIterator;
-import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.utils.DequeUtils;
-import java.util.Deque;
-import java.util.IdentityHashMap;
-import java.util.Map;
-
-class FieldReadBeforeWriteAnalysisImpl extends FieldReadBeforeWriteAnalysis {
-
-  private final AppView<AppInfoWithLiveness> appView;
-  private final IRCode code;
-  private final ProgramMethod context;
-
-  private Map<BasicBlock, AbstractFieldSet> fieldsMaybeReadBeforeBlockInclusiveCache;
-
-  public FieldReadBeforeWriteAnalysisImpl(
-      AppView<AppInfoWithLiveness> appView, IRCode code, ProgramMethod context) {
-    this.appView = appView;
-    this.code = code;
-    this.context = context;
-  }
-
-  @Override
-  public boolean isInstanceFieldMaybeReadBeforeInstruction(
-      Value receiver, DexEncodedField field, Instruction instruction) {
-    if (!code.context().getDefinition().isInstanceInitializer()
-        || receiver.getAliasedValue() != code.getThis()) {
-      return true;
-    }
-    return isFieldMaybeReadBeforeInstructionInInitializer(field, instruction);
-  }
-
-  @Override
-  public boolean isStaticFieldMaybeReadBeforeInstruction(
-      DexEncodedField field, Instruction instruction) {
-    if (!code.context().getDefinition().isClassInitializer()
-        || field.getHolderType() != code.context().getHolderType()) {
-      return true;
-    }
-    return isFieldMaybeReadBeforeInstructionInInitializer(field, instruction);
-  }
-
-  public boolean isFieldMaybeReadBeforeInstructionInInitializer(
-      DexEncodedField field, Instruction instruction) {
-    BasicBlock block = instruction.getBlock();
-
-    // First check if the field may be read in any of the (transitive) predecessor blocks.
-    if (fieldMaybeReadBeforeBlock(field, block)) {
-      return true;
-    }
-
-    // Then check if any of the instructions that precede the given instruction in the current block
-    // may read the field.
-    InstructionIterator instructionIterator = block.iterator();
-    while (instructionIterator.hasNext()) {
-      Instruction current = instructionIterator.next();
-      if (current == instruction) {
-        break;
-      }
-      if (current.readSet(appView, context).contains(field)) {
-        return true;
-      }
-    }
-
-    // Otherwise, the field is not read prior to the given instruction.
-    return false;
-  }
-
-  private boolean fieldMaybeReadBeforeBlock(DexEncodedField field, BasicBlock block) {
-    for (BasicBlock predecessor : block.getPredecessors()) {
-      if (fieldMaybeReadBeforeBlockInclusive(field, predecessor)) {
-        return true;
-      }
-    }
-    return false;
-  }
-
-  private boolean fieldMaybeReadBeforeBlockInclusive(DexEncodedField field, BasicBlock block) {
-    return getOrCreateFieldsMaybeReadBeforeBlockInclusive().get(block).contains(field);
-  }
-
-  private Map<BasicBlock, AbstractFieldSet> getOrCreateFieldsMaybeReadBeforeBlockInclusive() {
-    if (fieldsMaybeReadBeforeBlockInclusiveCache == null) {
-      fieldsMaybeReadBeforeBlockInclusiveCache = createFieldsMaybeReadBeforeBlockInclusive();
-    }
-    return fieldsMaybeReadBeforeBlockInclusiveCache;
-  }
-
-  /**
-   * Eagerly creates a mapping from each block to the set of fields that may be read in that block
-   * and its transitive predecessors.
-   */
-  private Map<BasicBlock, AbstractFieldSet> createFieldsMaybeReadBeforeBlockInclusive() {
-    Map<BasicBlock, AbstractFieldSet> result = new IdentityHashMap<>();
-    Deque<BasicBlock> worklist = DequeUtils.newArrayDeque(code.entryBlock());
-    while (!worklist.isEmpty()) {
-      BasicBlock block = worklist.removeFirst();
-      boolean seenBefore = result.containsKey(block);
-      AbstractFieldSet readSet =
-          result.computeIfAbsent(block, ignore -> EmptyFieldSet.getInstance());
-      if (readSet.isTop()) {
-        // We already have unknown information for this block.
-        continue;
-      }
-
-      assert readSet.isKnownFieldSet();
-      KnownFieldSet knownReadSet = readSet.asKnownFieldSet();
-      int oldSize = seenBefore ? knownReadSet.size() : -1;
-
-      // Everything that is read in the predecessor blocks should also be included in the read set
-      // for the current block, so here we join the information from the predecessor blocks into the
-      // current read set.
-      boolean blockOrPredecessorMaybeReadAnyField = false;
-      for (BasicBlock predecessor : block.getPredecessors()) {
-        AbstractFieldSet predecessorReadSet =
-            result.getOrDefault(predecessor, EmptyFieldSet.getInstance());
-        if (predecessorReadSet.isBottom()) {
-          continue;
-        }
-        if (predecessorReadSet.isTop()) {
-          blockOrPredecessorMaybeReadAnyField = true;
-          break;
-        }
-        assert predecessorReadSet.isConcreteFieldSet();
-        if (!knownReadSet.isConcreteFieldSet()) {
-          knownReadSet = new ConcreteMutableFieldSet();
-        }
-        knownReadSet.asConcreteFieldSet().addAll(predecessorReadSet.asConcreteFieldSet());
-      }
-
-      if (!blockOrPredecessorMaybeReadAnyField) {
-        // Finally, we update the read set with the fields that are read by the instructions in the
-        // current block. This can be skipped if the block has already been processed.
-        if (seenBefore) {
-          assert verifyFieldSetContainsAllFieldReadsInBlock(knownReadSet, block, context);
-        } else {
-          for (Instruction instruction : block.getInstructions()) {
-            AbstractFieldSet instructionReadSet = instruction.readSet(appView, context);
-            if (instructionReadSet.isBottom()) {
-              continue;
-            }
-            if (instructionReadSet.isTop()) {
-              blockOrPredecessorMaybeReadAnyField = true;
-              break;
-            }
-            if (!knownReadSet.isConcreteFieldSet()) {
-              knownReadSet = new ConcreteMutableFieldSet();
-            }
-            knownReadSet.asConcreteFieldSet().addAll(instructionReadSet.asConcreteFieldSet());
-          }
-        }
-      }
-
-      boolean changed = false;
-      if (blockOrPredecessorMaybeReadAnyField) {
-        // Record that this block reads all fields.
-        result.put(block, UnknownFieldSet.getInstance());
-        changed = true;
-      } else {
-        if (knownReadSet != readSet) {
-          result.put(block, knownReadSet.asConcreteFieldSet());
-        }
-        if (knownReadSet.size() != oldSize) {
-          assert knownReadSet.size() > oldSize;
-          changed = true;
-        }
-      }
-
-      if (changed) {
-        // Rerun the analysis for all successors because the state of the current block changed.
-        worklist.addAll(block.getSuccessors());
-      }
-    }
-    return result;
-  }
-
-  private boolean verifyFieldSetContainsAllFieldReadsInBlock(
-      KnownFieldSet readSet, BasicBlock block, ProgramMethod context) {
-    for (Instruction instruction : block.getInstructions()) {
-      AbstractFieldSet instructionReadSet = instruction.readSet(appView, context);
-      assert !instructionReadSet.isTop();
-      if (instructionReadSet.isBottom()) {
-        continue;
-      }
-      for (DexEncodedField field : instructionReadSet.asConcreteFieldSet().getFields()) {
-        assert readSet.contains(field);
-      }
-    }
-    return true;
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/readbeforewrite/TrivialFieldReadBeforeWriteAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/readbeforewrite/TrivialFieldReadBeforeWriteAnalysis.java
deleted file mode 100644
index 9cd76dc..0000000
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/readbeforewrite/TrivialFieldReadBeforeWriteAnalysis.java
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.ir.analysis.fieldaccess.readbeforewrite;
-
-import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.ir.code.Instruction;
-import com.android.tools.r8.ir.code.Value;
-
-class TrivialFieldReadBeforeWriteAnalysis extends FieldReadBeforeWriteAnalysis {
-
-  @Override
-  public boolean isInstanceFieldMaybeReadBeforeInstruction(
-      Value receiver, DexEncodedField field, Instruction instruction) {
-    return true;
-  }
-
-  @Override
-  public boolean isStaticFieldMaybeReadBeforeInstruction(
-      DexEncodedField field, Instruction instruction) {
-    return true;
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/ConcreteMutableFieldSet.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/ConcreteMutableFieldSet.java
index 854d046..b18d02a 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/ConcreteMutableFieldSet.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/ConcreteMutableFieldSet.java
@@ -39,7 +39,7 @@
     return this;
   }
 
-  public Set<DexEncodedField> getFields() {
+  Set<DexEncodedField> getFields() {
     if (InternalOptions.assertionsEnabled()) {
       return Collections.unmodifiableSet(fields);
     }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java
index 50c075e..9c09ef2 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java
@@ -10,14 +10,13 @@
 import com.android.tools.r8.graph.DexValue;
 import com.android.tools.r8.graph.ProgramField;
 import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.analysis.fieldaccess.readbeforewrite.FieldReadBeforeWriteAnalysis;
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.DominatorTree;
 import com.android.tools.r8.ir.code.DominatorTree.Assumption;
 import com.android.tools.r8.ir.code.FieldInstruction;
 import com.android.tools.r8.ir.code.IRCode;
-import com.android.tools.r8.ir.code.InstancePut;
 import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionIterator;
 import com.android.tools.r8.ir.code.InvokeDirect;
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.ir.optimize.ClassInitializerDefaultsOptimization.ClassInitializerDefaultsResult;
@@ -25,11 +24,14 @@
 import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfo;
 import com.android.tools.r8.ir.optimize.info.field.UnknownInstanceFieldInitializationInfo;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.DequeUtils;
 import com.android.tools.r8.utils.ListUtils;
 import java.util.ArrayList;
+import java.util.Deque;
 import java.util.IdentityHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 
 public abstract class FieldValueAnalysis {
 
@@ -51,20 +53,16 @@
   final OptimizationFeedback feedback;
 
   private DominatorTree dominatorTree;
-  private FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis;
+  private Map<BasicBlock, AbstractFieldSet> fieldsMaybeReadBeforeBlockInclusiveCache;
 
   final Map<DexEncodedField, List<FieldInitializationInfo>> putsPerField = new IdentityHashMap<>();
 
   FieldValueAnalysis(
-      AppView<AppInfoWithLiveness> appView,
-      IRCode code,
-      OptimizationFeedback feedback,
-      FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis) {
+      AppView<AppInfoWithLiveness> appView, IRCode code, OptimizationFeedback feedback) {
     this.appView = appView;
     this.code = code;
-    this.context = code.context();
     this.feedback = feedback;
-    this.fieldReadBeforeWriteAnalysis = fieldReadBeforeWriteAnalysis;
+    this.context = code.context();
   }
 
   DominatorTree getOrCreateDominatorTree() {
@@ -74,6 +72,13 @@
     return dominatorTree;
   }
 
+  private Map<BasicBlock, AbstractFieldSet> getOrCreateFieldsMaybeReadBeforeBlockInclusive() {
+    if (fieldsMaybeReadBeforeBlockInclusiveCache == null) {
+      fieldsMaybeReadBeforeBlockInclusiveCache = createFieldsMaybeReadBeforeBlockInclusive();
+    }
+    return fieldsMaybeReadBeforeBlockInclusiveCache;
+  }
+
   boolean isInstanceFieldValueAnalysis() {
     return false;
   }
@@ -90,9 +95,9 @@
     return null;
   }
 
-  abstract boolean isSubjectToOptimizationIgnoringPinning(ProgramField field);
+  abstract boolean isSubjectToOptimizationIgnoringPinning(DexEncodedField field);
 
-  abstract boolean isSubjectToOptimization(ProgramField field);
+  abstract boolean isSubjectToOptimization(DexEncodedField field);
 
   void recordFieldPut(DexEncodedField field, Instruction instruction) {
     recordFieldPut(field, instruction, UnknownInstanceFieldInitializationInfo.getInstance());
@@ -122,12 +127,13 @@
           DexField field = fieldPut.getField();
           ProgramField programField = appInfo.resolveField(field).getProgramField();
           if (programField != null) {
-            if (isSubjectToOptimization(programField)) {
-              recordFieldPut(programField.getDefinition(), fieldPut);
+            DexEncodedField encodedField = programField.getDefinition();
+            if (isSubjectToOptimization(encodedField)) {
+              recordFieldPut(encodedField, fieldPut);
             } else if (isStaticFieldValueAnalysis()
                 && programField.getHolder().isEnum()
-                && isSubjectToOptimizationIgnoringPinning(programField)) {
-              recordFieldPut(programField.getDefinition(), fieldPut);
+                && isSubjectToOptimizationIgnoringPinning(encodedField)) {
+              recordFieldPut(encodedField, fieldPut);
             }
           }
         } else if (isInstanceFieldValueAnalysis()
@@ -138,54 +144,186 @@
       }
     }
 
-    boolean checkDominance = !isStraightLineCode;
     List<BasicBlock> normalExitBlocks = code.computeNormalExitBlocks();
-    putsPerField.forEach(
-        (field, fieldPuts) -> {
-          if (fieldPuts.size() > 1) {
-            return;
-          }
-          FieldInitializationInfo info = ListUtils.first(fieldPuts);
-          Instruction instruction = info.instruction;
-          if (instruction.isInvokeDirect()) {
-            asInstanceFieldValueAnalysis()
-                .recordInstanceFieldIsInitializedWithInfo(
-                    field, info.instanceFieldInitializationInfo);
-            return;
-          }
-          FieldInstruction fieldPut = instruction.asFieldInstruction();
-          if (checkDominance
-              && !getOrCreateDominatorTree()
-                  .dominatesAllOf(fieldPut.getBlock(), normalExitBlocks)) {
-            return;
-          }
-          boolean priorReadsWillReadSameValue =
-              !classInitializerDefaultsResult.hasStaticValue(field) && fieldPut.value().isZero();
-          if (!priorReadsWillReadSameValue) {
-            if (fieldPut.isInstancePut()) {
-              InstancePut instancePut = fieldPut.asInstancePut();
-              if (fieldReadBeforeWriteAnalysis.isInstanceFieldMaybeReadBeforeInstruction(
-                  instancePut.object(), field, instancePut)) {
-                return;
-              }
-            } else {
-              if (fieldReadBeforeWriteAnalysis.isStaticFieldMaybeReadBeforeInstruction(
-                  field, fieldPut)) {
-                // TODO(b/172528424): Generalize to InstanceFieldValueAnalysis.
-                // At this point the value read in the field can be only the default static value,
-                // if read prior to the put, or the value put, if read after the put. We still want
-                // to record it because the default static value is typically null/0, so code
-                // present after a null/0 check can take advantage of the optimization.
-                DexValue valueBeforePut = classInitializerDefaultsResult.getStaticValue(field);
-                asStaticFieldValueAnalysis()
-                    .updateFieldOptimizationInfoWith2Values(
-                        field, fieldPut.value(), valueBeforePut);
-                return;
-              }
+    for (Entry<DexEncodedField, List<FieldInitializationInfo>> entry : putsPerField.entrySet()) {
+      DexEncodedField field = entry.getKey();
+      List<FieldInitializationInfo> fieldPuts = entry.getValue();
+      if (fieldPuts.size() > 1) {
+        continue;
+      }
+      FieldInitializationInfo info = ListUtils.first(fieldPuts);
+      Instruction instruction = info.instruction;
+      if (instruction.isInvokeDirect()) {
+        asInstanceFieldValueAnalysis()
+            .recordInstanceFieldIsInitializedWithInfo(field, info.instanceFieldInitializationInfo);
+        continue;
+      }
+      FieldInstruction fieldPut = instruction.asFieldInstruction();
+      if (!isStraightLineCode) {
+        if (!getOrCreateDominatorTree().dominatesAllOf(fieldPut.getBlock(), normalExitBlocks)) {
+          continue;
+        }
+      }
+      boolean priorReadsWillReadSameValue =
+          !classInitializerDefaultsResult.hasStaticValue(field) && fieldPut.value().isZero();
+      if (!priorReadsWillReadSameValue && fieldMaybeReadBeforeInstruction(field, fieldPut)) {
+        // TODO(b/172528424): Generalize to InstanceFieldValueAnalysis.
+        if (isStaticFieldValueAnalysis()) {
+          // At this point the value read in the field can be only the default static value, if read
+          // prior to the put, or the value put, if read after the put. We still want to record it
+          // because the default static value is typically null/0, so code present after a null/0
+          // check can take advantage of the optimization.
+          DexValue valueBeforePut = classInitializerDefaultsResult.getStaticValue(field);
+          asStaticFieldValueAnalysis()
+              .updateFieldOptimizationInfoWith2Values(field, fieldPut.value(), valueBeforePut);
+        }
+        continue;
+      }
+      updateFieldOptimizationInfo(field, fieldPut, fieldPut.value());
+    }
+  }
+
+  private boolean fieldMaybeReadBeforeInstruction(
+      DexEncodedField encodedField, Instruction instruction) {
+    BasicBlock block = instruction.getBlock();
+
+    // First check if the field may be read in any of the (transitive) predecessor blocks.
+    if (fieldMaybeReadBeforeBlock(encodedField, block)) {
+      return true;
+    }
+
+    // Then check if any of the instructions that precede the given instruction in the current block
+    // may read the field.
+    InstructionIterator instructionIterator = block.iterator();
+    while (instructionIterator.hasNext()) {
+      Instruction current = instructionIterator.next();
+      if (current == instruction) {
+        break;
+      }
+      if (current.readSet(appView, context).contains(encodedField)) {
+        return true;
+      }
+    }
+
+    // Otherwise, the field is not read prior to the given instruction.
+    return false;
+  }
+
+  private boolean fieldMaybeReadBeforeBlock(DexEncodedField encodedField, BasicBlock block) {
+    for (BasicBlock predecessor : block.getPredecessors()) {
+      if (fieldMaybeReadBeforeBlockInclusive(encodedField, predecessor)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  private boolean fieldMaybeReadBeforeBlockInclusive(
+      DexEncodedField encodedField, BasicBlock block) {
+    return getOrCreateFieldsMaybeReadBeforeBlockInclusive().get(block).contains(encodedField);
+  }
+
+  /**
+   * Eagerly creates a mapping from each block to the set of fields that may be read in that block
+   * and its transitive predecessors.
+   */
+  private Map<BasicBlock, AbstractFieldSet> createFieldsMaybeReadBeforeBlockInclusive() {
+    Map<BasicBlock, AbstractFieldSet> result = new IdentityHashMap<>();
+    Deque<BasicBlock> worklist = DequeUtils.newArrayDeque(code.entryBlock());
+    while (!worklist.isEmpty()) {
+      BasicBlock block = worklist.removeFirst();
+      boolean seenBefore = result.containsKey(block);
+      AbstractFieldSet readSet =
+          result.computeIfAbsent(block, ignore -> EmptyFieldSet.getInstance());
+      if (readSet.isTop()) {
+        // We already have unknown information for this block.
+        continue;
+      }
+
+      assert readSet.isKnownFieldSet();
+      KnownFieldSet knownReadSet = readSet.asKnownFieldSet();
+      int oldSize = seenBefore ? knownReadSet.size() : -1;
+
+      // Everything that is read in the predecessor blocks should also be included in the read set
+      // for the current block, so here we join the information from the predecessor blocks into the
+      // current read set.
+      boolean blockOrPredecessorMaybeReadAnyField = false;
+      for (BasicBlock predecessor : block.getPredecessors()) {
+        AbstractFieldSet predecessorReadSet =
+            result.getOrDefault(predecessor, EmptyFieldSet.getInstance());
+        if (predecessorReadSet.isBottom()) {
+          continue;
+        }
+        if (predecessorReadSet.isTop()) {
+          blockOrPredecessorMaybeReadAnyField = true;
+          break;
+        }
+        assert predecessorReadSet.isConcreteFieldSet();
+        if (!knownReadSet.isConcreteFieldSet()) {
+          knownReadSet = new ConcreteMutableFieldSet();
+        }
+        knownReadSet.asConcreteFieldSet().addAll(predecessorReadSet.asConcreteFieldSet());
+      }
+
+      if (!blockOrPredecessorMaybeReadAnyField) {
+        // Finally, we update the read set with the fields that are read by the instructions in the
+        // current block. This can be skipped if the block has already been processed.
+        if (seenBefore) {
+          assert verifyFieldSetContainsAllFieldReadsInBlock(knownReadSet, block, context);
+        } else {
+          for (Instruction instruction : block.getInstructions()) {
+            AbstractFieldSet instructionReadSet = instruction.readSet(appView, context);
+            if (instructionReadSet.isBottom()) {
+              continue;
             }
+            if (instructionReadSet.isTop()) {
+              blockOrPredecessorMaybeReadAnyField = true;
+              break;
+            }
+            if (!knownReadSet.isConcreteFieldSet()) {
+              knownReadSet = new ConcreteMutableFieldSet();
+            }
+            knownReadSet.asConcreteFieldSet().addAll(instructionReadSet.asConcreteFieldSet());
           }
-          updateFieldOptimizationInfo(field, fieldPut, fieldPut.value());
-        });
+        }
+      }
+
+      boolean changed = false;
+      if (blockOrPredecessorMaybeReadAnyField) {
+        // Record that this block reads all fields.
+        result.put(block, UnknownFieldSet.getInstance());
+        changed = true;
+      } else {
+        if (knownReadSet != readSet) {
+          result.put(block, knownReadSet.asConcreteFieldSet());
+        }
+        if (knownReadSet.size() != oldSize) {
+          assert knownReadSet.size() > oldSize;
+          changed = true;
+        }
+      }
+
+      if (changed) {
+        // Rerun the analysis for all successors because the state of the current block changed.
+        worklist.addAll(block.getSuccessors());
+      }
+    }
+    return result;
+  }
+
+  private boolean verifyFieldSetContainsAllFieldReadsInBlock(
+      KnownFieldSet readSet, BasicBlock block, ProgramMethod context) {
+    for (Instruction instruction : block.getInstructions()) {
+      AbstractFieldSet instructionReadSet = instruction.readSet(appView, context);
+      assert !instructionReadSet.isTop();
+      if (instructionReadSet.isBottom()) {
+        continue;
+      }
+      for (DexEncodedField field : instructionReadSet.asConcreteFieldSet().getFields()) {
+        assert readSet.contains(field);
+      }
+    }
+    return true;
   }
 
   abstract void updateFieldOptimizationInfo(
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/InstanceFieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/InstanceFieldValueAnalysis.java
index 9ae014f..c4061ec 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/InstanceFieldValueAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/InstanceFieldValueAnalysis.java
@@ -11,9 +11,7 @@
 import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.ProgramField;
 import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.analysis.fieldaccess.readbeforewrite.FieldReadBeforeWriteAnalysis;
 import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
@@ -52,10 +50,9 @@
       AppView<AppInfoWithLiveness> appView,
       IRCode code,
       OptimizationFeedback feedback,
-      FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis,
       DexClassAndMethod parentConstructor,
       InvokeDirect parentConstructorCall) {
-    super(appView, code, feedback, fieldReadBeforeWriteAnalysis);
+    super(appView, code, feedback);
     this.factory = appView.instanceFieldInitializationInfoFactory();
     this.parentConstructor = parentConstructor;
     this.parentConstructorCall = parentConstructorCall;
@@ -70,11 +67,10 @@
       IRCode code,
       ClassInitializerDefaultsResult classInitializerDefaultsResult,
       OptimizationFeedback feedback,
-      FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis,
       Timing timing) {
     timing.begin("Analyze instance initializer");
     InstanceFieldInitializationInfoCollection result =
-        run(appView, code, classInitializerDefaultsResult, feedback, fieldReadBeforeWriteAnalysis);
+        run(appView, code, classInitializerDefaultsResult, feedback);
     timing.end();
     return result;
   }
@@ -83,8 +79,7 @@
       AppView<?> appView,
       IRCode code,
       ClassInitializerDefaultsResult classInitializerDefaultsResult,
-      OptimizationFeedback feedback,
-      FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis) {
+      OptimizationFeedback feedback) {
     assert appView.appInfo().hasLiveness();
     assert appView.enableWholeProgramOptimizations();
     assert code.context().getDefinition().isInstanceInitializer();
@@ -106,7 +101,6 @@
             appView.withLiveness(),
             code,
             feedback,
-            fieldReadBeforeWriteAnalysis,
             parentConstructor,
             parentConstructorCall);
     analysis.computeFieldOptimizationInfo(classInitializerDefaultsResult);
@@ -125,12 +119,12 @@
   }
 
   @Override
-  boolean isSubjectToOptimization(ProgramField field) {
-    return !field.getAccessFlags().isStatic() && field.getHolderType() == context.getHolderType();
+  boolean isSubjectToOptimization(DexEncodedField field) {
+    return !field.isStatic() && field.getHolderType() == context.getHolderType();
   }
 
   @Override
-  boolean isSubjectToOptimizationIgnoringPinning(ProgramField field) {
+  boolean isSubjectToOptimizationIgnoringPinning(DexEncodedField field) {
     throw new Unreachable("Used by static analysis only.");
   }
 
@@ -209,7 +203,7 @@
     if (abstractValue.isSingleValue()) {
       return abstractValue.asSingleValue();
     }
-    DexType fieldType = field.getType();
+    DexType fieldType = field.type();
     if (fieldType.isClassType()) {
       ClassTypeElement dynamicLowerBoundType = value.getDynamicLowerBoundType(appView);
       TypeElement dynamicUpperBoundType = value.getDynamicUpperBoundType(appView);
@@ -235,7 +229,7 @@
 
   private boolean fieldNeverWrittenBetweenInstancePutAndMethodExit(
       DexEncodedField field, InstancePut instancePut) {
-    if (field.getAccessFlags().isFinal()) {
+    if (field.isFinal()) {
       return true;
     }
 
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
index 5f8381d..4df6b75 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
@@ -15,8 +15,6 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DexValue;
 import com.android.tools.r8.graph.DexValue.DexValueNull;
-import com.android.tools.r8.graph.ProgramField;
-import com.android.tools.r8.ir.analysis.fieldaccess.readbeforewrite.FieldReadBeforeWriteAnalysis;
 import com.android.tools.r8.ir.analysis.type.DynamicTypeWithUpperBound;
 import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
@@ -51,11 +49,8 @@
   private final Map<Value, AbstractValue> computedValues = new IdentityHashMap<>();
 
   private StaticFieldValueAnalysis(
-      AppView<AppInfoWithLiveness> appView,
-      IRCode code,
-      OptimizationFeedback feedback,
-      FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis) {
-    super(appView, code, feedback, fieldReadBeforeWriteAnalysis);
+      AppView<AppInfoWithLiveness> appView, IRCode code, OptimizationFeedback feedback) {
+    super(appView, code, feedback);
     builder = StaticFieldValues.builder(code.context().getHolder());
   }
 
@@ -64,15 +59,13 @@
       IRCode code,
       ClassInitializerDefaultsResult classInitializerDefaultsResult,
       OptimizationFeedback feedback,
-      FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis,
       Timing timing) {
     assert appView.appInfo().hasLiveness();
     assert appView.enableWholeProgramOptimizations();
     assert code.context().getDefinition().isClassInitializer();
     timing.begin("Analyze class initializer");
     StaticFieldValues result =
-        new StaticFieldValueAnalysis(
-                appView.withLiveness(), code, feedback, fieldReadBeforeWriteAnalysis)
+        new StaticFieldValueAnalysis(appView.withLiveness(), code, feedback)
             .analyze(classInitializerDefaultsResult);
     timing.end();
     return result;
@@ -124,22 +117,19 @@
   }
 
   @Override
-  boolean isSubjectToOptimization(ProgramField field) {
-    return field.getAccessFlags().isStatic()
+  boolean isSubjectToOptimization(DexEncodedField field) {
+    return field.isStatic()
         && field.getHolderType() == context.getHolderType()
-        && appView
-            .appInfo()
-            .isFieldOnlyWrittenInMethod(field.getDefinition(), context.getDefinition());
+        && appView.appInfo().isFieldOnlyWrittenInMethod(field, context.getDefinition());
   }
 
   @Override
-  boolean isSubjectToOptimizationIgnoringPinning(ProgramField field) {
-    return field.getAccessFlags().isStatic()
+  boolean isSubjectToOptimizationIgnoringPinning(DexEncodedField field) {
+    return field.isStatic()
         && field.getHolderType() == context.getHolderType()
         && appView
             .appInfo()
-            .isFieldOnlyWrittenInMethodIgnoringPinning(
-                field.getDefinition(), context.getDefinition());
+            .isFieldOnlyWrittenInMethodIgnoringPinning(field, context.getDefinition());
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Monitor.java b/src/main/java/com/android/tools/r8/ir/code/Monitor.java
index 3f5727b..025df81 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Monitor.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Monitor.java
@@ -17,6 +17,7 @@
 import com.android.tools.r8.ir.conversion.DexBuilder;
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.InliningConstraints;
+import com.android.tools.r8.lightir.LirBuilder;
 
 public class Monitor extends Instruction {
 
@@ -137,6 +138,11 @@
   }
 
   @Override
+  public void buildLir(LirBuilder<Value, ?> builder) {
+    builder.addMonitor(type, object());
+  }
+
+  @Override
   public boolean throwsNpeIfValueIsNull(Value value, AppView<?> appView, ProgramMethod context) {
     return object() == value;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java b/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java
index a829247..477bd14 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java
@@ -24,6 +24,7 @@
 import com.android.tools.r8.ir.optimize.DeadCodeRemover.DeadInstructionResult;
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.InliningConstraints;
+import com.android.tools.r8.lightir.LirBuilder;
 
 public class NewArrayEmpty extends Instruction {
 
@@ -159,6 +160,11 @@
   }
 
   @Override
+  public void buildLir(LirBuilder<Value, ?> builder) {
+    builder.addNewArrayEmpty(size(), type);
+  }
+
+  @Override
   public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, ProgramMethod context) {
     return false;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/code/NumberConversion.java b/src/main/java/com/android/tools/r8/ir/code/NumberConversion.java
index 55ed123..72e5bc1 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NumberConversion.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NumberConversion.java
@@ -26,6 +26,7 @@
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.conversion.CfBuilder;
 import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.lightir.LirBuilder;
 import java.util.Set;
 
 public class NumberConversion extends Unop {
@@ -164,6 +165,11 @@
   }
 
   @Override
+  public void buildLir(LirBuilder<Value, ?> builder) {
+    builder.addNumberConversion(from, to, source());
+  }
+
+  @Override
   public boolean outTypeKnownToBeBoolean(Set<Phi> seen) {
     return to == NumericType.BYTE && source().knownToBeBoolean(seen);
   }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index bc02268..fc8d1f2 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -18,7 +18,6 @@
 import com.android.tools.r8.ir.analysis.VerifyTypesHelper;
 import com.android.tools.r8.ir.analysis.constant.SparseConditionalConstantPropagation;
 import com.android.tools.r8.ir.analysis.fieldaccess.FieldAccessAnalysis;
-import com.android.tools.r8.ir.analysis.fieldaccess.readbeforewrite.FieldReadBeforeWriteAnalysis;
 import com.android.tools.r8.ir.analysis.fieldvalueanalysis.InstanceFieldValueAnalysis;
 import com.android.tools.r8.ir.analysis.fieldvalueanalysis.StaticFieldValueAnalysis;
 import com.android.tools.r8.ir.analysis.fieldvalueanalysis.StaticFieldValues;
@@ -980,16 +979,10 @@
       timing.end();
     }
 
-    FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis =
-        FieldReadBeforeWriteAnalysis.create(appView, code, method);
     if (fieldAccessAnalysis != null) {
       timing.begin("Analyze field accesses");
       fieldAccessAnalysis.recordFieldAccesses(
-          code,
-          bytecodeMetadataProviderBuilder,
-          feedback,
-          fieldReadBeforeWriteAnalysis,
-          methodProcessor);
+          code, bytecodeMetadataProviderBuilder, feedback, methodProcessor);
       if (classInitializerDefaultsResult != null) {
         fieldAccessAnalysis.acceptClassInitializerDefaultsResult(classInitializerDefaultsResult);
       }
@@ -1006,21 +999,11 @@
       if (method.getDefinition().isClassInitializer()) {
         staticFieldValues =
             StaticFieldValueAnalysis.run(
-                appView,
-                code,
-                classInitializerDefaultsResult,
-                feedback,
-                fieldReadBeforeWriteAnalysis,
-                timing);
+                appView, code, classInitializerDefaultsResult, feedback, timing);
       } else {
         instanceFieldInitializationInfos =
             InstanceFieldValueAnalysis.run(
-                appView,
-                code,
-                classInitializerDefaultsResult,
-                feedback,
-                fieldReadBeforeWriteAnalysis,
-                timing);
+                appView, code, classInitializerDefaultsResult, feedback, timing);
       }
     }
     enumUnboxer.recordEnumState(method.getHolder(), staticFieldValues);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/callgraph/CallSiteInformation.java b/src/main/java/com/android/tools/r8/ir/conversion/callgraph/CallSiteInformation.java
index 125b53c..c2e13af 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/callgraph/CallSiteInformation.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/callgraph/CallSiteInformation.java
@@ -86,7 +86,7 @@
           continue;
         }
 
-        if (method.getDefinition().isDefaultInitializer()
+        if (method.getDefinition().isDefaultInstanceInitializer()
             && appView.hasProguardCompatibilityActions()
             && appView.getProguardCompatibilityActions().isCompatInstantiated(method.getHolder())) {
           continue;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/ContentProviderClientMethodRewrites.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/ContentProviderClientMethodRewrites.java
index 8881483..5d1114b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/backports/ContentProviderClientMethodRewrites.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/ContentProviderClientMethodRewrites.java
@@ -4,8 +4,16 @@
 
 package com.android.tools.r8.ir.desugar.backports;
 
+import com.android.tools.r8.cf.code.CfInstruction;
 import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.cf.code.CfStackInstruction;
+import com.android.tools.r8.cf.code.CfStackInstruction.Opcode;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.ir.desugar.BackportedMethodRewriter.FullMethodInvokeRewriter;
 import com.android.tools.r8.ir.desugar.BackportedMethodRewriter.MethodInvokeRewriter;
+import com.android.tools.r8.ir.desugar.LocalStackAllocator;
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
 import org.objectweb.asm.Opcodes;
 
 public final class ContentProviderClientMethodRewrites {
@@ -15,10 +23,18 @@
   public static MethodInvokeRewriter rewriteClose() {
     // Rewrite android/content/ContentProviderClient#close to
     // android/content/ContentProviderClient#recycle
-    return (invoke, factory) ->
-        new CfInvoke(
-            Opcodes.INVOKEVIRTUAL,
-            factory.androidContentContentProviderClientMembers.release,
-            false);
+    return new FullMethodInvokeRewriter() {
+      @Override
+      public Collection<CfInstruction> rewrite(
+          CfInvoke invoke, DexItemFactory factory, LocalStackAllocator localStackAllocator) {
+        // The invoke consumes the stack value and pushes another assumed to be the same.
+        return ImmutableList.of(
+            new CfInvoke(
+                Opcodes.INVOKEVIRTUAL,
+                factory.androidContentContentProviderClientMembers.release,
+                false),
+            new CfStackInstruction(Opcode.Pop));
+      }
+    };
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
index c92342f..f8e784e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
@@ -420,10 +420,9 @@
                 }
                 builder.setParent(invokedMethod);
               } else {
-                builder.markAllFieldsAsRead();
-                if (invoke.instructionMayHaveSideEffects(appView, context)) {
-                  builder.setMayHaveOtherSideEffectsThanInstanceFieldAssignments();
-                }
+                builder
+                    .markAllFieldsAsRead()
+                    .setMayHaveOtherSideEffectsThanInstanceFieldAssignments();
                 for (Value inValue : invoke.inValues()) {
                   if (couldBeReceiverValue(inValue, receiver, aliasesThroughAssumeAndCheckCasts)) {
                     builder.setReceiverMayEscapeOutsideConstructorChain();
@@ -454,10 +453,9 @@
           case INVOKE_VIRTUAL:
             {
               InvokeMethod invoke = instruction.asInvokeMethod();
-              builder.markAllFieldsAsRead();
-              if (invoke.instructionMayHaveSideEffects(appView, context)) {
-                builder.setMayHaveOtherSideEffectsThanInstanceFieldAssignments();
-              }
+              builder
+                  .markAllFieldsAsRead()
+                  .setMayHaveOtherSideEffectsThanInstanceFieldAssignments();
               for (Value argument : invoke.arguments()) {
                 if (couldBeReceiverValue(argument, receiver, aliasesThroughAssumeAndCheckCasts)) {
                   builder.setReceiverMayEscapeOutsideConstructorChain();
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java
index a9d515a..d63d8e7 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java
@@ -95,7 +95,8 @@
     Kotlin kotlin = appView.dexItemFactory().kotlin;
     KotlinClassMetadata kMetadata = toKotlinClassMetadata(kotlin, annotation.annotation);
     if (kMetadata == null) {
-      throw new KotlinMetadataException();
+      throw new KotlinMetadataException(
+          new Exception("Could not parse metadata for " + clazz.toSourceString()));
     }
     return createKotlinInfo(kotlin, clazz, kMetadata, appView, keepByteCode);
   }
diff --git a/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java b/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java
index 78e84db..70c442b 100644
--- a/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java
+++ b/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java
@@ -36,9 +36,13 @@
 import com.android.tools.r8.ir.code.InvokeStatic;
 import com.android.tools.r8.ir.code.InvokeSuper;
 import com.android.tools.r8.ir.code.InvokeVirtual;
+import com.android.tools.r8.ir.code.Monitor;
+import com.android.tools.r8.ir.code.MonitorType;
 import com.android.tools.r8.ir.code.MoveException;
 import com.android.tools.r8.ir.code.Mul;
+import com.android.tools.r8.ir.code.NewArrayEmpty;
 import com.android.tools.r8.ir.code.NewInstance;
+import com.android.tools.r8.ir.code.NumberConversion;
 import com.android.tools.r8.ir.code.NumberGenerator;
 import com.android.tools.r8.ir.code.NumericType;
 import com.android.tools.r8.ir.code.Phi;
@@ -374,6 +378,14 @@
     }
 
     @Override
+    public void onNumberConversion(NumericType from, NumericType to, EV value) {
+      Value dest =
+          getOutValueForNextInstruction(
+              to.toDexType(appView.dexItemFactory()).toTypeElement(appView));
+      addInstruction(new NumberConversion(from, to, dest, getValue(value)));
+    }
+
+    @Override
     public void onIf(IfType ifKind, int blockIndex, EV valueIndex) {
       BasicBlock targetBlock = getBasicBlock(blockIndex);
       Value value = getValue(valueIndex);
@@ -468,6 +480,12 @@
     }
 
     @Override
+    public void onNewArrayEmpty(DexType type, EV size) {
+      Value dest = getOutValueForNextInstruction(type.toTypeElement(appView));
+      addInstruction(new NewArrayEmpty(dest, getValue(size), type));
+    }
+
+    @Override
     public void onThrow(EV exception) {
       addInstruction(new Throw(getValue(exception)));
       closeCurrentBlock();
@@ -550,5 +568,15 @@
       Value dest = getOutValueForNextInstruction(TypeElement.getInt());
       addInstruction(new Cmp(type, bias, dest, leftValue, rightValue));
     }
+
+    @Override
+    public void onMonitorEnter(EV value) {
+      addInstruction(new Monitor(MonitorType.ENTER, getValue(value)));
+    }
+
+    @Override
+    public void onMonitorExit(EV value) {
+      addInstruction(new Monitor(MonitorType.EXIT, getValue(value)));
+    }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/lightir/LirBuilder.java b/src/main/java/com/android/tools/r8/lightir/LirBuilder.java
index 024d6d7..91ce188 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirBuilder.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirBuilder.java
@@ -5,6 +5,7 @@
 
 import com.android.tools.r8.cf.code.CfArithmeticBinop;
 import com.android.tools.r8.cf.code.CfArithmeticBinop.Opcode;
+import com.android.tools.r8.cf.code.CfNumberConversion;
 import com.android.tools.r8.errors.Unimplemented;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DebugLocalInfo;
@@ -21,6 +22,7 @@
 import com.android.tools.r8.ir.code.Cmp.Bias;
 import com.android.tools.r8.ir.code.IRMetadata;
 import com.android.tools.r8.ir.code.IfType;
+import com.android.tools.r8.ir.code.MonitorType;
 import com.android.tools.r8.ir.code.NumericType;
 import com.android.tools.r8.ir.code.Position;
 import com.android.tools.r8.ir.code.Position.SyntheticPosition;
@@ -325,21 +327,36 @@
   }
 
   public LirBuilder<V, EV> addDiv(NumericType type, V leftValue, V rightValue) {
+    int opcode;
     switch (type) {
       case BYTE:
       case CHAR:
       case SHORT:
       case INT:
         {
-          return addInstructionTemplate(
-              LirOpcodes.IDIV, Collections.emptyList(), ImmutableList.of(leftValue, rightValue));
+          opcode = LirOpcodes.IDIV;
+          break;
         }
       case LONG:
+        {
+          opcode = LirOpcodes.LDIV;
+          break;
+        }
       case FLOAT:
+        {
+          opcode = LirOpcodes.FDIV;
+          break;
+        }
       case DOUBLE:
+        {
+          opcode = LirOpcodes.DDIV;
+          break;
+        }
       default:
-        throw new Unimplemented();
+        throw new Unreachable("Unexpected type: " + type);
     }
+    return addInstructionTemplate(
+        opcode, Collections.emptyList(), ImmutableList.of(leftValue, rightValue));
   }
 
   public LirBuilder<V, EV> addArrayLength(V array) {
@@ -399,7 +416,7 @@
   }
 
   public LirBuilder<V, EV> addReturn(V value) {
-    throw new Unimplemented();
+    return addOneValueInstruction(LirOpcodes.ARETURN, value);
   }
 
   public LirBuilder<V, EV> addReturnVoid() {
@@ -556,4 +573,21 @@
     int opcode = CfArithmeticBinop.getAsmOpcode(binop, type);
     return addTwoValueInstruction(opcode, leftValue, rightValue);
   }
+
+  public LirBuilder<V, EV> addMonitor(MonitorType type, V value) {
+    return addOneValueInstruction(
+        type == MonitorType.ENTER ? LirOpcodes.MONITORENTER : LirOpcodes.MONITOREXIT, value);
+  }
+
+  public LirBuilder<V, EV> addNewArrayEmpty(V size, DexType type) {
+    return addInstructionTemplate(
+        LirOpcodes.NEWARRAY, Collections.singletonList(type), Collections.singletonList(size));
+  }
+
+  public LirBuilder<V, EV> addNumberConversion(NumericType from, NumericType to, V value) {
+    int opcode = new CfNumberConversion(from, to).getAsmOpcode();
+    assert LirOpcodes.I2L <= opcode;
+    assert opcode <= LirOpcodes.I2S;
+    return addOneValueInstruction(opcode, value);
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/lightir/LirOpcodes.java b/src/main/java/com/android/tools/r8/lightir/LirOpcodes.java
index a0300d2..302e85d 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirOpcodes.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirOpcodes.java
@@ -148,11 +148,13 @@
   // int RET = 169;
   int TABLESWITCH = 170;
   int LOOKUPSWITCH = 171;
-  int IRETURN = 172;
-  int LRETURN = 173;
-  int FRETURN = 174;
-  int DRETURN = 175;
+  // int IRETURN = 172;
+  // int LRETURN = 173;
+  // int FRETURN = 174;
+  // int DRETURN = 175;
+  // All value returns use areturn.
   int ARETURN = 176;
+  // Void return.
   int RETURN = 177;
   int GETSTATIC = 178;
   int PUTSTATIC = 179;
@@ -165,7 +167,8 @@
   int INVOKEDYNAMIC = 186;
   int NEW = 187;
   int NEWARRAY = 188;
-  int ANEWARRAY = 189;
+  // All arrays use NEWARRAY and a type item pointer
+  // int ANEWARRAY = 189;
   int ARRAYLENGTH = 190;
   int ATHROW = 191;
   int CHECKCAST = 192;
@@ -429,14 +432,6 @@
         return "TABLESWITCH";
       case LOOKUPSWITCH:
         return "LOOKUPSWITCH";
-      case IRETURN:
-        return "IRETURN";
-      case LRETURN:
-        return "LRETURN";
-      case FRETURN:
-        return "FRETURN";
-      case DRETURN:
-        return "DRETURN";
       case ARETURN:
         return "ARETURN";
       case RETURN:
@@ -463,8 +458,6 @@
         return "NEW";
       case NEWARRAY:
         return "NEWARRAY";
-      case ANEWARRAY:
-        return "ANEWARRAY";
       case ARRAYLENGTH:
         return "ARRAYLENGTH";
       case ATHROW:
diff --git a/src/main/java/com/android/tools/r8/lightir/LirParsedInstructionCallback.java b/src/main/java/com/android/tools/r8/lightir/LirParsedInstructionCallback.java
index 925e8af..de3a7b4 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirParsedInstructionCallback.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirParsedInstructionCallback.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.lightir;
 
+import com.android.tools.r8.cf.code.CfNumberConversion;
 import com.android.tools.r8.errors.Unimplemented;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexItem;
@@ -178,6 +179,17 @@
     onRem(NumericType.DOUBLE, leftValueIndex, rightValueIndex);
   }
 
+  public void onNumberConversion(int opcode, EV value) {
+    assert LirOpcodes.I2L <= opcode;
+    assert opcode <= LirOpcodes.I2S;
+    CfNumberConversion insn = CfNumberConversion.fromAsm(opcode);
+    onNumberConversion(insn.getFromType(), insn.getToType(), value);
+  }
+
+  public void onNumberConversion(NumericType from, NumericType to, EV value) {
+    onInstruction();
+  }
+
   public void onIf(IfType ifKind, int blockIndex, EV valueIndex) {
     onInstruction();
   }
@@ -238,6 +250,8 @@
 
   public abstract void onInstancePut(DexField field, EV object, EV value);
 
+  public abstract void onNewArrayEmpty(DexType type, EV size);
+
   public abstract void onThrow(EV exception);
 
   public void onReturnVoid() {
@@ -261,6 +275,10 @@
     onInstruction();
   }
 
+  public abstract void onMonitorEnter(EV value);
+
+  public abstract void onMonitorExit(EV value);
+
   private DexItem getConstantItem(int index) {
     return code.getConstantItem(index);
   }
@@ -487,6 +505,26 @@
           onRemDouble(leftValueIndex, rightValueIndex);
           return;
         }
+      case LirOpcodes.I2L:
+      case LirOpcodes.I2F:
+      case LirOpcodes.I2D:
+      case LirOpcodes.L2I:
+      case LirOpcodes.L2F:
+      case LirOpcodes.L2D:
+      case LirOpcodes.F2I:
+      case LirOpcodes.F2L:
+      case LirOpcodes.F2D:
+      case LirOpcodes.D2I:
+      case LirOpcodes.D2L:
+      case LirOpcodes.D2F:
+      case LirOpcodes.I2B:
+      case LirOpcodes.I2C:
+      case LirOpcodes.I2S:
+        {
+          EV value = getNextValueOperand(view);
+          onNumberConversion(opcode, value);
+          return;
+        }
       case LirOpcodes.IFNE:
         {
           int blockIndex = view.getNextBlockOperand();
@@ -568,6 +606,13 @@
           onInstancePut(field, object, value);
           return;
         }
+      case LirOpcodes.NEWARRAY:
+        {
+          DexType type = (DexType) getConstantItem(view.getNextConstantOperand());
+          EV size = getNextValueOperand(view);
+          onNewArrayEmpty(type, size);
+          return;
+        }
       case LirOpcodes.ATHROW:
         {
           EV exception = getNextValueOperand(view);
@@ -610,6 +655,18 @@
           onMoveException(type);
           return;
         }
+      case LirOpcodes.MONITORENTER:
+        {
+          EV value = getNextValueOperand(view);
+          onMonitorEnter(value);
+          return;
+        }
+      case LirOpcodes.MONITOREXIT:
+        {
+          EV value = getNextValueOperand(view);
+          onMonitorExit(value);
+          return;
+        }
       case LirOpcodes.DEBUGLOCALWRITE:
         {
           EV srcIndex = getNextValueOperand(view);
diff --git a/src/main/java/com/android/tools/r8/lightir/LirPrinter.java b/src/main/java/com/android/tools/r8/lightir/LirPrinter.java
index 965b6d7..19d5350 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirPrinter.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirPrinter.java
@@ -144,6 +144,12 @@
   }
 
   @Override
+  public void onNumberConversion(int opcode, EV value) {
+    appendOutValue();
+    appendValueArguments(value);
+  }
+
+  @Override
   public void onIf(IfType ifKind, int blockIndex, EV valueIndex) {
     builder.append(fmtValueIndex(valueIndex)).append(' ').append(fmtInsnIndex(blockIndex));
   }
@@ -202,6 +208,13 @@
   }
 
   @Override
+  public void onNewArrayEmpty(DexType type, EV size) {
+    appendOutValue();
+    builder.append(type).append(' ');
+    appendValueArguments(size);
+  }
+
+  @Override
   public void onThrow(EV exception) {
     appendValueArguments(exception);
   }
@@ -233,4 +246,14 @@
     appendOutValue();
     appendValueArguments(leftValue, rightValue);
   }
+
+  @Override
+  public void onMonitorEnter(EV value) {
+    appendValueArguments(value);
+  }
+
+  @Override
+  public void onMonitorExit(EV value) {
+    appendValueArguments(value);
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemovalOptions.java b/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemovalOptions.java
new file mode 100644
index 0000000..df58e45
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemovalOptions.java
@@ -0,0 +1,52 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.optimize.redundantbridgeremoval;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexLibraryClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.SetUtils;
+import java.util.Collections;
+import java.util.Set;
+
+public class RedundantBridgeRemovalOptions {
+
+  private final InternalOptions options;
+
+  private boolean enableRetargetingOfConstructorBridgeCalls = false;
+  private Set<DexType> noConstructorShrinkingHierarchies;
+
+  public RedundantBridgeRemovalOptions(InternalOptions options) {
+    this.options = options;
+  }
+
+  public void clearNoConstructorShrinkingHierarchiesForTesting() {
+    noConstructorShrinkingHierarchies = Collections.emptySet();
+  }
+
+  public RedundantBridgeRemovalOptions ensureInitialized() {
+    if (noConstructorShrinkingHierarchies == null) {
+      DexItemFactory dexItemFactory = options.dexItemFactory();
+      noConstructorShrinkingHierarchies =
+          SetUtils.newIdentityHashSet(
+              dexItemFactory.androidAppFragment, dexItemFactory.androidAppZygotePreload);
+    }
+    return this;
+  }
+
+  public boolean isPlatformReflectingOnDefaultConstructorInSubclasses(DexLibraryClass clazz) {
+    return noConstructorShrinkingHierarchies.contains(clazz.getType());
+  }
+
+  public boolean isRetargetingOfConstructorBridgeCallsEnabled() {
+    return enableRetargetingOfConstructorBridgeCalls;
+  }
+
+  public void setEnableRetargetingOfConstructorBridgeCalls(
+      boolean enableRetargetingOfConstructorBridgeCalls) {
+    this.enableRetargetingOfConstructorBridgeCalls = enableRetargetingOfConstructorBridgeCalls;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/RedundantBridgeRemover.java b/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemover.java
similarity index 72%
rename from src/main/java/com/android/tools/r8/optimize/RedundantBridgeRemover.java
rename to src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemover.java
index 763c40f..49b5d17 100644
--- a/src/main/java/com/android/tools/r8/optimize/RedundantBridgeRemover.java
+++ b/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemover.java
@@ -1,9 +1,10 @@
 // Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.optimize;
+package com.android.tools.r8.optimize.redundantbridgeremoval;
 
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexMethod;
@@ -15,23 +16,35 @@
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.PrunedItems;
 import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
+import com.android.tools.r8.optimize.InvokeSingleTargetExtractor;
 import com.android.tools.r8.optimize.InvokeSingleTargetExtractor.InvokeKind;
-import com.android.tools.r8.optimize.redundantbridgeremoval.RedundantBridgeRemovalLens;
+import com.android.tools.r8.optimize.MemberRebindingIdentityLens;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.KeepMethodInfo;
+import com.android.tools.r8.utils.IterableUtils;
 import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.WorkList;
 import com.android.tools.r8.utils.collections.ProgramMethodSet;
+import com.google.common.collect.Iterables;
+import java.util.Collections;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
+import java.util.function.Consumer;
 
 public class RedundantBridgeRemover {
 
   private final AppView<AppInfoWithLiveness> appView;
+  private final RedundantBridgeRemovalOptions redundantBridgeRemovalOptions;
+
+  private final InvokedReflectivelyFromPlatformAnalysis invokedReflectivelyFromPlatformAnalysis =
+      new InvokedReflectivelyFromPlatformAnalysis();
 
   public RedundantBridgeRemover(AppView<AppInfoWithLiveness> appView) {
     this.appView = appView;
+    this.redundantBridgeRemovalOptions =
+        appView.options().getRedundantBridgeRemovalOptions().ensureInitialized();
   }
 
   private DexClassAndMethod getTargetForRedundantBridge(ProgramMethod method) {
@@ -52,6 +65,9 @@
     if (!isTargetingSuperMethod(method, targetExtractor.getKind(), target)) {
       return null;
     }
+    if (invokedReflectivelyFromPlatformAnalysis.isMaybeInvokedReflectivelyFromPlatform(method)) {
+      return null;
+    }
     // This is a visibility forward, so check for the direct target.
     DexClassAndMethod targetMethod =
         appView.appInfo().unsafeResolveMethodDueToDexFormatLegacy(target).getResolutionPair();
@@ -159,7 +175,7 @@
                   // TODO(b/245882297): Refine these visibility checks so that we also rewrite when
                   //  the target is not public, but still accessible to call sites.
                   boolean isEligibleForRetargeting =
-                      appView.testing().enableRetargetingConstructorBridgeCalls
+                      redundantBridgeRemovalOptions.isRetargetingOfConstructorBridgeCallsEnabled()
                           || !method.getDefinition().isInstanceInitializer();
                   if (isEligibleForRetargeting
                       && target.getAccessFlags().isPublic()
@@ -236,4 +252,88 @@
         });
     appView.pruneItems(prunedItemsBuilder.build(), executorService);
   }
+
+  class InvokedReflectivelyFromPlatformAnalysis {
+
+    // Maps each class to a boolean indicating if the class inherits from android.app.Fragment or
+    // android.app.ZygotePreload.
+    private final Map<DexClass, Boolean> cache = new ConcurrentHashMap<>();
+
+    boolean isMaybeInvokedReflectivelyFromPlatform(ProgramMethod method) {
+      return method.getDefinition().isDefaultInstanceInitializer()
+          && !method.getHolder().isAbstract()
+          && computeIsPlatformReflectingOnDefaultConstructor(method.getHolder());
+    }
+
+    private boolean computeIsPlatformReflectingOnDefaultConstructor(DexProgramClass clazz) {
+      Boolean cacheResult = cache.get(clazz);
+      if (cacheResult != null) {
+        return cacheResult;
+      }
+      WorkList.<WorklistItem>newIdentityWorkList(new NotProcessedWorklistItem(clazz))
+          .process(WorklistItem::accept);
+      assert cache.containsKey(clazz);
+      return cache.get(clazz);
+    }
+
+    abstract class WorklistItem implements Consumer<WorkList<WorklistItem>> {
+
+      protected final DexClass clazz;
+
+      WorklistItem(DexClass clazz) {
+        this.clazz = clazz;
+      }
+
+      Iterable<DexClass> getImmediateSupertypes() {
+        return IterableUtils.flatMap(
+            clazz.allImmediateSupertypes(),
+            supertype -> {
+              DexClass definition = appView.definitionFor(supertype);
+              return definition != null
+                  ? Collections.singletonList(definition)
+                  : Collections.emptyList();
+            });
+      }
+    }
+
+    class NotProcessedWorklistItem extends WorklistItem {
+
+      NotProcessedWorklistItem(DexClass clazz) {
+        super(clazz);
+      }
+
+      @Override
+      public void accept(WorkList<WorklistItem> worklist) {
+        // Enqueue a worklist item to process the current class after processing its super classes.
+        worklist.addFirstIgnoringSeenSet(new ProcessedWorklistItem(clazz));
+        // Enqueue all superclasses for processing.
+        for (DexClass supertype : getImmediateSupertypes()) {
+          if (!cache.containsKey(supertype)) {
+            worklist.addFirstIgnoringSeenSet(new NotProcessedWorklistItem(supertype));
+          }
+        }
+      }
+    }
+
+    class ProcessedWorklistItem extends WorklistItem {
+
+      ProcessedWorklistItem(DexClass clazz) {
+        super(clazz);
+      }
+
+      @Override
+      public void accept(WorkList<WorklistItem> worklist) {
+        cache.put(
+            clazz,
+            Iterables.any(
+                getImmediateSupertypes(),
+                supertype ->
+                    cache.get(supertype)
+                        || (supertype.isLibraryClass()
+                            && redundantBridgeRemovalOptions
+                                .isPlatformReflectingOnDefaultConstructorInSubclasses(
+                                    supertype.asLibraryClass()))));
+      }
+    }
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/retrace/Retrace.java b/src/main/java/com/android/tools/r8/retrace/Retrace.java
index 3d2740a..a8cabe6 100644
--- a/src/main/java/com/android/tools/r8/retrace/Retrace.java
+++ b/src/main/java/com/android/tools/r8/retrace/Retrace.java
@@ -10,8 +10,6 @@
 import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.Keep;
 import com.android.tools.r8.Version;
-import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.references.TypeReference;
 import com.android.tools.r8.retrace.internal.ResultWithContextImpl;
 import com.android.tools.r8.retrace.internal.RetraceAbortException;
 import com.android.tools.r8.retrace.internal.StackTraceElementStringProxy;
@@ -159,17 +157,17 @@
   }
 
   private final StackTraceLineParser<T, ST> stackTraceLineParser;
-  private final StackTraceElementProxyRetracer<T, ST> proxyRetracer;
+  private final MappingSupplier<?> mappingSupplier;
   private final DiagnosticsHandler diagnosticsHandler;
   protected final boolean isVerbose;
 
   Retrace(
       StackTraceLineParser<T, ST> stackTraceLineParser,
-      StackTraceElementProxyRetracer<T, ST> proxyRetracer,
+      MappingSupplier<?> mappingSupplier,
       DiagnosticsHandler diagnosticsHandler,
       boolean isVerbose) {
     this.stackTraceLineParser = stackTraceLineParser;
-    this.proxyRetracer = proxyRetracer;
+    this.mappingSupplier = mappingSupplier;
     this.diagnosticsHandler = diagnosticsHandler;
     this.isVerbose = isVerbose;
   }
@@ -207,6 +205,10 @@
       List<ST> stackTrace, RetraceStackTraceContext context) {
     RetraceStackTraceElementProxyEquivalence<T, ST> equivalence =
         new RetraceStackTraceElementProxyEquivalence<>(isVerbose);
+    stackTrace.forEach(proxy -> proxy.registerUses(mappingSupplier, diagnosticsHandler));
+    StackTraceElementProxyRetracer<T, ST> proxyRetracer =
+        StackTraceElementProxyRetracer.createDefault(
+            mappingSupplier.createRetracer(diagnosticsHandler));
     List<List<List<T>>> finalResult = new ArrayList<>();
     RetraceStackTraceContext finalContext =
         ListUtils.fold(
@@ -260,6 +262,10 @@
     Map<RetraceStackTraceElementProxy<T, ST>, List<T>> ambiguousBlocks = new HashMap<>();
     List<RetraceStackTraceElementProxy<T, ST>> ambiguousKeys = new ArrayList<>();
     ST parsedLine = stackTraceLineParser.parse(stackTraceFrame);
+    parsedLine.registerUses(mappingSupplier, diagnosticsHandler);
+    StackTraceElementProxyRetracer<T, ST> proxyRetracer =
+        StackTraceElementProxyRetracer.createDefault(
+            mappingSupplier.createRetracer(diagnosticsHandler));
     Box<RetraceStackTraceContext> contextBox = new Box<>(context);
     proxyRetracer.retrace(parsedLine, context).stream()
         .forEach(
@@ -289,6 +295,10 @@
    */
   public ResultWithContext<T> retraceLine(T stackTraceLine, RetraceStackTraceContext context) {
     ST parsedLine = stackTraceLineParser.parse(stackTraceLine);
+    parsedLine.registerUses(mappingSupplier, diagnosticsHandler);
+    StackTraceElementProxyRetracer<T, ST> proxyRetracer =
+        StackTraceElementProxyRetracer.createDefault(
+            mappingSupplier.createRetracer(diagnosticsHandler));
     Box<RetraceStackTraceContext> contextBox = new Box<>(context);
     List<T> result =
         proxyRetracer.retrace(parsedLine, context).stream()
@@ -335,31 +345,10 @@
           lineNumber += 1;
         }
         timing.end();
-        parsedStackTrace.forEach(
-            proxy -> {
-              if (proxy.hasClassName()) {
-                mappingSupplier.registerClassUse(diagnosticsHandler, proxy.getClassReference());
-              }
-              if (proxy.hasMethodArguments()) {
-                Arrays.stream(proxy.getMethodArguments().split(","))
-                    .forEach(
-                        typeName ->
-                            registerUseFromTypeReference(
-                                mappingSupplier, typeName, diagnosticsHandler));
-              }
-              if (proxy.hasFieldOrReturnType() && !proxy.getFieldOrReturnType().equals("void")) {
-                registerUseFromTypeReference(
-                    mappingSupplier, proxy.getFieldOrReturnType(), diagnosticsHandler);
-              }
-            });
         timing.begin("Read proguard map");
         StringRetrace stringRetracer =
             new StringRetrace(
-                stackTraceLineParser,
-                StackTraceElementProxyRetracer.createDefault(
-                    mappingSupplier.createRetracer(diagnosticsHandler)),
-                diagnosticsHandler,
-                options.isVerbose());
+                stackTraceLineParser, mappingSupplier, diagnosticsHandler, options.isVerbose());
         timing.end();
         timing.begin("Retracing");
         ResultWithContext<String> result = stringRetracer.retraceParsed(parsedStackTrace, context);
@@ -389,17 +378,6 @@
     }
   }
 
-  private static void registerUseFromTypeReference(
-      MappingSupplier<?> mappingSupplier, String typeName, DiagnosticsHandler diagnosticsHandler) {
-    TypeReference typeReference = Reference.typeFromTypeName(typeName);
-    if (typeReference.isArray()) {
-      typeReference = typeReference.asArray().getBaseType();
-    }
-    if (typeReference.isClass()) {
-      mappingSupplier.registerClassUse(diagnosticsHandler, typeReference.asClass());
-    }
-  }
-
   public static void run(String[] args) throws RetraceFailedException {
     // To be compatible with standard retrace and remapper, we translate -arg into --arg.
     String[] mappedArgs = new String[args.length];
@@ -502,7 +480,7 @@
   public static class Builder<T, ST extends StackTraceElementProxy<T, ST>> {
 
     private StackTraceLineParser<T, ST> stackTraceLineParser;
-    private StackTraceElementProxyRetracer<T, ST> proxyRetracer;
+    private MappingSupplier<?> mappingSupplier;
     private DiagnosticsHandler diagnosticsHandler;
     protected boolean isVerbose;
 
@@ -512,12 +490,8 @@
       return this;
     }
 
-    public Builder<T, ST> setRetracer(Retracer retracer) {
-      return setProxyRetracer(StackTraceElementProxyRetracer.createDefault(retracer));
-    }
-
-    public Builder<T, ST> setProxyRetracer(StackTraceElementProxyRetracer<T, ST> proxyRetracer) {
-      this.proxyRetracer = proxyRetracer;
+    public Builder<T, ST> setMappingSupplier(MappingSupplier<?> mappingSupplier) {
+      this.mappingSupplier = mappingSupplier;
       return this;
     }
 
@@ -532,7 +506,7 @@
     }
 
     public Retrace<T, ST> build() {
-      return new Retrace<>(stackTraceLineParser, proxyRetracer, diagnosticsHandler, isVerbose);
+      return new Retrace<>(stackTraceLineParser, mappingSupplier, diagnosticsHandler, isVerbose);
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/retrace/StackTraceElementProxy.java b/src/main/java/com/android/tools/r8/retrace/StackTraceElementProxy.java
index 46c7eff..d18f748 100644
--- a/src/main/java/com/android/tools/r8/retrace/StackTraceElementProxy.java
+++ b/src/main/java/com/android/tools/r8/retrace/StackTraceElementProxy.java
@@ -4,8 +4,12 @@
 
 package com.android.tools.r8.retrace;
 
+import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.Keep;
 import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.references.TypeReference;
+import java.util.Arrays;
 
 @Keep
 public abstract class StackTraceElementProxy<T, ST extends StackTraceElementProxy<T, ST>> {
@@ -40,4 +44,31 @@
 
   public abstract T toRetracedItem(
       RetraceStackTraceElementProxy<T, ST> retracedProxy, boolean verbose);
+
+  public void registerUses(
+      MappingSupplier<?> mappingSupplier, DiagnosticsHandler diagnosticsHandler) {
+    if (hasClassName()) {
+      mappingSupplier.registerClassUse(diagnosticsHandler, getClassReference());
+    }
+    if (hasMethodArguments()) {
+      Arrays.stream(getMethodArguments().split(","))
+          .forEach(
+              typeName ->
+                  registerUseFromTypeReference(mappingSupplier, typeName, diagnosticsHandler));
+    }
+    if (hasFieldOrReturnType() && !getFieldOrReturnType().equals("void")) {
+      registerUseFromTypeReference(mappingSupplier, getFieldOrReturnType(), diagnosticsHandler);
+    }
+  }
+
+  private static void registerUseFromTypeReference(
+      MappingSupplier<?> mappingSupplier, String typeName, DiagnosticsHandler diagnosticsHandler) {
+    TypeReference typeReference = Reference.typeFromTypeName(typeName);
+    if (typeReference.isArray()) {
+      typeReference = typeReference.asArray().getBaseType();
+    }
+    if (typeReference.isClass()) {
+      mappingSupplier.registerClassUse(diagnosticsHandler, typeReference.asClass());
+    }
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/retrace/StringRetrace.java b/src/main/java/com/android/tools/r8/retrace/StringRetrace.java
index 5a7a797..2810d8b 100644
--- a/src/main/java/com/android/tools/r8/retrace/StringRetrace.java
+++ b/src/main/java/com/android/tools/r8/retrace/StringRetrace.java
@@ -27,10 +27,10 @@
 
   StringRetrace(
       StackTraceLineParser<String, StackTraceElementStringProxy> stackTraceLineParser,
-      StackTraceElementProxyRetracer<String, StackTraceElementStringProxy> proxyRetracer,
+      MappingSupplier<?> mappingSupplier,
       DiagnosticsHandler diagnosticsHandler,
       boolean isVerbose) {
-    super(stackTraceLineParser, proxyRetracer, diagnosticsHandler, isVerbose);
+    super(stackTraceLineParser, mappingSupplier, diagnosticsHandler, isVerbose);
   }
 
   /**
@@ -41,7 +41,7 @@
    */
   public static StringRetrace create(RetraceOptions command) {
     return create(
-        command.getMappingSupplier().createRetracer(command.getDiagnosticsHandler()),
+        command.getMappingSupplier(),
         command.getDiagnosticsHandler(),
         command.getRegularExpression(),
         command.isVerbose());
@@ -51,20 +51,20 @@
    * Entry point for creating a retracer designed for string input and output where the mapping file
    * has already been parsed.
    *
-   * @param retracer a loaded retracer with parsed mapping
+   * @param mappingSupplier a supplier that can be used to construct a retracer
    * @param diagnosticsHandler a diagnosticshandler for emitting information
    * @param regularExpression the regular expression to use for identifying information in strings
    * @param isVerbose specify to emit verbose information
    * @return a StringRetrace object
    */
   public static StringRetrace create(
-      Retracer retracer,
+      MappingSupplier<?> mappingSupplier,
       DiagnosticsHandler diagnosticsHandler,
       String regularExpression,
       boolean isVerbose) {
     return new StringRetrace(
         StackTraceLineParser.createRegularExpressionParser(regularExpression),
-        StackTraceElementProxyRetracer.createDefault(retracer),
+        mappingSupplier,
         diagnosticsHandler,
         isVerbose);
   }
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/ProguardMapReaderWithFiltering.java b/src/main/java/com/android/tools/r8/retrace/internal/ProguardMapReaderWithFiltering.java
index 7c30dcd..282e28a 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/ProguardMapReaderWithFiltering.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/ProguardMapReaderWithFiltering.java
@@ -4,9 +4,6 @@
 
 package com.android.tools.r8.retrace.internal;
 
-import static com.android.tools.r8.retrace.internal.ProguardMapReaderWithFiltering.LineParserState.COMPLETE_CLASS_MAPPING;
-import static com.android.tools.r8.retrace.internal.ProguardMapReaderWithFiltering.LineParserState.IS_COMMENT_SOURCE_FILE;
-import static com.android.tools.r8.retrace.internal.ProguardMapReaderWithFiltering.LineParserState.NOT_CLASS_MAPPING_OR_SOURCE_FILE;
 import static java.lang.Integer.MAX_VALUE;
 
 import com.android.tools.r8.errors.Unreachable;
@@ -23,20 +20,9 @@
 
 public abstract class ProguardMapReaderWithFiltering implements LineReader {
 
-  // The LineParserState encodes a simple state that the line parser can be in, where the
-  // (successful) transitions allowed are:
+  private static final byte[] SOURCE_FILE_BYTES = "sourceFile".getBytes();
 
-  // BEGINNING -> BEGINNING_NO_WHITESPACE -> SEEN_ORIGINAL_CLASS || IS_COMMENT_START
-
-  // IS_COMMENT_START -> IS_COMMENT_SOURCE_FILE
-
-  // SEEN_ORIGINAL_CLASS -> SEEN_ARROW -> SEEN_OBFUSCATED_CLASS -> COMPLETE_CLASS_MAPPING
-  //
-  // From all states there is a transition on invalid input to NOT_CLASS_MAPPING_OR_SOURCE_FILE.
-  // The terminal states are:
-  // { IS_COMMENT_SOURCE_FILE, COMPLETE_CLASS_MAPPING, NOT_CLASS_MAPPING_OR_SOURCE_FILE }
-  //
-  public enum LineParserState {
+  public enum LineParserNode {
     BEGINNING,
     BEGINNING_NO_WHITESPACE,
     SEEN_ORIGINAL_CLASS,
@@ -52,62 +38,87 @@
           || this == COMPLETE_CLASS_MAPPING
           || this == IS_COMMENT_SOURCE_FILE;
     }
+  }
 
-    private static int currentIndex;
-    private static int endIndex;
-    private static byte[] bytes;
-    private static final byte[] SOURCE_FILE_BYTES = "sourceFile".getBytes();
+  // The LineParserState encodes a simple state that the line parser can be in, where the
+  // (successful) transitions allowed are:
 
-    public static LineParserState computeState(byte[] bytes, int startIndex, int endIndex) {
-      currentIndex = startIndex;
-      LineParserState.endIndex = endIndex;
-      LineParserState.bytes = bytes;
-      LineParserState currentState = BEGINNING;
-      while (!currentState.isTerminal()) {
-        currentState = currentState.computeNextState();
-      }
-      return currentState;
+  // BEGINNING -> BEGINNING_NO_WHITESPACE -> SEEN_ORIGINAL_CLASS || IS_COMMENT_START
+
+  // IS_COMMENT_START -> IS_COMMENT_SOURCE_FILE
+
+  // SEEN_ORIGINAL_CLASS -> SEEN_ARROW -> SEEN_OBFUSCATED_CLASS -> COMPLETE_CLASS_MAPPING
+  //
+  // From all states there is a transition on invalid input to NOT_CLASS_MAPPING_OR_SOURCE_FILE.
+  // The terminal states are:
+  // { IS_COMMENT_SOURCE_FILE, COMPLETE_CLASS_MAPPING, NOT_CLASS_MAPPING_OR_SOURCE_FILE }
+  //
+  private static class LineParserState {
+
+    private int currentIndex;
+    private final int endIndex;
+    private final byte[] bytes;
+    private LineParserNode node;
+
+    private LineParserState(byte[] bytes, int currentIndex, int endIndex) {
+      this.currentIndex = currentIndex;
+      this.endIndex = endIndex;
+      this.bytes = bytes;
+      node = LineParserNode.BEGINNING;
     }
 
-    private LineParserState computeNextState() {
-      assert this != NOT_CLASS_MAPPING_OR_SOURCE_FILE;
-      switch (this) {
+    private LineParserNode run() {
+      while (!node.isTerminal()) {
+        node = computeNextState();
+      }
+      return node;
+    }
+
+    private LineParserNode computeNextState() {
+      assert node != LineParserNode.NOT_CLASS_MAPPING_OR_SOURCE_FILE;
+      switch (node) {
         case BEGINNING:
           return readUntilNoWhiteSpace()
-              ? BEGINNING_NO_WHITESPACE
-              : NOT_CLASS_MAPPING_OR_SOURCE_FILE;
+              ? LineParserNode.BEGINNING_NO_WHITESPACE
+              : LineParserNode.NOT_CLASS_MAPPING_OR_SOURCE_FILE;
         case BEGINNING_NO_WHITESPACE:
           if (isCommentChar()) {
-            return IS_COMMENT_START;
+            return LineParserNode.IS_COMMENT_START;
           } else {
             int readLength = readCharactersNoWhiteSpaceUntil(' ');
-            return readLength > 0 ? SEEN_ORIGINAL_CLASS : NOT_CLASS_MAPPING_OR_SOURCE_FILE;
+            return readLength > 0
+                ? LineParserNode.SEEN_ORIGINAL_CLASS
+                : LineParserNode.NOT_CLASS_MAPPING_OR_SOURCE_FILE;
           }
         case SEEN_ORIGINAL_CLASS:
-          return readArrow() ? SEEN_ARROW : NOT_CLASS_MAPPING_OR_SOURCE_FILE;
+          return readArrow()
+              ? LineParserNode.SEEN_ARROW
+              : LineParserNode.NOT_CLASS_MAPPING_OR_SOURCE_FILE;
         case SEEN_ARROW:
           int colonIndex = readCharactersNoWhiteSpaceUntil(':');
-          return colonIndex > 0 ? SEEN_OBFUSCATED_CLASS : NOT_CLASS_MAPPING_OR_SOURCE_FILE;
+          return colonIndex > 0
+              ? LineParserNode.SEEN_OBFUSCATED_CLASS
+              : LineParserNode.NOT_CLASS_MAPPING_OR_SOURCE_FILE;
         case SEEN_OBFUSCATED_CLASS:
           boolean read = readColon();
           if (!read) {
-            return NOT_CLASS_MAPPING_OR_SOURCE_FILE;
+            return LineParserNode.NOT_CLASS_MAPPING_OR_SOURCE_FILE;
           }
           boolean noWhiteSpace = readUntilNoWhiteSpace();
           return (!noWhiteSpace || isCommentChar())
-              ? COMPLETE_CLASS_MAPPING
-              : NOT_CLASS_MAPPING_OR_SOURCE_FILE;
+              ? LineParserNode.COMPLETE_CLASS_MAPPING
+              : LineParserNode.NOT_CLASS_MAPPING_OR_SOURCE_FILE;
         case IS_COMMENT_START:
           if (readCharactersUntil('{')
               && readCharactersUntil(':')
               && readSingleOrDoubleQuote()
               && readSourceFile()) {
-            return IS_COMMENT_SOURCE_FILE;
+            return LineParserNode.IS_COMMENT_SOURCE_FILE;
           } else {
-            return NOT_CLASS_MAPPING_OR_SOURCE_FILE;
+            return LineParserNode.NOT_CLASS_MAPPING_OR_SOURCE_FILE;
           }
         default:
-          assert isTerminal();
+          assert node.isTerminal();
           throw new Unreachable("Should not compute next state on terminal state");
       }
     }
@@ -208,7 +219,7 @@
 
   private boolean isInsideClassOfInterest = false;
   private boolean seenFirstClass = false;
-  private LineParserState lineParserState = NOT_CLASS_MAPPING_OR_SOURCE_FILE;
+  private LineParserNode lineParserResult = LineParserNode.NOT_CLASS_MAPPING_OR_SOURCE_FILE;
 
   @Override
   public String readLine() throws IOException {
@@ -220,8 +231,8 @@
       if (filter == null) {
         return new String(bytes, startIndex, endIndex - startIndex, StandardCharsets.UTF_8);
       }
-      lineParserState = LineParserState.computeState(bytes, startIndex, endIndex);
-      if (lineParserState == COMPLETE_CLASS_MAPPING) {
+      lineParserResult = new LineParserState(bytes, startIndex, endIndex).run();
+      if (lineParserResult == LineParserNode.COMPLETE_CLASS_MAPPING) {
         seenFirstClass = true;
         String classMapping = getBufferAsString(bytes);
         String obfuscatedClassName = getObfuscatedClassName(classMapping);
@@ -229,7 +240,8 @@
         if (isInsideClassOfInterest || readPreambleAndSourceFiles) {
           return classMapping;
         }
-      } else if (lineParserState == IS_COMMENT_SOURCE_FILE && readPreambleAndSourceFiles) {
+      } else if (lineParserResult == LineParserNode.IS_COMMENT_SOURCE_FILE
+          && readPreambleAndSourceFiles) {
         return getBufferAsString(bytes);
       } else if (isInsideClassOfInterest || (!seenFirstClass && readPreambleAndSourceFiles)) {
         return getBufferAsString(bytes);
@@ -238,7 +250,7 @@
   }
 
   public boolean isClassMapping() {
-    return lineParserState == COMPLETE_CLASS_MAPPING;
+    return lineParserResult == LineParserNode.COMPLETE_CLASS_MAPPING;
   }
 
   private String getBufferAsString(byte[] bytes) {
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index 07a5bb2..39d8035 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -954,6 +954,14 @@
     return staticInitializer != null && isFieldOnlyWrittenInMethod(field, staticInitializer);
   }
 
+  public boolean mayPropagateArgumentsTo(ProgramMethod method) {
+    DexMethod reference = method.getReference();
+    return method.getDefinition().hasCode()
+        && !method.getDefinition().isLibraryMethodOverride().isPossiblyTrue()
+        && !neverReprocess.contains(reference)
+        && !keepInfo.getMethodInfo(method).isPinned(options());
+  }
+
   public boolean mayPropagateValueFor(
       AppView<AppInfoWithLiveness> appView, DexClassAndMember<?, ?> member) {
     assert checkIfObsolete();
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 722a412..f48a387 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -74,6 +74,7 @@
 import com.android.tools.r8.naming.ClassNameMapper;
 import com.android.tools.r8.naming.MapVersion;
 import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagatorEventConsumer;
+import com.android.tools.r8.optimize.redundantbridgeremoval.RedundantBridgeRemovalOptions;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.position.Position;
 import com.android.tools.r8.profile.art.ArtProfileOptions;
@@ -889,6 +890,8 @@
   private final OpenClosedInterfacesOptions openClosedInterfacesOptions =
       new OpenClosedInterfacesOptions();
   private final ProtoShrinkingOptions protoShrinking = new ProtoShrinkingOptions();
+  private final RedundantBridgeRemovalOptions redundantBridgeRemovalOptions =
+      new RedundantBridgeRemovalOptions(this);
   private final KotlinOptimizationOptions kotlinOptimizationOptions =
       new KotlinOptimizationOptions();
   private final ApiModelTestingOptions apiModelTestingOptions = new ApiModelTestingOptions();
@@ -957,6 +960,10 @@
     return cfCodeAnalysisOptions;
   }
 
+  public RedundantBridgeRemovalOptions getRedundantBridgeRemovalOptions() {
+    return redundantBridgeRemovalOptions;
+  }
+
   public DumpInputFlags getDumpInputFlags() {
     return dumpInputFlags;
   }
@@ -2141,7 +2148,6 @@
     public boolean enableDeadSwitchCaseElimination = true;
     public boolean enableInvokeSuperToInvokeVirtualRewriting = true;
     public boolean enableMultiANewArrayDesugaringForClassFiles = false;
-    public boolean enableRetargetingConstructorBridgeCalls = false;
     public boolean enableSyntheticSharing = true;
     public boolean enableSwitchToIfRewriting = true;
     public boolean enableEnumUnboxingDebugLogs =
@@ -2917,8 +2923,7 @@
   }
 
   public boolean canHaveNonReboundConstructorInvoke() {
-    // TODO(b/246679983): Turned off while diagnosing b/246679983.
-    return false && isGeneratingDex() && minApiLevel.isGreaterThanOrEqualTo(AndroidApiLevel.L);
+    return isGeneratingDex() && minApiLevel.isGreaterThanOrEqualTo(AndroidApiLevel.L);
   }
 
   // b/238399429 Some art 6 vms have issues with multiple monitors in the same method
diff --git a/src/main/java/com/android/tools/r8/utils/MapUtils.java b/src/main/java/com/android/tools/r8/utils/MapUtils.java
index 7c603b2..5757218 100644
--- a/src/main/java/com/android/tools/r8/utils/MapUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/MapUtils.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.utils;
 
 import com.android.tools.r8.utils.StringUtils.BraceType;
+import com.google.common.collect.ImmutableMap;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceMaps;
 import java.util.Collections;
@@ -13,6 +14,7 @@
 import java.util.Map.Entry;
 import java.util.function.BiFunction;
 import java.util.function.BiPredicate;
+import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.IntFunction;
 import java.util.function.Supplier;
@@ -41,9 +43,10 @@
     return ignore -> supplier.get();
   }
 
-  public static <K, V> IdentityHashMap<K, V> newIdentityHashMap(BiForEachable<K, V> forEachable) {
+  public static <K, V> IdentityHashMap<K, V> newIdentityHashMap(
+      Consumer<IdentityHashMap<K, V>> builder) {
     IdentityHashMap<K, V> map = new IdentityHashMap<>();
-    forEachable.forEach(map::put);
+    builder.accept(map);
     return map;
   }
 
@@ -54,6 +57,13 @@
     return map;
   }
 
+  public static <K, V> ImmutableMap<K, V> newImmutableMap(
+      Consumer<ImmutableMap.Builder<K, V>> consumer) {
+    ImmutableMap.Builder<K, V> builder = ImmutableMap.builder();
+    consumer.accept(builder);
+    return builder.build();
+  }
+
   public static <T> void removeIdentityMappings(Map<T, T> map) {
     map.entrySet().removeIf(entry -> entry.getKey() == entry.getValue());
   }
diff --git a/src/test/java/com/android/tools/r8/TestParameters.java b/src/test/java/com/android/tools/r8/TestParameters.java
index c22f43e..206a8ab 100644
--- a/src/test/java/com/android/tools/r8/TestParameters.java
+++ b/src/test/java/com/android/tools/r8/TestParameters.java
@@ -58,8 +58,7 @@
    * runtime error.
    */
   public boolean canHaveNonReboundConstructorInvoke() {
-    // TODO(b/246679983): Turned off while diagnosing b/246679983.
-    return false && isDexRuntime() && getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.L);
+    return isDexRuntime() && getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.L);
   }
 
   public boolean canUseDefaultAndStaticInterfaceMethods() {
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingAfterConstructorShrinkingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingAfterConstructorShrinkingTest.java
index 70f55b4..4283b1a 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingAfterConstructorShrinkingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingAfterConstructorShrinkingTest.java
@@ -10,9 +10,10 @@
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.utils.AndroidApiLevel;
-import org.junit.Assume;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
+import java.util.List;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -23,36 +24,43 @@
 public class HorizontalClassMergingAfterConstructorShrinkingTest extends TestBase {
 
   @Parameter(0)
+  public boolean enableRetargetingOfConstructorBridgeCalls;
+
+  @Parameter(1)
   public TestParameters parameters;
 
-  @Parameters(name = "{0}")
-  public static TestParametersCollection data() {
-    return getTestParameters()
-        .withDexRuntimes()
-        .withApiLevelsStartingAtIncluding(AndroidApiLevel.L)
-        .build();
+  @Parameters(name = "{1}, retarget: {0}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        BooleanUtils.values(),
+        getTestParameters()
+            .withDexRuntimes()
+            .withApiLevelsStartingAtIncluding(AndroidApiLevel.L)
+            .build());
   }
 
   @Test
   public void test() throws Exception {
-    // TODO(b/276385221): Disabled constructor shrinking for now
-    Assume.assumeTrue(parameters.canHaveNonReboundConstructorInvoke());
     assertTrue(parameters.canHaveNonReboundConstructorInvoke());
     testForR8(parameters.getBackend())
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
+            options ->
+                options
+                    .getRedundantBridgeRemovalOptions()
+                    .setEnableRetargetingOfConstructorBridgeCalls(
+                        enableRetargetingOfConstructorBridgeCalls))
+        .addOptionsModification(
             options -> options.horizontalClassMergerOptions().disableInitialRoundOfClassMerging())
         .addHorizontallyMergedClassesInspector(
-            inspector ->
-                inspector.assertIsCompleteMergeGroup(A.class, B.class).assertNoOtherClassesMerged())
+            HorizontallyMergedClassesInspector::assertNoClassesMerged)
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .setMinApi(parameters)
         .compile()
         .run(parameters.getRuntime(), Main.class)
-        // TODO(b/276385221): Should not trigger A.<init>.
-        .assertSuccessWithOutputLines("Ouch!", "B");
+        .assertSuccessWithOutputLines("B");
   }
 
   static class Main {
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingAfterConstructorShrinkingWithRepackagingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingAfterConstructorShrinkingWithRepackagingTest.java
new file mode 100644
index 0000000..e705e0c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingAfterConstructorShrinkingWithRepackagingTest.java
@@ -0,0 +1,180 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.classmerging.horizontal;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.KeepConstantArguments;
+import com.android.tools.r8.KeepUnusedArguments;
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
+import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+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;
+
+/**
+ * Similar to {@link HorizontalClassMergingAfterConstructorShrinkingTest}, but extended so that
+ * {@code B.<init>(Parent)} needs to be correctly lens rewritten in the horizontal class merger,
+ * since {@link Parent} is subject to repackaging, which runs prior to horizontal class merging.
+ */
+@RunWith(Parameterized.class)
+public class HorizontalClassMergingAfterConstructorShrinkingWithRepackagingTest extends TestBase {
+
+  @Parameter(0)
+  public boolean enableRetargetingOfConstructorBridgeCalls;
+
+  @Parameter(1)
+  public TestParameters parameters;
+
+  @Parameters(name = "{1}, retarget: {0}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        BooleanUtils.values(),
+        getTestParameters()
+            .withDexRuntimes()
+            .withApiLevelsStartingAtIncluding(AndroidApiLevel.L)
+            .build());
+  }
+
+  @Test
+  public void test() throws Exception {
+    assertTrue(parameters.canHaveNonReboundConstructorInvoke());
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(Main.class)
+        .addKeepRules("-repackageclasses")
+        .addOptionsModification(
+            options ->
+                options
+                    .getRedundantBridgeRemovalOptions()
+                    .setEnableRetargetingOfConstructorBridgeCalls(
+                        enableRetargetingOfConstructorBridgeCalls))
+        .addOptionsModification(
+            options -> options.horizontalClassMergerOptions().disableInitialRoundOfClassMerging())
+        .addHorizontallyMergedClassesInspector(
+            HorizontallyMergedClassesInspector::assertNoClassesMerged)
+        .enableConstantArgumentAnnotations()
+        .enableInliningAnnotations()
+        .enableNeverClassInliningAnnotations()
+        .enableUnusedArgumentAnnotations()
+        .setMinApi(parameters)
+        .compile()
+        .inspect(
+            inspector -> {
+              // Verify Parent and Parent.<init>(Parent) are present and that Parent has been
+              // repackaged
+              // into the default package.
+              ClassSubject parentClassSubject = inspector.clazz(Parent.class);
+              assertThat(parentClassSubject, isPresent());
+              assertEquals("", parentClassSubject.getDexProgramClass().getType().getPackageName());
+
+              MethodSubject parentInstanceInitializerSubject =
+                  parentClassSubject.uniqueInstanceInitializer();
+              assertThat(parentInstanceInitializerSubject, isPresent());
+              assertEquals(
+                  parentClassSubject.asTypeSubject(),
+                  parentInstanceInitializerSubject.getParameter(0));
+
+              // Verify that A and A.<init>(Parent) are present.
+              ClassSubject aClassSubject = inspector.clazz(A.class);
+              assertThat(aClassSubject, isPresent());
+              assertEquals("", aClassSubject.getDexProgramClass().getType().getPackageName());
+
+              MethodSubject aInstanceInitializerSubject = aClassSubject.uniqueInstanceInitializer();
+              assertThat(aInstanceInitializerSubject, isPresent());
+              assertEquals(
+                  parentClassSubject.asTypeSubject(), aInstanceInitializerSubject.getParameter(0));
+
+              // Verify that B's initializer was removed.
+              ClassSubject bClassSubject = inspector.clazz(B.class);
+              assertThat(bClassSubject, isPresent());
+              assertEquals("", bClassSubject.getDexProgramClass().getType().getPackageName());
+              assertEquals(
+                  0, bClassSubject.allMethods(FoundMethodSubject::isInstanceInitializer).size());
+            })
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("B");
+  }
+
+  public static class Main {
+
+    static {
+      new B(null).setFieldOnB().printFieldOnB();
+    }
+
+    public static void main(String[] args) {
+      if (System.currentTimeMillis() < 0) {
+        new A(null).setFieldOnA().printFieldOnA();
+      }
+    }
+  }
+
+  public static class Parent {
+
+    @KeepConstantArguments
+    @KeepUnusedArguments
+    public Parent(Parent parent) {}
+  }
+
+  @NeverClassInline
+  public static class A extends Parent {
+
+    Object field;
+
+    @KeepConstantArguments
+    public A(Parent parent) {
+      super(parent);
+      System.out.println("Ouch!");
+    }
+
+    @NeverInline
+    public A setFieldOnA() {
+      field = "A";
+      return this;
+    }
+
+    @NeverInline
+    public void printFieldOnA() {
+      System.out.println(field);
+    }
+  }
+
+  @NeverClassInline
+  public static class B extends Parent {
+
+    Object field;
+
+    // Removed by constructor shrinking.
+    @KeepConstantArguments
+    public B(Parent parent) {
+      super(parent);
+    }
+
+    @NeverInline
+    public B setFieldOnB() {
+      field = "B";
+      return this;
+    }
+
+    @NeverInline
+    public void printFieldOnB() {
+      System.out.println(field);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debug/KotlinInlineTest.java b/src/test/java/com/android/tools/r8/debug/KotlinInlineTest.java
index 2205c9c..14aff54 100644
--- a/src/test/java/com/android/tools/r8/debug/KotlinInlineTest.java
+++ b/src/test/java/com/android/tools/r8/debug/KotlinInlineTest.java
@@ -5,15 +5,16 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
-import static org.junit.Assume.assumeTrue;
 
 import com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion;
 import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.debug.DebugTestBase.JUnit3Wrapper.Command;
 import com.android.tools.r8.kotlin.AbstractR8KotlinTestBase;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.util.ArrayList;
 import java.util.List;
 import org.apache.harmony.jpda.tests.framework.jdwp.Value;
 import org.junit.Test;
@@ -257,11 +258,6 @@
 
   @Test
   public void testNestedInlining() throws Throwable {
-    assumeTrue(
-        "b/244704042: Incorrect step-into StringBuilder.",
-        parameters.isCfRuntime()
-            || !(parameters.getDexRuntimeVersion().isEqualTo(Version.V13_0_0)
-                || parameters.getDexRuntimeVersion().isEqualTo(Version.V14_0_0)));
     // Count the number of lines in the source file. This is needed to check that inlined code
     // refers to non-existing line numbers.
     Path sourceFilePath =
@@ -343,13 +339,7 @@
         stepInto(),
         checkLocals(left_mangledLvName, right_mangledLvName),
         // Enter "foo"
-        stepInto(),
-        // See b/207743106 for incorrect debug info on Kotlin 1.6.
-        applyIf(
-            kotlinParameters.getCompilerVersion() == KotlinCompilerVersion.KOTLINC_1_6_0,
-            this::stepInto),
-        checkMethod(DEBUGGEE_CLASS, "foo"),
-        checkLine(SOURCE_FILE, 34),
+        stepIntoFooWithWorkaroundKotlin16(),
         stepOut(),
         // We're back to the inline section, at the end of the lambda
         inspect(
@@ -401,15 +391,27 @@
         checkLocal(inlinee2_inlineScope),
         checkNoLocal(inlinee2_lambda1_inlineScope),
         checkNoLocal(inlinee2_lambda2_inlineScope),
-        // Enter the call to "foo"
-        stepInto(),
-        // See b/207743106 for incorrect debug info on Kotlin 1.6.
-        applyIf(
-            kotlinParameters.getCompilerVersion() == KotlinCompilerVersion.KOTLINC_1_6_0,
-            this::stepInto),
-        checkMethod(DEBUGGEE_CLASS, "foo"),
-        checkLine(SOURCE_FILE, 34),
+        stepIntoFooWithWorkaroundKotlin16(),
         run());
   }
 
+  // See b/207743106 for incorrect debug info on Kotlin 1.6.
+  private Command stepIntoFooWithWorkaroundKotlin16() {
+    boolean is16 = kotlinParameters.getCompilerVersion() == KotlinCompilerVersion.KOTLINC_1_6_0;
+    boolean stepHitsStringBuilder = parameters.isDexRuntimeVersionNewerThanOrEqual(Version.V13_0_0);
+    List<Command> commands = new ArrayList<>();
+    // Enter the call to "foo"
+    commands.add(stepInto());
+    // The code on kotlin 1.6 does not have an active line on entry, so advance to hit it.
+    if (is16) {
+      commands.add(stepInto());
+      // On newer VMs the stepInput will enter StringBuilder, so step out of that again.
+      if (stepHitsStringBuilder) {
+        commands.add(stepOut());
+      }
+    }
+    commands.add(checkMethod(DEBUGGEE_CLASS, "foo"));
+    commands.add(checkLine(SOURCE_FILE, 34));
+    return subcommands(commands);
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/AbstractBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/AbstractBackportTest.java
index 1ea71b6..b935de4 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/AbstractBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/AbstractBackportTest.java
@@ -14,6 +14,7 @@
 
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestBuilder;
+import com.android.tools.r8.TestDiagnosticMessages;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.errors.InterfaceDesugarMissingTypeDiagnostic;
 import com.android.tools.r8.utils.AndroidApiLevel;
@@ -144,7 +145,7 @@
     ignoredInvokes.add(methodName);
   }
 
-  private void configureProgram(TestBuilder<?, ?> builder) throws IOException {
+  protected void configureProgram(TestBuilder<?, ?> builder) throws IOException {
     builder.addProgramClasses(MiniAssert.class, IgnoreInvokes.class);
     if (testClass != null) {
       testClass.addAsProgramClass(builder);
@@ -162,33 +163,45 @@
         .assertSuccess();
   }
 
+  private void checkDiagnostics(TestDiagnosticMessages diagnostics) {
+    if (diagnostics.getWarnings().isEmpty()) {
+      diagnostics.assertNoMessages();
+      return;
+    }
+    // When compiling with an old android.jar some tests refer to non-present types.
+    // Check only java.util types are missing and that none of them are about the target
+    // type that is being backported.
+    diagnostics
+        .assertOnlyWarnings()
+        .assertAllWarningsMatch(diagnosticType(InterfaceDesugarMissingTypeDiagnostic.class))
+        .assertAllWarningsMatch(diagnosticMessage(containsString("java.util")))
+        .assertNoWarningsMatch(diagnosticMessage(containsString(targetClass.getName())));
+  }
+
   @Test
   public void testD8() throws Exception {
     parameters.assumeDexRuntime();
-      testForD8()
-          .setMinApi(parameters)
-          .apply(this::configureProgram)
-          .setIncludeClassesChecksum(true)
-          .compileWithExpectedDiagnostics(
-              diagnostics -> {
-                if (diagnostics.getWarnings().isEmpty()) {
-                  diagnostics.assertNoMessages();
-                  return;
-                }
-                // When compiling with an old android.jar some tests refer to non-present types.
-                // Check only java.util types are missing and that none of them are about the target
-                // type that is being backported.
-                diagnostics
-                    .assertOnlyWarnings()
-                    .assertAllWarningsMatch(
-                        diagnosticType(InterfaceDesugarMissingTypeDiagnostic.class))
-                    .assertAllWarningsMatch(diagnosticMessage(containsString("java.util")))
-                    .assertNoWarningsMatch(
-                        diagnosticMessage(containsString(targetClass.getName())));
-              })
-          .run(parameters.getRuntime(), testClassName)
-          .assertSuccess()
-          .inspect(this::assertDesugaring);
+    testForD8()
+        .setMinApi(parameters)
+        .apply(this::configureProgram)
+        .setIncludeClassesChecksum(true)
+        .compileWithExpectedDiagnostics(this::checkDiagnostics)
+        .run(parameters.getRuntime(), testClassName)
+        .assertSuccess()
+        .inspect(this::assertDesugaring);
+  }
+
+  @Test
+  public void testD8Cf() throws Exception {
+    parameters.assumeCfRuntime();
+    testForD8(Backend.CF)
+        .setMinApi(parameters)
+        .apply(this::configureProgram)
+        .setIncludeClassesChecksum(true)
+        .compileWithExpectedDiagnostics(this::checkDiagnostics)
+        .run(parameters.getRuntime(), testClassName)
+        .assertSuccess()
+        .inspect(this::assertDesugaring);
   }
 
   private void assertDesugaring(CodeInspector inspector) {
@@ -219,6 +232,10 @@
         + javaInvokeStatics, expectedTargetInvokes, actualTargetInvokes);
   }
 
+  public String getTestClassName() {
+    return testClassName;
+  }
+
   /** JUnit {@link Assert} isn't available in the VM runtime. This is a mini mirror of its API. */
   static abstract class MiniAssert {
     static void assertTrue(boolean value) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/AtomicReferenceArrayTest.java b/src/test/java/com/android/tools/r8/desugar/backports/AtomicReferenceArrayTest.java
index 1c95fa0..b4ec061 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/AtomicReferenceArrayTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/AtomicReferenceArrayTest.java
@@ -19,7 +19,8 @@
   public static Iterable<?> data() {
     return getTestParameters()
         .withDexRuntimesStartingFromExcluding(Version.V4_0_4)
-        .withAllApiLevels()
+        .withCfRuntimes()
+        .withAllApiLevelsAlsoForCf()
         .build();
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/AtomicReferenceFieldUpdaterTest.java b/src/test/java/com/android/tools/r8/desugar/backports/AtomicReferenceFieldUpdaterTest.java
index ac1bb54..50e0d50 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/AtomicReferenceFieldUpdaterTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/AtomicReferenceFieldUpdaterTest.java
@@ -19,7 +19,8 @@
   public static Iterable<?> data() {
     return getTestParameters()
         .withDexRuntimesStartingFromExcluding(Version.V4_0_4)
-        .withAllApiLevels()
+        .withCfRuntimes()
+        .withAllApiLevelsAlsoForCf()
         .build();
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/AtomicReferenceTest.java b/src/test/java/com/android/tools/r8/desugar/backports/AtomicReferenceTest.java
index 8506aec..64a27bf 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/AtomicReferenceTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/AtomicReferenceTest.java
@@ -19,7 +19,8 @@
   public static Iterable<?> data() {
     return getTestParameters()
         .withDexRuntimesStartingFromExcluding(Version.V4_0_4)
-        .withAllApiLevels()
+        .withCfRuntimes()
+        .withAllApiLevelsAlsoForCf()
         .build();
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/BooleanBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/BooleanBackportTest.java
index 3e4b973..ccee141 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/BooleanBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/BooleanBackportTest.java
@@ -14,9 +14,7 @@
 public final class BooleanBackportTest extends AbstractBackportTest {
   @Parameters(name = "{0}")
   public static Iterable<?> data() {
-    return getTestParameters()
-        .withAllRuntimesAndApiLevels()
-        .build();
+    return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
   }
 
   public BooleanBackportTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/ByteBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/ByteBackportJava9Test.java
index 81038d6..010f36d 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/ByteBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/ByteBackportJava9Test.java
@@ -23,7 +23,7 @@
     return getTestParameters()
         .withCfRuntimesStartingFromIncluding(CfVm.JDK9)
         .withDexRuntimes()
-        .withAllApiLevels()
+        .withAllApiLevelsAlsoForCf()
         .build();
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/ByteBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/ByteBackportTest.java
index e92fcc7..eccbd85 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/ByteBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/ByteBackportTest.java
@@ -14,9 +14,7 @@
 public final class ByteBackportTest extends AbstractBackportTest {
   @Parameters(name = "{0}")
   public static Iterable<?> data() {
-    return getTestParameters()
-        .withAllRuntimesAndApiLevels()
-        .build();
+    return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
   }
 
   public ByteBackportTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/CharSequenceBackportJava11Test.java b/src/test/java/com/android/tools/r8/desugar/backports/CharSequenceBackportJava11Test.java
index 65f2f55..42a85c0 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/CharSequenceBackportJava11Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/CharSequenceBackportJava11Test.java
@@ -4,6 +4,8 @@
 
 package com.android.tools.r8.desugar.backports;
 
+import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
+
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestRuntime.CfVm;
 import com.android.tools.r8.ToolHelper;
@@ -13,16 +15,14 @@
 import org.junit.runners.Parameterized;
 import org.junit.runners.Parameterized.Parameters;
 
-import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
-
 @RunWith(Parameterized.class)
 public final class CharSequenceBackportJava11Test extends AbstractBackportTest {
   @Parameters(name = "{0}")
   public static Iterable<?> data() {
     return getTestParameters()
         .withDexRuntimes()
-        .withAllApiLevels()
         .withCfRuntimesStartingFromIncluding(CfVm.JDK11)
+        .withAllApiLevelsAlsoForCf()
         .build();
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/CharacterBackportJava11Test.java b/src/test/java/com/android/tools/r8/desugar/backports/CharacterBackportJava11Test.java
index 9a03c16..735ba69 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/CharacterBackportJava11Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/CharacterBackportJava11Test.java
@@ -4,6 +4,8 @@
 
 package com.android.tools.r8.desugar.backports;
 
+import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
+
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestRuntime.CfVm;
 import com.android.tools.r8.ToolHelper;
@@ -13,16 +15,14 @@
 import org.junit.runners.Parameterized;
 import org.junit.runners.Parameterized.Parameters;
 
-import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
-
 @RunWith(Parameterized.class)
 public final class CharacterBackportJava11Test extends AbstractBackportTest {
   @Parameters(name = "{0}")
   public static Iterable<?> data() {
     return getTestParameters()
         .withDexRuntimes()
-        .withAllApiLevels()
         .withCfRuntimesStartingFromIncluding(CfVm.JDK11)
+        .withAllApiLevelsAlsoForCf()
         .build();
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/CharacterBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/CharacterBackportTest.java
index e4eef82..2345798 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/CharacterBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/CharacterBackportTest.java
@@ -14,9 +14,7 @@
 public final class CharacterBackportTest extends AbstractBackportTest {
   @Parameters(name = "{0}")
   public static Iterable<?> data() {
-    return getTestParameters()
-        .withAllRuntimesAndApiLevels()
-        .build();
+    return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
   }
 
   public CharacterBackportTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/CollectionsBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/CollectionsBackportTest.java
index 66af3d0..078bff1 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/CollectionsBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/CollectionsBackportTest.java
@@ -19,9 +19,7 @@
 public final class CollectionsBackportTest extends AbstractBackportTest {
   @Parameters(name = "{0}")
   public static Iterable<?> data() {
-    return getTestParameters()
-        .withAllRuntimesAndApiLevels()
-        .build();
+    return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
   }
 
   public CollectionsBackportTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/ContentProviderClientBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/ContentProviderClientBackportTest.java
index e15b419..8d3b5f2 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/ContentProviderClientBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/ContentProviderClientBackportTest.java
@@ -4,15 +4,15 @@
 
 package com.android.tools.r8.desugar.backports;
 
-import static org.junit.Assert.assertTrue;
+import static org.hamcrest.CoreMatchers.containsString;
 
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.ToolHelper.DexVm;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.google.common.collect.ImmutableList;
 import java.io.IOException;
+import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 import org.junit.runners.Parameterized.Parameters;
@@ -22,7 +22,7 @@
 
   @Parameters(name = "{0}")
   public static TestParametersCollection data() {
-    return getTestParameters().withDexRuntimesAndAllApiLevels().build();
+    return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
   }
 
   public ContentProviderClientBackportTest(TestParameters parameters) throws IOException {
@@ -42,9 +42,18 @@
     registerTarget(AndroidApiLevel.N, 1);
   }
 
+  @Test
+  public void testJvm() throws Exception {
+    parameters.assumeJvmTestParameters();
+    testForJvm(parameters)
+        .apply(this::configureProgram)
+        .run(parameters.getRuntime(), getTestClassName())
+        // Fails when not desugared.
+        .assertFailureWithErrorThatMatches(containsString("Failed: close should not be called"));
+  }
+
   private static byte[] getContentProviderClient(TestParameters parameters) throws IOException {
     if (parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N)) {
-      assertTrue(parameters.getRuntime().asDex().getVm().isNewerThanOrEqual(DexVm.ART_7_0_0_HOST));
       return transformer(ContentProviderClientApiLevel24.class)
           .setClassDescriptor(DexItemFactory.androidContentContentProviderClientDescriptorString)
           .transform();
@@ -70,8 +79,9 @@
       TestRunner.doFail("close should not be called");
     }
 
-    public void release() {
+    public boolean release() {
       wasClosed = true;
+      return wasClosed;
     }
   }
 
@@ -82,8 +92,9 @@
       wasClosed = true;
     }
 
-    public void release() {
+    public boolean release() {
       TestRunner.doFail("release should not be called");
+      return true;
     }
   }
 
@@ -92,8 +103,11 @@
     public static void main(String[] args) {
       ContentProviderClient contentProviderClient = new ContentProviderClient();
       MiniAssert.assertFalse(contentProviderClient.wasClosed);
-      contentProviderClient.close();
-      MiniAssert.assertTrue(contentProviderClient.wasClosed);
+      // Loop as regression test for b/276874854.
+      for (int i = 0; i < 2; i++) {
+        contentProviderClient.close();
+        MiniAssert.assertTrue(contentProviderClient.wasClosed);
+      }
     }
 
     // Forwards to MiniAssert to avoid having to make it public.
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/DoubleBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/DoubleBackportTest.java
index 16cdeda..ecd8ea9 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/DoubleBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/DoubleBackportTest.java
@@ -14,9 +14,7 @@
 public final class DoubleBackportTest extends AbstractBackportTest {
   @Parameters(name = "{0}")
   public static Iterable<?> data() {
-    return getTestParameters()
-        .withAllRuntimesAndApiLevels()
-        .build();
+    return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
   }
 
   public DoubleBackportTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/DrmManagerClientBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/DrmManagerClientBackportTest.java
index 6b16c6c..5d3ee55 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/DrmManagerClientBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/DrmManagerClientBackportTest.java
@@ -4,15 +4,15 @@
 
 package com.android.tools.r8.desugar.backports;
 
-import static org.junit.Assert.assertTrue;
+import static org.hamcrest.CoreMatchers.containsString;
 
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.ToolHelper.DexVm;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.google.common.collect.ImmutableList;
 import java.io.IOException;
+import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 import org.junit.runners.Parameterized.Parameters;
@@ -22,7 +22,7 @@
 
   @Parameters(name = "{0}")
   public static TestParametersCollection data() {
-    return getTestParameters().withDexRuntimesAndAllApiLevels().build();
+    return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
   }
 
   public DrmManagerClientBackportTest(TestParameters parameters) throws IOException {
@@ -42,9 +42,18 @@
     registerTarget(AndroidApiLevel.N, 1);
   }
 
+  @Test
+  public void testJvm() throws Exception {
+    parameters.assumeJvmTestParameters();
+    testForJvm(parameters)
+        .apply(this::configureProgram)
+        .run(parameters.getRuntime(), getTestClassName())
+        // Fails when not desugared.
+        .assertFailureWithErrorThatMatches(containsString("Failed: close should not be called"));
+  }
+
   private static byte[] getDrmManagerClient(TestParameters parameters) throws IOException {
     if (parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N)) {
-      assertTrue(parameters.getRuntime().asDex().getVm().isNewerThanOrEqual(DexVm.ART_7_0_0_HOST));
       return transformer(DrmManagerClientApiLevel24.class)
           .setClassDescriptor(DexItemFactory.androidDrmDrmManagerClientDescriptorString)
           .transform();
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/FloatBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/FloatBackportTest.java
index 92db3a6..591a41e 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/FloatBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/FloatBackportTest.java
@@ -14,9 +14,7 @@
 public final class FloatBackportTest extends AbstractBackportTest {
   @Parameters(name = "{0}")
   public static Iterable<?> data() {
-    return getTestParameters()
-        .withAllRuntimesAndApiLevels()
-        .build();
+    return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
   }
 
   public FloatBackportTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/IntegerBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/IntegerBackportJava9Test.java
index f8c5a53..5cc2e23 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/IntegerBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/IntegerBackportJava9Test.java
@@ -23,7 +23,7 @@
     return getTestParameters()
         .withCfRuntimesStartingFromIncluding(CfVm.JDK9)
         .withDexRuntimes()
-        .withAllApiLevels()
+        .withAllApiLevelsAlsoForCf()
         .build();
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/IntegerBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/IntegerBackportTest.java
index ed697a7..0d5ac49 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/IntegerBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/IntegerBackportTest.java
@@ -15,9 +15,7 @@
 public final class IntegerBackportTest extends AbstractBackportTest {
   @Parameters(name = "{0}")
   public static Iterable<?> data() {
-    return getTestParameters()
-        .withAllRuntimesAndApiLevels()
-        .build();
+    return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
   }
 
   public IntegerBackportTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/ListBackportJava10Test.java b/src/test/java/com/android/tools/r8/desugar/backports/ListBackportJava10Test.java
index 7b80e96..0f63b03 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/ListBackportJava10Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/ListBackportJava10Test.java
@@ -24,7 +24,7 @@
     return getTestParameters()
         .withCfRuntimesStartingFromIncluding(CfVm.JDK10)
         .withDexRuntimes()
-        .withAllApiLevels()
+        .withAllApiLevelsAlsoForCf()
         .build();
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/ListBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/ListBackportJava9Test.java
index ddf502b..604e82c 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/ListBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/ListBackportJava9Test.java
@@ -26,7 +26,7 @@
     return getTestParameters()
         .withCfRuntimesStartingFromIncluding(CfVm.JDK9)
         .withDexRuntimes()
-        .withAllApiLevels()
+        .withAllApiLevelsAlsoForCf()
         .build();
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/LongBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/LongBackportJava9Test.java
index 487f80f..8ffb905 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/LongBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/LongBackportJava9Test.java
@@ -23,7 +23,7 @@
     return getTestParameters()
         .withCfRuntimesStartingFromIncluding(CfVm.JDK9)
         .withDexRuntimes()
-        .withAllApiLevels()
+        .withAllApiLevelsAlsoForCf()
         .build();
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/LongBackportSingleMethodTest.java b/src/test/java/com/android/tools/r8/desugar/backports/LongBackportSingleMethodTest.java
index 7344bd8..39cc434 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/LongBackportSingleMethodTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/LongBackportSingleMethodTest.java
@@ -20,9 +20,7 @@
 
   @Parameters(name = "{0}")
   public static Iterable<?> data() {
-    return getTestParameters()
-        .withAllRuntimesAndApiLevels()
-        .build();
+    return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
   }
 
   public LongBackportSingleMethodTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/LongBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/LongBackportTest.java
index f8a6ad8..cbcf278 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/LongBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/LongBackportTest.java
@@ -15,9 +15,7 @@
 public final class LongBackportTest extends AbstractBackportTest {
   @Parameters(name = "{0}")
   public static Iterable<?> data() {
-    return getTestParameters()
-        .withAllRuntimesAndApiLevels()
-        .build();
+    return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
   }
 
   public LongBackportTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/MapBackportJava10Test.java b/src/test/java/com/android/tools/r8/desugar/backports/MapBackportJava10Test.java
index 11008cc..19c89e5 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/MapBackportJava10Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/MapBackportJava10Test.java
@@ -24,7 +24,7 @@
     return getTestParameters()
         .withCfRuntimesStartingFromIncluding(CfVm.JDK10)
         .withDexRuntimes()
-        .withAllApiLevels()
+        .withAllApiLevelsAlsoForCf()
         .build();
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/MapBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/MapBackportJava9Test.java
index 73ffa29..a5c1b1f 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/MapBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/MapBackportJava9Test.java
@@ -26,7 +26,7 @@
     return getTestParameters()
         .withCfRuntimesStartingFromIncluding(CfVm.JDK9)
         .withDexRuntimes()
-        .withAllApiLevels()
+        .withAllApiLevelsAlsoForCf()
         .build();
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/MathBackportJava17Test.java b/src/test/java/com/android/tools/r8/desugar/backports/MathBackportJava17Test.java
index 8f9332d..c612792 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/MathBackportJava17Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/MathBackportJava17Test.java
@@ -22,8 +22,8 @@
   public static Iterable<?> data() {
     return getTestParameters()
         .withDexRuntimes()
-        .withAllApiLevels()
         .withCfRuntimesStartingFromIncluding(CfVm.JDK17)
+        .withAllApiLevelsAlsoForCf()
         .build();
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/MathBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/MathBackportJava9Test.java
index 2f70fc2..f41ce56 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/MathBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/MathBackportJava9Test.java
@@ -22,8 +22,8 @@
   public static Iterable<?> data() {
     return getTestParameters()
         .withDexRuntimes()
-        .withAllApiLevels()
         .withCfRuntimesStartingFromIncluding(CfVm.JDK9)
+        .withAllApiLevelsAlsoForCf()
         .build();
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/MathBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/MathBackportTest.java
index acae4c9..36aa95e 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/MathBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/MathBackportTest.java
@@ -14,9 +14,7 @@
 public final class MathBackportTest extends AbstractBackportTest {
   @Parameters(name = "{0}")
   public static Iterable<?> data() {
-    return getTestParameters()
-        .withAllRuntimesAndApiLevels()
-        .build();
+    return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
   }
 
   public MathBackportTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/MediaDrmBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/MediaDrmBackportTest.java
index 500308b..144d841 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/MediaDrmBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/MediaDrmBackportTest.java
@@ -4,15 +4,15 @@
 
 package com.android.tools.r8.desugar.backports;
 
-import static org.junit.Assert.assertTrue;
+import static org.hamcrest.CoreMatchers.containsString;
 
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.ToolHelper.DexVm;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.google.common.collect.ImmutableList;
 import java.io.IOException;
+import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 import org.junit.runners.Parameterized.Parameters;
@@ -22,7 +22,7 @@
 
   @Parameters(name = "{0}")
   public static TestParametersCollection data() {
-    return getTestParameters().withDexRuntimesAndAllApiLevels().build();
+    return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
   }
 
   public MediaDrmBackportTest(TestParameters parameters) throws IOException {
@@ -41,9 +41,18 @@
     registerTarget(AndroidApiLevel.P, 1);
   }
 
+  @Test
+  public void testJvm() throws Exception {
+    parameters.assumeJvmTestParameters();
+    testForJvm(parameters)
+        .apply(this::configureProgram)
+        .run(parameters.getRuntime(), getTestClassName())
+        // Fails when not desugared.
+        .assertFailureWithErrorThatMatches(containsString("Failed: close should not be called"));
+  }
+
   private static byte[] getMediaDrm(TestParameters parameters) throws IOException {
     if (parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.P)) {
-      assertTrue(parameters.getRuntime().asDex().getVm().isNewerThanOrEqual(DexVm.ART_8_1_0_HOST));
       return transformer(MediaDrmApiLevel28.class)
           .setClassDescriptor(DexItemFactory.androidMediaMediaDrmDescriptorString)
           .transform();
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/MediaMetadataRetrieverBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/MediaMetadataRetrieverBackportTest.java
index b3cbfe2..d2cb8f8 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/MediaMetadataRetrieverBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/MediaMetadataRetrieverBackportTest.java
@@ -4,15 +4,15 @@
 
 package com.android.tools.r8.desugar.backports;
 
-import static org.junit.Assert.assertTrue;
+import static org.hamcrest.CoreMatchers.containsString;
 
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.ToolHelper.DexVm;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.google.common.collect.ImmutableList;
 import java.io.IOException;
+import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 import org.junit.runners.Parameterized.Parameters;
@@ -22,7 +22,7 @@
 
   @Parameters(name = "{0}")
   public static TestParametersCollection data() {
-    return getTestParameters().withDexRuntimesAndAllApiLevels().build();
+    return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
   }
 
   public MediaMetadataRetrieverBackportTest(TestParameters parameters) throws IOException {
@@ -42,9 +42,18 @@
     registerTarget(AndroidApiLevel.Q, 1);
   }
 
+  @Test
+  public void testJvm() throws Exception {
+    parameters.assumeJvmTestParameters();
+    testForJvm(parameters)
+        .apply(this::configureProgram)
+        .run(parameters.getRuntime(), getTestClassName())
+        // Fails when not desugared.
+        .assertFailureWithErrorThatMatches(containsString("Failed: close should not be called"));
+  }
+
   private static byte[] getMediaMetadataRetriever(TestParameters parameters) throws IOException {
     if (parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.Q)) {
-      assertTrue(parameters.getRuntime().asDex().getVm().isNewerThanOrEqual(DexVm.ART_10_0_0_HOST));
       return transformer(MediaMetadataRetrieverApiLevel29.class)
           .setClassDescriptor(DexItemFactory.androidMediaMediaMetadataRetrieverDescriptorString)
           .transform();
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/MethodBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/MethodBackportTest.java
index 78c2770..3597845 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/MethodBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/MethodBackportTest.java
@@ -14,7 +14,7 @@
 public final class MethodBackportTest extends AbstractBackportTest {
   @Parameters(name = "{0}")
   public static Iterable<?> data() {
-    return getTestParameters().withAllRuntimesAndApiLevels().build();
+    return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
   }
 
   public MethodBackportTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/ObjectsBackportJava17Test.java b/src/test/java/com/android/tools/r8/desugar/backports/ObjectsBackportJava17Test.java
index 4a5345fe..af9af9d 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/ObjectsBackportJava17Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/ObjectsBackportJava17Test.java
@@ -23,8 +23,8 @@
   public static Iterable<?> data() {
     return getTestParameters()
         .withDexRuntimes()
-        .withAllApiLevels()
         .withCfRuntimesStartingFromIncluding(CfVm.JDK17)
+        .withAllApiLevelsAlsoForCf()
         .build();
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/ObjectsBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/ObjectsBackportJava9Test.java
index f926a22..3864c5e 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/ObjectsBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/ObjectsBackportJava9Test.java
@@ -25,8 +25,8 @@
   public static Iterable<?> data() {
     return getTestParameters()
         .withDexRuntimes()
-        .withAllApiLevels()
         .withCfRuntimesStartingFromIncluding(CfVm.JDK9)
+        .withAllApiLevelsAlsoForCf()
         .build();
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/ObjectsBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/ObjectsBackportTest.java
index 5e034bc..63a9e06 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/ObjectsBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/ObjectsBackportTest.java
@@ -17,9 +17,7 @@
 public final class ObjectsBackportTest extends AbstractBackportTest {
   @Parameters(name = "{0}")
   public static Iterable<?> data() {
-    return getTestParameters()
-        .withAllRuntimesAndApiLevels()
-        .build();
+    return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
   }
 
   public ObjectsBackportTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/OptionalBackportJava10Test.java b/src/test/java/com/android/tools/r8/desugar/backports/OptionalBackportJava10Test.java
index c5e6adc..956a2fb 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/OptionalBackportJava10Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/OptionalBackportJava10Test.java
@@ -26,6 +26,7 @@
         .withDexRuntimesStartingFromIncluding(Version.V7_0_0)
         .withApiLevelsStartingAtIncluding(AndroidApiLevel.N)
         .withCfRuntimesStartingFromIncluding(CfVm.JDK10)
+        .enableApiLevelsForCf()
         .build();
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/OptionalBackportJava11Test.java b/src/test/java/com/android/tools/r8/desugar/backports/OptionalBackportJava11Test.java
index 8b57c0f..a7255b6 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/OptionalBackportJava11Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/OptionalBackportJava11Test.java
@@ -26,6 +26,7 @@
         .withDexRuntimesStartingFromIncluding(Version.V7_0_0)
         .withApiLevelsStartingAtIncluding(AndroidApiLevel.N)
         .withCfRuntimesStartingFromIncluding(CfVm.JDK11)
+        .enableApiLevelsForCf()
         .build();
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/OptionalBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/OptionalBackportJava9Test.java
index a726dc5..e71d3ca 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/OptionalBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/OptionalBackportJava9Test.java
@@ -26,6 +26,7 @@
         .withDexRuntimesStartingFromIncluding(Version.V7_0_0)
         .withApiLevelsStartingAtIncluding(AndroidApiLevel.N)
         .withCfRuntimesStartingFromIncluding(CfVm.JDK9)
+        .enableApiLevelsForCf()
         .build();
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/OptionalDoubleBackportJava10Test.java b/src/test/java/com/android/tools/r8/desugar/backports/OptionalDoubleBackportJava10Test.java
index a181a64..794e049 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/OptionalDoubleBackportJava10Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/OptionalDoubleBackportJava10Test.java
@@ -26,6 +26,7 @@
         .withDexRuntimesStartingFromIncluding(Version.V7_0_0)
         .withApiLevelsStartingAtIncluding(AndroidApiLevel.N)
         .withCfRuntimesStartingFromIncluding(CfVm.JDK10)
+        .enableApiLevelsForCf()
         .build();
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/OptionalDoubleBackportJava11Test.java b/src/test/java/com/android/tools/r8/desugar/backports/OptionalDoubleBackportJava11Test.java
index b5e9c02..377ed2e 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/OptionalDoubleBackportJava11Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/OptionalDoubleBackportJava11Test.java
@@ -26,6 +26,7 @@
         .withDexRuntimesStartingFromIncluding(Version.V7_0_0)
         .withApiLevelsStartingAtIncluding(AndroidApiLevel.N)
         .withCfRuntimesStartingFromIncluding(CfVm.JDK11)
+        .enableApiLevelsForCf()
         .build();
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/OptionalDoubleBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/OptionalDoubleBackportJava9Test.java
index bb9bb65..b3dacdd 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/OptionalDoubleBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/OptionalDoubleBackportJava9Test.java
@@ -26,6 +26,7 @@
         .withDexRuntimesStartingFromIncluding(Version.V7_0_0)
         .withApiLevelsStartingAtIncluding(AndroidApiLevel.N)
         .withCfRuntimesStartingFromIncluding(CfVm.JDK9)
+        .enableApiLevelsForCf()
         .build();
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/OptionalIntBackportJava10Test.java b/src/test/java/com/android/tools/r8/desugar/backports/OptionalIntBackportJava10Test.java
index 00f5b4b..ab44630 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/OptionalIntBackportJava10Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/OptionalIntBackportJava10Test.java
@@ -26,6 +26,7 @@
         .withDexRuntimesStartingFromIncluding(Version.V7_0_0)
         .withApiLevelsStartingAtIncluding(AndroidApiLevel.N)
         .withCfRuntimesStartingFromIncluding(CfVm.JDK10)
+        .enableApiLevelsForCf()
         .build();
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/OptionalIntBackportJava11Test.java b/src/test/java/com/android/tools/r8/desugar/backports/OptionalIntBackportJava11Test.java
index 62d66cd..95c6adc 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/OptionalIntBackportJava11Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/OptionalIntBackportJava11Test.java
@@ -26,6 +26,7 @@
         .withDexRuntimesStartingFromIncluding(Version.V7_0_0)
         .withApiLevelsStartingAtIncluding(AndroidApiLevel.N)
         .withCfRuntimesStartingFromIncluding(CfVm.JDK11)
+        .enableApiLevelsForCf()
         .build();
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/OptionalIntBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/OptionalIntBackportJava9Test.java
index 54f08ae..d061489 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/OptionalIntBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/OptionalIntBackportJava9Test.java
@@ -26,6 +26,7 @@
         .withDexRuntimesStartingFromIncluding(Version.V7_0_0)
         .withApiLevelsStartingAtIncluding(AndroidApiLevel.N)
         .withCfRuntimesStartingFromIncluding(CfVm.JDK9)
+        .enableApiLevelsForCf()
         .build();
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/OptionalLongBackportJava10Test.java b/src/test/java/com/android/tools/r8/desugar/backports/OptionalLongBackportJava10Test.java
index c353e92..5621a3c 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/OptionalLongBackportJava10Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/OptionalLongBackportJava10Test.java
@@ -26,6 +26,7 @@
         .withDexRuntimesStartingFromIncluding(Version.V7_0_0)
         .withApiLevelsStartingAtIncluding(AndroidApiLevel.N)
         .withCfRuntimesStartingFromIncluding(CfVm.JDK10)
+        .enableApiLevelsForCf()
         .build();
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/OptionalLongBackportJava11Test.java b/src/test/java/com/android/tools/r8/desugar/backports/OptionalLongBackportJava11Test.java
index fd8fc6f..7333cb9 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/OptionalLongBackportJava11Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/OptionalLongBackportJava11Test.java
@@ -26,6 +26,7 @@
         .withDexRuntimesStartingFromIncluding(Version.V7_0_0)
         .withApiLevelsStartingAtIncluding(AndroidApiLevel.N)
         .withCfRuntimesStartingFromIncluding(CfVm.JDK11)
+        .enableApiLevelsForCf()
         .build();
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/OptionalLongBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/OptionalLongBackportJava9Test.java
index efb516a..ea02d61 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/OptionalLongBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/OptionalLongBackportJava9Test.java
@@ -26,6 +26,7 @@
         .withDexRuntimesStartingFromIncluding(Version.V7_0_0)
         .withApiLevelsStartingAtIncluding(AndroidApiLevel.N)
         .withCfRuntimesStartingFromIncluding(CfVm.JDK9)
+        .enableApiLevelsForCf()
         .build();
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/PredicateBackportJava11Test.java b/src/test/java/com/android/tools/r8/desugar/backports/PredicateBackportJava11Test.java
index f98f769..d0a6c35 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/PredicateBackportJava11Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/PredicateBackportJava11Test.java
@@ -26,6 +26,7 @@
         .withDexRuntimesStartingFromIncluding(Version.V7_0_0)
         .withApiLevelsStartingAtIncluding(AndroidApiLevel.N)
         .withCfRuntimesStartingFromIncluding(CfVm.JDK11)
+        .enableApiLevelsForCf()
         .build();
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/SetBackportJava10Test.java b/src/test/java/com/android/tools/r8/desugar/backports/SetBackportJava10Test.java
index 2a17cb2..ec22ac6 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/SetBackportJava10Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/SetBackportJava10Test.java
@@ -24,7 +24,7 @@
     return getTestParameters()
         .withCfRuntimesStartingFromIncluding(CfVm.JDK10)
         .withDexRuntimes()
-        .withAllApiLevels()
+        .withAllApiLevelsAlsoForCf()
         .build();
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/SetBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/SetBackportJava9Test.java
index 652cde0..efdc935 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/SetBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/SetBackportJava9Test.java
@@ -26,7 +26,7 @@
     return getTestParameters()
         .withCfRuntimesStartingFromIncluding(CfVm.JDK9)
         .withDexRuntimes()
-        .withAllApiLevels()
+        .withAllApiLevelsAlsoForCf()
         .build();
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/ShortBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/ShortBackportJava9Test.java
index d1d3c95..d641452 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/ShortBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/ShortBackportJava9Test.java
@@ -23,7 +23,7 @@
     return getTestParameters()
         .withCfRuntimesStartingFromIncluding(CfVm.JDK9)
         .withDexRuntimes()
-        .withAllApiLevels()
+        .withAllApiLevelsAlsoForCf()
         .build();
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/ShortBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/ShortBackportTest.java
index 2d1a61a..05bc2db 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/ShortBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/ShortBackportTest.java
@@ -14,9 +14,7 @@
 public final class ShortBackportTest extends AbstractBackportTest {
   @Parameters(name = "{0}")
   public static Iterable<?> data() {
-    return getTestParameters()
-        .withAllRuntimesAndApiLevels()
-        .build();
+    return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
   }
 
   public ShortBackportTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/SparseArrayBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/SparseArrayBackportTest.java
index 1ebfe97..de8a8d1 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/SparseArrayBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/SparseArrayBackportTest.java
@@ -4,11 +4,13 @@
 
 package com.android.tools.r8.desugar.backports;
 
+import static org.hamcrest.CoreMatchers.containsString;
+
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper.DexVm;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.google.common.collect.ImmutableList;
 import java.io.IOException;
+import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 import org.junit.runners.Parameterized.Parameters;
@@ -20,7 +22,7 @@
 
   @Parameters(name = "{0}")
   public static Iterable<?> data() {
-    return getTestParameters().withDexRuntimes().withAllApiLevels().build();
+    return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
   }
 
   public SparseArrayBackportTest(TestParameters parameters) throws IOException {
@@ -40,9 +42,18 @@
     registerTarget(AndroidApiLevel.S, 1);
   }
 
+  @Test
+  public void testJvm() throws Exception {
+    parameters.assumeJvmTestParameters();
+    testForJvm(parameters)
+        .apply(this::configureProgram)
+        .run(parameters.getRuntime(), getTestClassName())
+        // Fails when not desugared.
+        .assertFailureWithErrorThatMatches(containsString("Failed: set should not be called"));
+  }
+
   private static byte[] getSparseArray(TestParameters parameters) throws IOException {
     if (parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.S)) {
-      assert parameters.getRuntime().asDex().getVm().isNewerThanOrEqual(DexVm.ART_12_0_0_HOST);
       return transformer(SparseArrayAndroid12.class)
           .setClassDescriptor(SPARSE_ARRAY_DESCRIPTOR)
           .transform();
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/StreamBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/StreamBackportJava9Test.java
index 5e33276..2416783 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/StreamBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/StreamBackportJava9Test.java
@@ -26,6 +26,7 @@
         .withDexRuntimesStartingFromIncluding(Version.V7_0_0)
         .withApiLevelsStartingAtIncluding(AndroidApiLevel.N)
         .withCfRuntimesStartingFromIncluding(CfVm.JDK9)
+        .enableApiLevelsForCf()
         .build();
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/StrictMathBackportJava17Test.java b/src/test/java/com/android/tools/r8/desugar/backports/StrictMathBackportJava17Test.java
index c93b1ee..f180eab 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/StrictMathBackportJava17Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/StrictMathBackportJava17Test.java
@@ -23,8 +23,8 @@
   public static Iterable<?> data() {
     return getTestParameters()
         .withDexRuntimes()
-        .withAllApiLevels()
         .withCfRuntimesStartingFromIncluding(CfVm.JDK17)
+        .withAllApiLevelsAlsoForCf()
         .build();
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/StrictMathBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/StrictMathBackportJava9Test.java
index 2f124e7..105c190 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/StrictMathBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/StrictMathBackportJava9Test.java
@@ -21,8 +21,8 @@
   public static Iterable<?> data() {
     return getTestParameters()
         .withDexRuntimes()
-        .withAllApiLevels()
         .withCfRuntimesStartingFromIncluding(CfVm.JDK9)
+        .withAllApiLevelsAlsoForCf()
         .build();
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/StrictMathBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/StrictMathBackportTest.java
index 1926975..b93bd2f 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/StrictMathBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/StrictMathBackportTest.java
@@ -14,9 +14,7 @@
 public final class StrictMathBackportTest extends AbstractBackportTest {
   @Parameters(name = "{0}")
   public static Iterable<?> data() {
-    return getTestParameters()
-        .withAllRuntimesAndApiLevels()
-        .build();
+    return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
   }
 
   public StrictMathBackportTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/StringBackportJava11Test.java b/src/test/java/com/android/tools/r8/desugar/backports/StringBackportJava11Test.java
index 6290238..e6d81ac 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/StringBackportJava11Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/StringBackportJava11Test.java
@@ -22,8 +22,8 @@
   public static Iterable<?> data() {
     return getTestParameters()
         .withDexRuntimes()
-        .withAllApiLevels()
         .withCfRuntimesStartingFromIncluding(CfVm.JDK11)
+        .withAllApiLevelsAlsoForCf()
         .build();
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/StringBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/StringBackportTest.java
index ff4350f..37d8462 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/StringBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/StringBackportTest.java
@@ -4,22 +4,20 @@
 
 package com.android.tools.r8.desugar.backports;
 
+import static java.util.Arrays.asList;
+import static java.util.Collections.emptyList;
+
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 import org.junit.runners.Parameterized.Parameters;
 
-import static java.util.Arrays.asList;
-import static java.util.Collections.emptyList;
-
 @RunWith(Parameterized.class)
 public final class StringBackportTest extends AbstractBackportTest {
   @Parameters(name = "{0}")
   public static Iterable<?> data() {
-    return getTestParameters()
-        .withAllRuntimesAndApiLevels()
-        .build();
+    return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
   }
 
   public StringBackportTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/TypedArrayBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/TypedArrayBackportTest.java
index 06e759d..1b7e834 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/TypedArrayBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/TypedArrayBackportTest.java
@@ -4,15 +4,15 @@
 
 package com.android.tools.r8.desugar.backports;
 
-import static org.junit.Assert.assertTrue;
+import static org.hamcrest.CoreMatchers.containsString;
 
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.ToolHelper.DexVm;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.google.common.collect.ImmutableList;
 import java.io.IOException;
+import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 import org.junit.runners.Parameterized.Parameters;
@@ -22,7 +22,7 @@
 
   @Parameters(name = "{0}")
   public static TestParametersCollection data() {
-    return getTestParameters().withDexRuntimesAndAllApiLevels().build();
+    return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
   }
 
   public TypedArrayBackportTest(TestParameters parameters) throws IOException {
@@ -42,9 +42,18 @@
     registerTarget(AndroidApiLevel.S, 1);
   }
 
+  @Test
+  public void testJvm() throws Exception {
+    parameters.assumeJvmTestParameters();
+    testForJvm(parameters)
+        .apply(this::configureProgram)
+        .run(parameters.getRuntime(), getTestClassName())
+        // Fails when not desugared.
+        .assertFailureWithErrorThatMatches(containsString("Failed: close should not be called"));
+  }
+
   private static byte[] getTypedArray(TestParameters parameters) throws IOException {
     if (parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.S)) {
-      assertTrue(parameters.getRuntime().asDex().getVm().isNewerThanOrEqual(DexVm.ART_12_0_0_HOST));
       return transformer(TypedArrayAndroidApiLevel31.class)
           .setClassDescriptor(DexItemFactory.androidContentResTypedArrayDescriptorString)
           .transform();
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java b/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
index bb382f8..2cdeffd 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
@@ -26,7 +26,6 @@
 import com.android.tools.r8.graph.DirectMappedDexApplication;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadataProvider;
-import com.android.tools.r8.ir.analysis.fieldaccess.readbeforewrite.FieldReadBeforeWriteAnalysis;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.conversion.MethodProcessorEventConsumer;
 import com.android.tools.r8.ir.conversion.MethodProcessorWithWave;
@@ -97,11 +96,7 @@
         method -> {
           IRCode code = method.buildIR(appView);
           fieldAccessAnalysis.recordFieldAccesses(
-              code,
-              BytecodeMetadataProvider.builder(),
-              feedback,
-              FieldReadBeforeWriteAnalysis.trivial(),
-              new PrimaryMethodProcessorMock());
+              code, BytecodeMetadataProvider.builder(), feedback, new PrimaryMethodProcessorMock());
         });
 
     int bitsReadInBitField = feedback.bitsReadPerField.getInt(uniqueFieldByName(clazz, "bitField"));
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/NonNullInstanceFieldPropagationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/NonNullInstanceFieldPropagationTest.java
deleted file mode 100644
index 972cc79..0000000
--- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/NonNullInstanceFieldPropagationTest.java
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.ir.optimize.membervaluepropagation;
-
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
-
-import com.android.tools.r8.TestBase;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import 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 NonNullInstanceFieldPropagationTest 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)
-        .compile()
-        .inspect(
-            inspector -> {
-              ClassSubject mainClassSubject = inspector.clazz(Main.class);
-              assertThat(mainClassSubject, isPresent());
-              assertEquals(1, inspector.allClasses().size());
-              assertEquals(1, mainClassSubject.allMethods().size());
-              assertEquals(0, mainClassSubject.allFields().size());
-            })
-        .run(parameters.getRuntime(), Main.class)
-        .assertSuccessWithEmptyOutput();
-  }
-
-  static class Main {
-
-    public static void main(String[] args) {
-      Main p = new Main();
-      p.mThreadChecker.assertOnValidThread();
-    }
-
-    public final ThreadChecker mThreadChecker = new ThreadChecker();
-  }
-
-  static class ThreadChecker {
-    public void assertOnValidThread() {
-      if (System.currentTimeMillis() == 0) {
-        throw new RuntimeException();
-      }
-    }
-  }
-}
diff --git a/src/test/java/com/android/tools/r8/naming/AdaptResourceFileNamesTest.java b/src/test/java/com/android/tools/r8/naming/AdaptResourceFileNamesTest.java
index 8078ed6..92404f0 100644
--- a/src/test/java/com/android/tools/r8/naming/AdaptResourceFileNamesTest.java
+++ b/src/test/java/com/android/tools/r8/naming/AdaptResourceFileNamesTest.java
@@ -101,7 +101,6 @@
         "  void <init>();",
         "}",
         "-neverclassinline class *",
-        "-nohorizontalclassmerging class adaptresourcefilenames.B",
         "-nohorizontalclassmerging class adaptresourcefilenames.pkg.C",
         "-nohorizontalclassmerging class adaptresourcefilenames.pkg.innerpkg.D");
   }
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/LambdaStaticLibraryMethodImplementationProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/LambdaStaticLibraryMethodImplementationProfileRewritingTest.java
index 2d38860..c83d81d 100644
--- a/src/test/java/com/android/tools/r8/profile/art/completeness/LambdaStaticLibraryMethodImplementationProfileRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/LambdaStaticLibraryMethodImplementationProfileRewritingTest.java
@@ -75,15 +75,22 @@
   }
 
   private void inspectD8(ArtProfileInspector profileInspector, CodeInspector inspector) {
-    inspect(profileInspector, inspector, false);
+    inspect(profileInspector, inspector, false, false);
   }
 
   private void inspectR8(ArtProfileInspector profileInspector, CodeInspector inspector) {
-    inspect(profileInspector, inspector, parameters.isCfRuntime());
+    inspect(
+        profileInspector,
+        inspector,
+        parameters.canHaveNonReboundConstructorInvoke(),
+        parameters.isCfRuntime());
   }
 
   public void inspect(
-      ArtProfileInspector profileInspector, CodeInspector inspector, boolean canUseLambdas) {
+      ArtProfileInspector profileInspector,
+      CodeInspector inspector,
+      boolean canHaveNonReboundConstructorInvoke,
+      boolean canUseLambdas) {
     ClassSubject mainClassSubject = inspector.clazz(Main.class);
     assertThat(mainClassSubject, isPresent());
 
@@ -96,21 +103,26 @@
     assertThat(lambdaClassSubject, notIf(isPresent(), canUseLambdas));
 
     MethodSubject lambdaInitializerSubject = lambdaClassSubject.uniqueInstanceInitializer();
-    assertThat(lambdaInitializerSubject, notIf(isPresent(), canUseLambdas));
+    assertThat(
+        lambdaInitializerSubject,
+        notIf(isPresent(), canHaveNonReboundConstructorInvoke || canUseLambdas));
 
     MethodSubject lambdaMainMethodSubject =
         lambdaClassSubject.uniqueMethodThatMatches(FoundMethodSubject::isVirtual);
     assertThat(lambdaMainMethodSubject, notIf(isPresent(), canUseLambdas));
 
     if (canUseLambdas) {
-      profileInspector.assertContainsMethodRule(mainMethodSubject).assertContainsNoOtherRules();
+      profileInspector.assertContainsMethodRule(mainMethodSubject);
     } else {
       profileInspector
           .assertContainsClassRules(lambdaClassSubject)
-          .assertContainsMethodRules(
-              mainMethodSubject, lambdaInitializerSubject, lambdaMainMethodSubject)
-          .assertContainsNoOtherRules();
+          .assertContainsMethodRules(mainMethodSubject, lambdaMainMethodSubject)
+          .applyIf(
+              !canHaveNonReboundConstructorInvoke,
+              i -> i.assertContainsMethodRule(lambdaInitializerSubject));
     }
+
+    profileInspector.assertContainsNoOtherRules();
   }
 
   static class Main {
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/NestBasedAccessBridgesProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/NestBasedAccessBridgesProfileRewritingTest.java
index 7d9e9ab..577518d 100644
--- a/src/test/java/com/android/tools/r8/profile/art/completeness/NestBasedAccessBridgesProfileRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/NestBasedAccessBridgesProfileRewritingTest.java
@@ -104,17 +104,22 @@
 
   private void inspectD8(ArtProfileInspector profileInspector, CodeInspector inspector)
       throws Exception {
-    inspect(profileInspector, inspector, parameters.canUseNestBasedAccessesWhenDesugaring());
+    inspect(profileInspector, inspector, false, parameters.canUseNestBasedAccessesWhenDesugaring());
   }
 
   private void inspectR8(ArtProfileInspector profileInspector, CodeInspector inspector)
       throws Exception {
-    inspect(profileInspector, inspector, parameters.canUseNestBasedAccesses());
+    inspect(
+        profileInspector,
+        inspector,
+        parameters.canHaveNonReboundConstructorInvoke(),
+        parameters.canUseNestBasedAccesses());
   }
 
   private void inspect(
       ArtProfileInspector profileInspector,
       CodeInspector inspector,
+      boolean canHaveNonReboundConstructorInvoke,
       boolean canUseNestBasedAccesses)
       throws Exception {
     ClassSubject nestMemberClassSubject = inspector.clazz(NestMember.class);
@@ -128,7 +133,7 @@
         syntheticConstructorArgumentClassSubject, notIf(isPresent(), canUseNestBasedAccesses));
 
     MethodSubject instanceInitializer = nestMemberClassSubject.init();
-    assertThat(instanceInitializer, isPresent());
+    assertThat(instanceInitializer, notIf(isPresent(), canHaveNonReboundConstructorInvoke));
 
     MethodSubject instanceInitializerWithSyntheticArgumentSubject =
         syntheticConstructorArgumentClassSubject.isPresent()
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java
index 5f1886d..6d03704 100644
--- a/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java
@@ -149,6 +149,7 @@
         profileInspector,
         inspector,
         SyntheticItemsTestUtils.syntheticRecordTagClass(),
+        false,
         parameters.canUseNestBasedAccessesWhenDesugaring(),
         parameters.canUseRecordsWhenDesugaring());
   }
@@ -158,6 +159,7 @@
         profileInspector,
         inspector,
         RECORD_REFERENCE,
+        parameters.canHaveNonReboundConstructorInvoke(),
         parameters.canUseNestBasedAccesses(),
         parameters.canUseRecords());
   }
@@ -166,6 +168,7 @@
       ArtProfileInspector profileInspector,
       CodeInspector inspector,
       ClassReference recordClassReference,
+      boolean canHaveNonReboundConstructorInvoke,
       boolean canUseNestBasedAccesses,
       boolean canUseRecords) {
     ClassSubject mainClassSubject = inspector.clazz(MAIN_REFERENCE);
@@ -177,11 +180,14 @@
     ClassSubject recordTagClassSubject = inspector.clazz(recordClassReference);
     assertThat(recordTagClassSubject, notIf(isPresent(), canUseRecords));
     if (!canUseRecords) {
-      assertEquals(1, recordTagClassSubject.allMethods().size());
+      assertEquals(
+          canHaveNonReboundConstructorInvoke ? 0 : 1, recordTagClassSubject.allMethods().size());
     }
 
     MethodSubject recordTagInstanceInitializerSubject = recordTagClassSubject.init();
-    assertThat(recordTagInstanceInitializerSubject, notIf(isPresent(), canUseRecords));
+    assertThat(
+        recordTagInstanceInitializerSubject,
+        notIf(isPresent(), canHaveNonReboundConstructorInvoke || canUseRecords));
 
     ClassSubject personRecordClassSubject = inspector.clazz(PERSON_REFERENCE);
     assertThat(personRecordClassSubject, isPresent());
@@ -290,11 +296,13 @@
                         hashCodeHelperClassSubject,
                         toStringHelperClassSubject)
                     .assertContainsMethodRules(
-                        recordTagInstanceInitializerSubject,
                         equalsHelperMethodSubject,
                         getFieldsAsObjectsMethodSubject,
                         hashCodeHelperMethodSubject,
-                        toStringHelperMethodSubject))
+                        toStringHelperMethodSubject)
+                    .applyIf(
+                        !canHaveNonReboundConstructorInvoke,
+                        j -> j.assertContainsMethodRule(recordTagInstanceInitializerSubject)))
         .assertContainsNoOtherRules();
   }
 }
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/SyntheticLambdaClassProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/SyntheticLambdaClassProfileRewritingTest.java
index 385f94b..ef6b3a3 100644
--- a/src/test/java/com/android/tools/r8/profile/art/completeness/SyntheticLambdaClassProfileRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/SyntheticLambdaClassProfileRewritingTest.java
@@ -68,6 +68,7 @@
     public void inspect(
         ArtProfileInspector profileInspector,
         CodeInspector inspector,
+        boolean canHaveNonReboundConstructorInvoke,
         boolean canUseLambdas,
         boolean canAccessModifyLambdaImplementationMethods) {
       ClassSubject mainClassSubject = inspector.clazz(Main.class);
@@ -106,7 +107,9 @@
       assertThat(lambdaClassSubject, notIf(isPresent(), canUseLambdas));
 
       MethodSubject lambdaInitializerSubject = lambdaClassSubject.uniqueInstanceInitializer();
-      assertThat(lambdaInitializerSubject, notIf(isPresent(), canUseLambdas));
+      assertThat(
+          lambdaInitializerSubject,
+          notIf(isPresent(), canHaveNonReboundConstructorInvoke || canUseLambdas));
 
       MethodSubject lambdaMainMethodSubject =
           lambdaClassSubject.uniqueMethodThatMatches(FoundMethodSubject::isVirtual);
@@ -119,7 +122,9 @@
 
       MethodSubject otherLambdaInitializerSubject =
           otherLambdaClassSubject.uniqueInstanceInitializer();
-      assertThat(otherLambdaInitializerSubject, notIf(isPresent(), canUseLambdas));
+      assertThat(
+          otherLambdaInitializerSubject,
+          notIf(isPresent(), canHaveNonReboundConstructorInvoke || canUseLambdas));
 
       MethodSubject otherLambdaMainMethodSubject =
           otherLambdaClassSubject.uniqueMethodThatMatches(FoundMethodSubject::isVirtual);
@@ -149,8 +154,12 @@
             // interface method implementation does not need to be included in the profile.
             profileInspector
                 .assertContainsClassRules(lambdaClassSubject, otherLambdaClassSubject)
-                .assertContainsMethodRules(
-                    mainMethodSubject, lambdaInitializerSubject, otherLambdaInitializerSubject)
+                .assertContainsMethodRules(mainMethodSubject)
+                .applyIf(
+                    !canHaveNonReboundConstructorInvoke,
+                    i ->
+                        i.assertContainsMethodRules(
+                            lambdaInitializerSubject, otherLambdaInitializerSubject))
                 .assertContainsNoOtherRules();
             break;
           case IMPLEMENTATION_METHOD:
@@ -221,11 +230,16 @@
   }
 
   private void inspectD8(ArtProfileInspector profileInspector, CodeInspector inspector) {
-    artProfileInputOutput.inspect(profileInspector, inspector, false, true);
+    artProfileInputOutput.inspect(profileInspector, inspector, false, false, true);
   }
 
   private void inspectR8(ArtProfileInspector profileInspector, CodeInspector inspector) {
-    artProfileInputOutput.inspect(profileInspector, inspector, parameters.isCfRuntime(), false);
+    artProfileInputOutput.inspect(
+        profileInspector,
+        inspector,
+        parameters.canHaveNonReboundConstructorInvoke(),
+        parameters.isCfRuntime(),
+        false);
   }
 
   static class Main {
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetracePartitionStackTraceTest.java b/src/test/java/com/android/tools/r8/retrace/api/RetracePartitionStackTraceTest.java
new file mode 100644
index 0000000..671d075
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetracePartitionStackTraceTest.java
@@ -0,0 +1,234 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.retrace.api;
+
+import static com.android.tools.r8.naming.retrace.StackTrace.isSame;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestDiagnosticMessagesImpl;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.naming.retrace.StackTrace;
+import com.android.tools.r8.naming.retrace.StackTrace.StackTraceLine;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.retrace.MappingPartitionMetadata;
+import com.android.tools.r8.retrace.PartitionMappingSupplier;
+import com.android.tools.r8.retrace.ProguardMapPartitioner;
+import com.android.tools.r8.retrace.ProguardMapProducer;
+import com.android.tools.r8.retrace.Retrace;
+import com.android.tools.r8.retrace.RetraceStackTraceContext;
+import com.android.tools.r8.retrace.RetraceStackTraceElementProxy;
+import com.android.tools.r8.retrace.RetracedMethodReference.KnownRetracedMethodReference;
+import com.android.tools.r8.retrace.StackTraceElementProxy;
+import com.android.tools.r8.retrace.StackTraceLineParser;
+import com.android.tools.r8.utils.BooleanBox;
+import com.android.tools.r8.utils.StringUtils;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class RetracePartitionStackTraceTest extends TestBase {
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  public RetracePartitionStackTraceTest(TestParameters parameters) {
+    parameters.assertNoneRuntime();
+  }
+
+  private final String MAPPING =
+      StringUtils.unixLines(
+          "com.Foo -> a:",
+          "  1:1:void m1():42:42 -> a",
+          "com.Bar -> b:",
+          "  2:2:void m2():43:43 -> b",
+          "com.Baz -> c:",
+          "  3:3:void m3():44:44 -> c");
+
+  @Test
+  public void testRetrace() throws Exception {
+    TestDiagnosticMessagesImpl diagnosticMessages = new TestDiagnosticMessagesImpl();
+    Map<String, byte[]> partitions = new HashMap<>();
+    MappingPartitionMetadata metadata =
+        ProguardMapPartitioner.builder(diagnosticMessages)
+            .setProguardMapProducer(ProguardMapProducer.fromString(MAPPING))
+            .setPartitionConsumer(
+                partition -> partitions.put(partition.getKey(), partition.getPayload()))
+            .build()
+            .run();
+
+    Set<String> requestedKeys = new HashSet<>();
+    BooleanBox setPartitionCallBack = new BooleanBox();
+    PartitionMappingSupplier mappingSupplier =
+        PartitionMappingSupplier.builder()
+            .setMetadata(metadata.getBytes())
+            .setRegisterMappingPartitionCallback(requestedKeys::add)
+            .setPrepareMappingPartitionsCallback(setPartitionCallBack::set)
+            .setMappingPartitionFromKeySupplier(partitions::get)
+            .build();
+
+    Retrace<StackTraceLine, StackTraceLineProxy> retrace =
+        Retrace.<StackTraceLine, StackTraceLineProxy>builder()
+            .setMappingSupplier(mappingSupplier)
+            .setStackTraceLineParser(StackTraceLineProxy::new)
+            .setDiagnosticsHandler(diagnosticMessages)
+            .build();
+
+    List<StackTraceLine> minifiedStackTrace = new ArrayList<>();
+    minifiedStackTrace.add(StackTraceLine.parse("at a.a(SourceFile:1)"));
+    minifiedStackTrace.add(StackTraceLine.parse("at b.b(SourceFile:2)"));
+    minifiedStackTrace.add(StackTraceLine.parse("at c.c(SourceFile:3)"));
+    StackTrace.Builder retraceStackTraceBuilder = StackTrace.builder();
+    retrace
+        .retraceStackTrace(minifiedStackTrace, RetraceStackTraceContext.empty())
+        .forEach(
+            stackTraceLines -> {
+              assertEquals(1, stackTraceLines.size());
+              stackTraceLines.get(0).forEach(retraceStackTraceBuilder::add);
+            });
+    assertEquals(partitions.keySet(), requestedKeys);
+    StackTrace expectedStackTrace =
+        StackTrace.builder()
+            .add(
+                StackTraceLine.builder()
+                    .setClassName("com.Foo")
+                    .setMethodName("m1")
+                    .setFileName("Foo.java")
+                    .setLineNumber(42)
+                    .build())
+            .add(
+                StackTraceLine.builder()
+                    .setClassName("com.Bar")
+                    .setMethodName("m2")
+                    .setFileName("Bar.java")
+                    .setLineNumber(43)
+                    .build())
+            .add(
+                StackTraceLine.builder()
+                    .setClassName("com.Baz")
+                    .setMethodName("m3")
+                    .setFileName("Baz.java")
+                    .setLineNumber(44)
+                    .build())
+            .build();
+    assertThat(expectedStackTrace, isSame(retraceStackTraceBuilder.build()));
+  }
+
+  public static class IdentityStackTraceLineParser
+      implements StackTraceLineParser<StackTraceLine, StackTraceLineProxy> {
+
+    @Override
+    public StackTraceLineProxy parse(StackTraceLine stackTraceLine) {
+      return new StackTraceLineProxy(stackTraceLine);
+    }
+  }
+
+  public static class StackTraceLineProxy
+      extends StackTraceElementProxy<StackTraceLine, StackTraceLineProxy> {
+
+    private final StackTraceLine stackTraceLine;
+
+    public StackTraceLineProxy(StackTraceLine stackTraceLine) {
+      this.stackTraceLine = stackTraceLine;
+    }
+
+    @Override
+    public boolean hasClassName() {
+      return true;
+    }
+
+    @Override
+    public boolean hasMethodName() {
+      return true;
+    }
+
+    @Override
+    public boolean hasSourceFile() {
+      return true;
+    }
+
+    @Override
+    public boolean hasLineNumber() {
+      return true;
+    }
+
+    @Override
+    public boolean hasFieldName() {
+      return false;
+    }
+
+    @Override
+    public boolean hasFieldOrReturnType() {
+      return false;
+    }
+
+    @Override
+    public boolean hasMethodArguments() {
+      return false;
+    }
+
+    @Override
+    public ClassReference getClassReference() {
+      return Reference.classFromTypeName(stackTraceLine.className);
+    }
+
+    @Override
+    public String getMethodName() {
+      return stackTraceLine.methodName;
+    }
+
+    @Override
+    public String getSourceFile() {
+      return stackTraceLine.fileName;
+    }
+
+    @Override
+    public int getLineNumber() {
+      return stackTraceLine.lineNumber;
+    }
+
+    @Override
+    public String getFieldName() {
+      return null;
+    }
+
+    @Override
+    public String getFieldOrReturnType() {
+      return null;
+    }
+
+    @Override
+    public String getMethodArguments() {
+      return null;
+    }
+
+    @Override
+    public StackTraceLine toRetracedItem(
+        RetraceStackTraceElementProxy<StackTraceLine, StackTraceLineProxy> retracedProxy,
+        boolean verbose) {
+      KnownRetracedMethodReference knownRetracedMethodReference =
+          retracedProxy.getRetracedMethod().asKnown();
+      return new StackTraceLine(
+          stackTraceLine.toString(),
+          knownRetracedMethodReference.getMethodReference().getHolderClass().getTypeName(),
+          knownRetracedMethodReference.getMethodName(),
+          retracedProxy.getSourceFile(),
+          knownRetracedMethodReference.getOriginalPositionOrDefault(0));
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/KeepClassMembersFieldTest.java b/src/test/java/com/android/tools/r8/shaking/KeepClassMembersFieldTest.java
index babd7a7..31d557e 100644
--- a/src/test/java/com/android/tools/r8/shaking/KeepClassMembersFieldTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/KeepClassMembersFieldTest.java
@@ -59,10 +59,7 @@
     Bar value = new Bar();
 
     public static void main(String[] args) {
-      Foo foo = new Foo();
-      if (System.currentTimeMillis() < 0) {
-        System.out.println(foo);
-      }
+      new Foo();
     }
   }
 }
diff --git a/src/test/java/com/android/tools/r8/shaking/RetainIndirectlyReferencedConstructorShakingOnDexTest.java b/src/test/java/com/android/tools/r8/shaking/RetainIndirectlyReferencedConstructorShakingOnDexTest.java
index a19fd93..f44de8c 100644
--- a/src/test/java/com/android/tools/r8/shaking/RetainIndirectlyReferencedConstructorShakingOnDexTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/RetainIndirectlyReferencedConstructorShakingOnDexTest.java
@@ -30,7 +30,7 @@
 public class RetainIndirectlyReferencedConstructorShakingOnDexTest extends TestBase {
 
   @Parameter(0)
-  public boolean enableRetargetingConstructorBridgeCalls;
+  public boolean enableRetargetingOfConstructorBridgeCalls;
 
   @Parameter(1)
   public TestParameters parameters;
@@ -48,8 +48,10 @@
         .addKeepMainRule(Main.class)
         .addOptionsModification(
             options ->
-                options.testing.enableRetargetingConstructorBridgeCalls =
-                    enableRetargetingConstructorBridgeCalls)
+                options
+                    .getRedundantBridgeRemovalOptions()
+                    .setEnableRetargetingOfConstructorBridgeCalls(
+                        enableRetargetingOfConstructorBridgeCalls))
         .enableNoVerticalClassMergingAnnotations()
         .setMinApi(parameters)
         .compile()
@@ -80,7 +82,7 @@
         invokesMethod(
             MethodReferenceUtils.instanceConstructor(
                 parameters.canHaveNonReboundConstructorInvoke()
-                        && enableRetargetingConstructorBridgeCalls
+                        && enableRetargetingOfConstructorBridgeCalls
                     ? Reference.classFromDescriptor(aClassSubject.getFinalDescriptor())
                     : Reference.classFromDescriptor(bClassSubject.getFinalDescriptor()))));
   }
diff --git a/src/test/java/com/android/tools/r8/shaking/constructor/ForwardingConstructorUsedFromPlatformShakingOnDexTest.java b/src/test/java/com/android/tools/r8/shaking/constructor/ForwardingConstructorUsedFromPlatformShakingOnDexTest.java
index 534bbb9..7dacf64 100644
--- a/src/test/java/com/android/tools/r8/shaking/constructor/ForwardingConstructorUsedFromPlatformShakingOnDexTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/constructor/ForwardingConstructorUsedFromPlatformShakingOnDexTest.java
@@ -5,14 +5,21 @@
 package com.android.tools.r8.shaking.constructor;
 
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.onlyIf;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assume.assumeFalse;
 
 import com.android.tools.r8.NeverClassInline;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.apimodel.ApiModelingTestHelper;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -22,48 +29,168 @@
 @RunWith(Parameterized.class)
 public class ForwardingConstructorUsedFromPlatformShakingOnDexTest extends TestBase {
 
+  private static final String APPLICATION_INFO_DESCRIPTOR = "Landroid/content/pm/ApplicationInfo;";
+  private static final String FRAGMENT_DESCRIPTOR = "Landroid/app/Fragment;";
+  private static final String ZYGOTE_PRELOAD_DESCRIPTOR = "Landroid/app/ZygotePreload;";
+
+  private static final String EXPECTED_OUTPUT =
+      StringUtils.lines(
+          "Fragment.onCreate()", "MyFragment.onCreate()", "MyZygotePreload.doPreload()");
+
+  private static List<byte[]> transformedProgramClassFileData;
+  private static List<byte[]> transformedLibraryClassFileData;
+
   @Parameter(0)
+  public boolean enableModeling;
+
+  @Parameter(1)
   public TestParameters parameters;
 
-  @Parameters(name = "{0}")
-  public static TestParametersCollection data() {
-    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  @Parameters(name = "{1}, modeling: {0}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build());
+  }
+
+  @BeforeClass
+  public static void setup() throws Exception {
+    transformedProgramClassFileData = getTransformedProgramClasses();
+    transformedLibraryClassFileData = getTransformedLibraryClasses();
   }
 
   @Test
-  public void test() throws Exception {
+  public void testRuntime() throws Exception {
+    assumeFalse(enableModeling);
+    testForRuntime(parameters)
+        .addProgramClassFileData(transformedProgramClassFileData)
+        .addProgramClassFileData(transformedLibraryClassFileData)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  @Test
+  public void testR8() throws Exception {
     testForR8(parameters.getBackend())
-        .addProgramClasses(Main.class, MyFragment.class)
-        .addLibraryClasses(Fragment.class)
+        .addProgramClassFileData(transformedProgramClassFileData)
+        .addLibraryClassFileData(transformedLibraryClassFileData)
         .addLibraryFiles(parameters.getDefaultRuntimeLibrary())
         .addKeepMainRule(Main.class)
+        .applyIf(
+            !enableModeling,
+            testBuilder ->
+                testBuilder.addOptionsModification(
+                    options ->
+                        options
+                            .getRedundantBridgeRemovalOptions()
+                            .clearNoConstructorShrinkingHierarchiesForTesting()))
+        // Since Fragment is first defined in API 11.
+        .apply(ApiModelingTestHelper::disableStubbingOfClasses)
         .enableNeverClassInliningAnnotations()
         .setMinApi(parameters)
         .compile()
         .inspect(this::inspect)
-        .addBootClasspathClasses(Fragment.class)
+        .addRunClasspathClassFileData(transformedLibraryClassFileData)
         .run(parameters.getRuntime(), Main.class)
-        .assertSuccessWithOutputLines("Instantiating");
+        .applyIf(
+            enableModeling || !parameters.canHaveNonReboundConstructorInvoke(),
+            runResult -> runResult.assertSuccessWithOutput(EXPECTED_OUTPUT),
+            runResult -> runResult.assertFailureWithErrorThatThrows(NoSuchMethodException.class));
+  }
+
+  private static List<byte[]> getTransformedProgramClasses() throws Exception {
+    return ImmutableList.of(
+        transformer(Main.class)
+            .replaceClassDescriptorInMethodInstructions(
+                descriptor(Fragment.class), FRAGMENT_DESCRIPTOR)
+            .replaceClassDescriptorInMethodInstructions(
+                descriptor(ZygotePreload.class), ZYGOTE_PRELOAD_DESCRIPTOR)
+            .transform(),
+        transformer(MyFragment.class)
+            .replaceClassDescriptorInMethodInstructions(
+                descriptor(Fragment.class), FRAGMENT_DESCRIPTOR)
+            .setSuper(FRAGMENT_DESCRIPTOR)
+            .transform(),
+        transformer(MyZygotePreload.class)
+            .replaceClassDescriptorInMembers(
+                descriptor(ApplicationInfo.class), APPLICATION_INFO_DESCRIPTOR)
+            .setImplementsClassDescriptors(ZYGOTE_PRELOAD_DESCRIPTOR)
+            .transform());
+  }
+
+  private static List<byte[]> getTransformedLibraryClasses() throws Exception {
+    return ImmutableList.of(
+        transformer(ApplicationInfo.class)
+            .setClassDescriptor(APPLICATION_INFO_DESCRIPTOR)
+            .transform(),
+        transformer(Fragment.class).setClassDescriptor(FRAGMENT_DESCRIPTOR).transform(),
+        transformer(Platform.class)
+            .replaceClassDescriptorInMembers(descriptor(Fragment.class), FRAGMENT_DESCRIPTOR)
+            .replaceClassDescriptorInMethodInstructions(
+                descriptor(Fragment.class), FRAGMENT_DESCRIPTOR)
+            .replaceClassDescriptorInMembers(
+                descriptor(ZygotePreload.class), ZYGOTE_PRELOAD_DESCRIPTOR)
+            .replaceClassDescriptorInMethodInstructions(
+                descriptor(ApplicationInfo.class), APPLICATION_INFO_DESCRIPTOR)
+            .replaceClassDescriptorInMethodInstructions(
+                descriptor(ZygotePreload.class), ZYGOTE_PRELOAD_DESCRIPTOR)
+            .transform(),
+        transformer(ZygotePreload.class)
+            .replaceClassDescriptorInMembers(
+                descriptor(ApplicationInfo.class), APPLICATION_INFO_DESCRIPTOR)
+            .setClassDescriptor(ZYGOTE_PRELOAD_DESCRIPTOR)
+            .transform());
   }
 
   private void inspect(CodeInspector inspector) {
     ClassSubject myFragmentClassSubject = inspector.clazz(MyFragment.class);
     assertThat(myFragmentClassSubject, isPresent());
-    assertThat(myFragmentClassSubject.init(), isPresent());
+    assertThat(
+        myFragmentClassSubject.init(),
+        onlyIf(enableModeling || !parameters.canHaveNonReboundConstructorInvoke(), isPresent()));
+
+    ClassSubject myZygotePreloadClassSubject = inspector.clazz(MyZygotePreload.class);
+    assertThat(myZygotePreloadClassSubject, isPresent());
+    assertThat(
+        myZygotePreloadClassSubject.init(),
+        onlyIf(enableModeling || !parameters.canHaveNonReboundConstructorInvoke(), isPresent()));
   }
 
+  // Library classes.
+
+  public abstract static class ApplicationInfo {}
+
   public abstract static class Fragment {
 
-    public Fragment newInstance() throws Exception {
-      System.out.println("Instantiating");
-      return getClass().getDeclaredConstructor().newInstance();
+    public void onCreate() {
+      System.out.println("Fragment.onCreate()");
     }
   }
 
+  public interface ZygotePreload {
+
+    void doPreload(ApplicationInfo applicationInfo);
+  }
+
+  public static class Platform {
+
+    public static void accept(Fragment fragment) throws Exception {
+      Fragment newFragment = fragment.getClass().getDeclaredConstructor().newInstance();
+      newFragment.onCreate();
+    }
+
+    public static void accept(ZygotePreload runnable) throws Exception {
+      ZygotePreload newZygotePreload = runnable.getClass().getDeclaredConstructor().newInstance();
+      newZygotePreload.doPreload(null);
+    }
+  }
+
+  // Program classes.
+
   public static class Main {
 
     public static void main(String[] args) throws Exception {
-      new MyFragment().newInstance();
+      Platform.accept(new MyFragment());
+      Platform.accept(new MyZygotePreload());
     }
   }
 
@@ -71,5 +198,21 @@
   public static class MyFragment extends Fragment {
 
     public MyFragment() {}
+
+    @Override
+    public void onCreate() {
+      super.onCreate();
+      System.out.println("MyFragment.onCreate()");
+    }
+  }
+
+  public static class MyZygotePreload implements ZygotePreload {
+
+    public MyZygotePreload() {}
+
+    @Override
+    public void doPreload(ApplicationInfo applicationInfo) {
+      System.out.println("MyZygotePreload.doPreload()");
+    }
   }
 }