Merge prepare and scan methods in cf to cf desugaring

This fixes a couple of inconsistencies/bugs in cf-to-cf desugaring:

* Scan was never called in R8
* Prepare was never called for synthetics in D8, but scan was
* Interface method desugaring would generate abstract static companion methods leading to compilation errors (MemberRebindingAmbiguousDispatchTest)

This also makes the LazyCfCode to CfCode conversion in D8 explicit. This was previously implicit inside desugaring.

Fixes: b/193004879
Fixes: b/259227990
Change-Id: I2d3dbace2d77e6177891260aa49e79330c7d315c
diff --git a/src/main/java/com/android/tools/r8/cf/CfPrinter.java b/src/main/java/com/android/tools/r8/cf/CfPrinter.java
index fac5344..67b2e25 100644
--- a/src/main/java/com/android/tools/r8/cf/CfPrinter.java
+++ b/src/main/java/com/android/tools/r8/cf/CfPrinter.java
@@ -407,7 +407,11 @@
 
   public void print(CfInvoke invoke) {
     indent();
-    builder.append(opcodeName(invoke.getOpcode())).append(' ');
+    builder.append(opcodeName(invoke.getOpcode()));
+    if (!invoke.isInvokeInterface() && !invoke.isInvokeVirtual() && invoke.isInterface()) {
+      builder.append("_itf");
+    }
+    builder.append(' ');
     appendMethod(invoke.getMethod());
   }
 
diff --git a/src/main/java/com/android/tools/r8/desugar/covariantreturntype/CovariantReturnTypeEnqueuerExtension.java b/src/main/java/com/android/tools/r8/desugar/covariantreturntype/CovariantReturnTypeEnqueuerExtension.java
index 26194ed..0970525 100644
--- a/src/main/java/com/android/tools/r8/desugar/covariantreturntype/CovariantReturnTypeEnqueuerExtension.java
+++ b/src/main/java/com/android/tools/r8/desugar/covariantreturntype/CovariantReturnTypeEnqueuerExtension.java
@@ -17,6 +17,7 @@
 import com.android.tools.r8.graph.analysis.FixpointEnqueuerAnalysis;
 import com.android.tools.r8.graph.analysis.NewlyLiveMethodEnqueuerAnalysis;
 import com.android.tools.r8.shaking.Enqueuer;
+import com.android.tools.r8.shaking.EnqueuerEvent;
 import com.android.tools.r8.shaking.EnqueuerWorklist;
 import com.android.tools.r8.shaking.KeepInfo;
 import com.android.tools.r8.shaking.KeepMethodInfo;
@@ -95,6 +96,16 @@
           enqueuer.getKeepInfo().registerCompilerSynthesizedMethod(bridge);
           enqueuer.applyMinimumKeepInfoWhenLiveOrTargeted(bridge, bridgeKeepInfo);
           enqueuer.getProfileCollectionAdditions().addMethodIfContextIsInProfile(bridge, target);
+
+          // When shrinking is disabled we need to explicitly trace the code.
+          if (!appView.options().isShrinking()) {
+            KeepMethodInfo.Joiner noShrinkingKeepInfo =
+                bridgeKeepInfo.isShrinkingAllowed()
+                    ? KeepMethodInfo.newEmptyJoiner().disallowShrinking()
+                    : bridgeKeepInfo;
+            enqueuer.enqueueMethodDueToNoShrinkingRule(
+                bridge, noShrinkingKeepInfo, EnqueuerEvent.unconditional());
+          }
         },
         executorService);
     errors.forEachValue(appView.reporter()::error);
diff --git a/src/main/java/com/android/tools/r8/graph/Code.java b/src/main/java/com/android/tools/r8/graph/Code.java
index d687be2..c893b40 100644
--- a/src/main/java/com/android/tools/r8/graph/Code.java
+++ b/src/main/java/com/android/tools/r8/graph/Code.java
@@ -87,6 +87,10 @@
     return false;
   }
 
+  public boolean isLazyCfCode() {
+    return false;
+  }
+
   public boolean isCfWritableCode() {
     return false;
   }
@@ -176,7 +180,7 @@
   }
 
   public LazyCfCode asLazyCfCode() {
-    throw new Unreachable(getClass().getCanonicalName() + ".asLazyCfCode()");
+    return null;
   }
 
   public DexCode asDexCode() {
diff --git a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
index 33f9f26..3618df3 100644
--- a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
@@ -124,6 +124,11 @@
   }
 
   @Override
+  public boolean isLazyCfCode() {
+    return true;
+  }
+
+  @Override
   public boolean isCfWritableCode() {
     return true;
   }
@@ -142,6 +147,15 @@
     return code;
   }
 
+  public void parseCodeConcurrently() {
+    ReparseContext context = this.context;
+    if (context != null) {
+      synchronized (context) {
+        asCfCode();
+      }
+    }
+  }
+
   @Override
   public CfWritableCode asCfWritableCode() {
     return asCfCode();
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java
index 8c517ef..b139e49 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.ir.conversion;
 
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.Code;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexProgramClass;
@@ -131,10 +132,15 @@
               .build();
     }
 
+    parseLazyCfCodeConcurrently(executorService);
+
     CfInstructionDesugaringEventConsumer instructionDesugaringEventConsumerForPrepareStep =
         CfInstructionDesugaringEventConsumer.createForD8(
             appView, profileCollectionAdditions, resultBuilder, methodProcessor);
-    converter.prepareDesugaring(instructionDesugaringEventConsumerForPrepareStep, executorService);
+    converter.prepareDesugaring(
+        instructionDesugaringEventConsumerForPrepareStep,
+        executorService,
+        appView.appInfo().classes());
     assert instructionDesugaringEventConsumerForPrepareStep.verifyNothingToFinalize();
 
     // When adding nest members to the wave we must do so deterministically.
@@ -187,6 +193,16 @@
         // Create a new processor context to ensure unique method processing contexts.
         methodProcessor.newWave();
 
+        CfInstructionDesugaringEventConsumer
+            instructionDesugaringEventConsumerForSyntheticPrepareStep =
+                CfInstructionDesugaringEventConsumer.createForD8(
+                    appView, profileCollectionAdditions, resultBuilder, methodProcessor);
+        converter.prepareDesugaring(
+            instructionDesugaringEventConsumerForSyntheticPrepareStep,
+            executorService,
+            needsProcessing);
+        assert instructionDesugaringEventConsumerForSyntheticPrepareStep.verifyNothingToFinalize();
+
         // Process the methods that require reprocessing. These are all simple bridge methods and
         // should therefore not lead to additional desugaring.
         ThreadUtils.processItems(
@@ -220,6 +236,30 @@
     }
   }
 
+  private void parseLazyCfCodeConcurrently(ExecutorService executorService)
+      throws ExecutionException {
+    ThreadUtils.processItems(
+        appView.appInfo().classes(),
+        clazz ->
+            clazz.forEachProgramMethod(
+                method -> {
+                  Code code = method.getDefinition().getCode();
+                  if (code != null && code.isLazyCfCode()) {
+                    code.asLazyCfCode().parseCodeConcurrently();
+                  }
+                  DexEncodedMethod definition = method.getDefinition();
+                  if (appView.options().isGeneratingClassFiles()
+                      && definition.hasClassFileVersion()) {
+                    definition.downgradeClassFileVersion(
+                        appView
+                            .options()
+                            .classFileVersionAfterDesugaring(definition.getClassFileVersion()));
+                  }
+                }),
+        appView.options().getThreadingModule(),
+        executorService);
+  }
+
   private void checkWaveDeterminism(Collection<DexProgramClass> wave) {
     appView
         .options()
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessorEventConsumer.java b/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessorEventConsumer.java
index ac0ca71..27be7e5 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessorEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessorEventConsumer.java
@@ -94,6 +94,12 @@
     }
 
     @Override
+    public void acceptUtilityThrowAbstractMethodErrorMethod(
+        ProgramMethod method, ProgramMethod context) {
+      // Intentionally empty.
+    }
+
+    @Override
     public void acceptUtilityThrowIllegalAccessErrorMethod(
         ProgramMethod method, ProgramMethod context) {
       // Intentionally empty.
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryD8L8IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryD8L8IRConverter.java
index 65f1d29..d947078 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryD8L8IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryD8L8IRConverter.java
@@ -35,9 +35,11 @@
 import com.android.tools.r8.profile.rewriting.ProfileCollectionAdditions;
 import com.android.tools.r8.threading.ThreadingModule;
 import com.android.tools.r8.utils.ExceptionUtils;
+import com.android.tools.r8.utils.ForEachable;
 import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.timing.Timing;
+import java.util.Collection;
 import java.util.List;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
@@ -135,10 +137,6 @@
       MethodProcessingContext methodProcessingContext,
       Timing timing) {
     DexEncodedMethod definition = method.getDefinition();
-    if (options.isGeneratingClassFiles() && definition.hasClassFileVersion()) {
-      definition.downgradeClassFileVersion(
-          appView.options().classFileVersionAfterDesugaring(definition.getClassFileVersion()));
-    }
     if (definition.getCode() == null) {
       return;
     }
@@ -317,22 +315,46 @@
   }
 
   void prepareDesugaring(
-      CfInstructionDesugaringEventConsumer desugaringEventConsumer, ExecutorService executorService)
+      CfInstructionDesugaringEventConsumer desugaringEventConsumer,
+      ExecutorService executorService,
+      Collection<DexProgramClass> classes)
+      throws ExecutionException {
+    prepareDesugaring(
+        desugaringEventConsumer,
+        executorService,
+        consumer ->
+            classes.forEach(
+                c ->
+                    c.forEachProgramMethodMatching(
+                        m -> m.hasCode() && m.getCode().isCfCode(), consumer)));
+  }
+
+  void prepareDesugaring(
+      CfInstructionDesugaringEventConsumer desugaringEventConsumer,
+      ExecutorService executorService,
+      List<ProgramMethod> methods)
+      throws ExecutionException {
+    prepareDesugaring(desugaringEventConsumer, executorService, methods::forEach);
+  }
+
+  void prepareDesugaring(
+      CfInstructionDesugaringEventConsumer desugaringEventConsumer,
+      ExecutorService executorService,
+      ForEachable<ProgramMethod> methods)
       throws ExecutionException {
     try (Timing t0 = timing.begin("Prepare desugaring")) {
       // Prepare desugaring by collecting all the synthetic methods required on program classes.
       ProgramAdditions programAdditions = new ProgramAdditions();
       ThreadingModule threadingModule = appView.options().getThreadingModule();
       ThreadUtils.processItems(
-          appView.appInfo().classes(),
-          clazz -> {
-            CfInstructionDesugaringCollection instructionDesugaringForClass =
-                instructionDesugaring.get(clazz);
-            clazz.forEachProgramMethodMatching(
-                method -> method.hasCode() && method.getCode().isCfCode(),
-                method ->
-                    instructionDesugaringForClass.prepare(
-                        method, desugaringEventConsumer, programAdditions));
+          methods,
+          method -> {
+            assert method.getDefinition().hasCode();
+            assert method.getDefinition().getCode().isCfCode();
+            CfInstructionDesugaringCollection instructionDesugaringForMethod =
+                instructionDesugaring.get(method);
+            instructionDesugaringForMethod.prepare(
+                method, desugaringEventConsumer, programAdditions);
           },
           threadingModule,
           executorService);
@@ -398,7 +420,6 @@
     try (Timing t0 = timing.begin("Desugar code")) {
       CfInstructionDesugaringCollection instructionDesugaringForMethod =
           instructionDesugaring.get(method);
-      instructionDesugaringForMethod.scan(method, desugaringEventConsumer);
       if (instructionDesugaringForMethod.needsDesugaring(method)) {
         instructionDesugaringForMethod.desugar(
             method, methodProcessingContext, desugaringEventConsumer);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaring.java
index c2c9731..9faee9b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaring.java
@@ -18,11 +18,6 @@
 
   default void acceptRelevantCompareToIds(IntConsumer consumer) {}
 
-  // TODO(193004879): Merge the scan and prepare methods.
-  default void scan(ProgramMethod method, CfInstructionDesugaringEventConsumer eventConsumer) {
-    // Default scan is to do nothing.
-  }
-
   /**
    * Prepare step which is called on all classes scheduled for desugaring before the actual
    * instruction level desugaring is preformed. This allows the desugaring to prepare and provide
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java
index 567fd16..ae758cf 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java
@@ -54,9 +54,6 @@
       CfInstructionDesugaringEventConsumer eventConsumer,
       ProgramAdditions programAdditions);
 
-  public abstract void scan(
-      ProgramMethod method, CfInstructionDesugaringEventConsumer eventConsumer);
-
   /** Desugars the instructions in the given method. */
   public abstract void desugar(
       ProgramMethod method,
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
index b8ef523..52ab44c 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
@@ -360,6 +360,12 @@
     }
 
     @Override
+    public void acceptUtilityThrowAbstractMethodErrorMethod(
+        ProgramMethod method, ProgramMethod context) {
+      acceptUtilityMethod(method, context);
+    }
+
+    @Override
     public void acceptUtilityThrowIllegalAccessErrorMethod(
         ProgramMethod method, ProgramMethod context) {
       acceptUtilityMethod(method, context);
@@ -649,6 +655,12 @@
     }
 
     @Override
+    public void acceptUtilityThrowAbstractMethodErrorMethod(
+        ProgramMethod method, ProgramMethod context) {
+      acceptUtilityMethod(method, context);
+    }
+
+    @Override
     public void acceptUtilityThrowIllegalAccessErrorMethod(
         ProgramMethod method, ProgramMethod context) {
       acceptUtilityMethod(method, context);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/EmptyCfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/EmptyCfInstructionDesugaringCollection.java
index b099cf9..f107f79 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/EmptyCfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/EmptyCfInstructionDesugaringCollection.java
@@ -40,11 +40,6 @@
   }
 
   @Override
-  public void scan(ProgramMethod method, CfInstructionDesugaringEventConsumer eventConsumer) {
-    // Intentionally empty.
-  }
-
-  @Override
   public void desugar(
       ProgramMethod method,
       MethodProcessingContext methodProcessingContext,
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
index 81173c6..aef9da4 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
@@ -135,8 +135,9 @@
     if (appView.options().enableTryWithResourcesDesugaring()) {
       desugarings.add(new TwrInstructionDesugaring(appView));
     }
+    TypeSwitchDesugaring typeSwitchDesugaring = null;
     if (appView.options().enableTypeSwitchDesugaring) {
-      desugarings.add(new TypeSwitchDesugaring(appView));
+      desugarings.add(typeSwitchDesugaring = new TypeSwitchDesugaring(appView));
     }
     RecordInstructionDesugaring recordRewriter = RecordInstructionDesugaring.create(appView);
     if (recordRewriter != null) {
@@ -156,7 +157,7 @@
                 autoCloseableRetargeter,
                 desugaredLibraryRetargeter),
             SetUtils.newImmutableSetExcludingNullItems(
-                lambdaDesugaring, stringConcatDesugaring, recordRewriter));
+                lambdaDesugaring, stringConcatDesugaring, recordRewriter, typeSwitchDesugaring));
     if (interfaceMethodRewriter != null) {
       desugarings.add(interfaceMethodRewriter);
     } else if (appView.options().canHaveArtArrayCloneFromInterfaceMethodBug()) {
@@ -270,13 +271,6 @@
   }
 
   @Override
-  public void scan(ProgramMethod method, CfInstructionDesugaringEventConsumer eventConsumer) {
-    ensureCfCode(method);
-    desugarings.forEach(d -> d.scan(method, eventConsumer));
-    yieldingDesugarings.forEach(d -> d.scan(method, eventConsumer));
-  }
-
-  @Override
   public void desugar(
       ProgramMethod method,
       MethodProcessingContext methodProcessingContext,
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicInstructionDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicInstructionDesugaring.java
index be6c7a3..9dde895 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicInstructionDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicInstructionDesugaring.java
@@ -20,6 +20,7 @@
 import com.android.tools.r8.ir.desugar.DesugarDescription;
 import com.android.tools.r8.ir.desugar.FreshLocalProvider;
 import com.android.tools.r8.ir.desugar.LocalStackAllocator;
+import com.android.tools.r8.ir.desugar.ProgramAdditions;
 import com.android.tools.r8.position.MethodPosition;
 import com.android.tools.r8.utils.Box;
 import java.util.Collection;
@@ -127,7 +128,10 @@
   }
 
   @Override
-  public void scan(ProgramMethod method, CfInstructionDesugaringEventConsumer eventConsumer) {
+  public void prepare(
+      ProgramMethod method,
+      CfInstructionDesugaringEventConsumer eventConsumer,
+      ProgramAdditions programAdditions) {
     for (CfInstruction instruction :
         method.getDefinition().getCode().asCfCode().getInstructions()) {
       compute(instruction, method).scan();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/icce/AlwaysThrowingInstructionDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/icce/AlwaysThrowingInstructionDesugaring.java
index cd23661..2607b7f 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/icce/AlwaysThrowingInstructionDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/icce/AlwaysThrowingInstructionDesugaring.java
@@ -283,6 +283,9 @@
       if (resolutionResult.getResolvedMethod().isStatic() != invoke.isInvokeStatic()) {
         return UtilityMethodsForCodeOptimizations
             ::synthesizeThrowIncompatibleClassChangeErrorMethod;
+      } else if (invoke.isInvokeSpecial()
+          && resolutionResult.getResolvedMethod().getAccessFlags().isAbstract()) {
+        return UtilityMethodsForCodeOptimizations::synthesizeThrowAbstractMethodErrorMethod;
       }
     } else if (resolutionResult.isFailedResolution()) {
       FailedResolutionResult failedResolutionResult = resolutionResult.asFailedResolution();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
index bf94859..f7b48eb 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
@@ -37,6 +37,7 @@
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaring;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.DesugarDescription;
+import com.android.tools.r8.ir.desugar.ProgramAdditions;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.DerivedMethod;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
 import com.android.tools.r8.ir.desugar.icce.AlwaysThrowingInstructionDesugaring;
@@ -235,21 +236,24 @@
    * class file version, as well as reporting missing type.
    */
   @Override
-  public void scan(ProgramMethod context, CfInstructionDesugaringEventConsumer eventConsumer) {
+  public void prepare(
+      ProgramMethod method,
+      CfInstructionDesugaringEventConsumer eventConsumer,
+      ProgramAdditions programAdditions) {
     if (desugaringMode == EMULATED_INTERFACE_ONLY) {
       return;
     }
-    if (isSyntheticMethodThatShouldNotBeDoubleProcessed(context)) {
-      leavingStaticInvokeToInterface(context);
+    if (isSyntheticMethodThatShouldNotBeDoubleProcessed(method)) {
+      leavingStaticInvokeToInterface(method);
       return;
     }
-    CfCode code = context.getDefinition().getCode().asCfCode();
+    CfCode code = method.getDefinition().getCode().asCfCode();
     for (CfInstruction instruction : code.getInstructions()) {
       if (instruction.isInvokeDynamic()
-          && !isAlreadyDesugared(instruction.asInvokeDynamic(), context)) {
-        reportInterfaceMethodHandleCallSite(instruction.asInvokeDynamic().getCallSite(), context);
+          && !isAlreadyDesugared(instruction.asInvokeDynamic(), method)) {
+        reportInterfaceMethodHandleCallSite(instruction.asInvokeDynamic().getCallSite(), method);
       }
-      compute(instruction, context).scan();
+      compute(instruction, method).scan();
     }
   }
 
@@ -778,6 +782,10 @@
                 })
             .build();
       } else {
+        DexClassAndMethod method = resolutionResult.getResolutionPair();
+        if (method.getAccessFlags().isAbstract()) {
+          return computeInvokeAsThrowRewrite(invoke, resolutionResult, context);
+        }
         return DesugarDescription.builder()
             .setDesugarRewrite(
                 (position,
@@ -789,7 +797,6 @@
                     methodProcessingContext,
                     desugaringCollection,
                     dexItemFactory) -> {
-                  DexClassAndMethod method = resolutionResult.getResolutionPair();
                   // TODO(b/199135051): Why do this amend routine. We have done resolution, so would
                   //  that not be the correct target!? I think this is just legacy from before
                   //  resolution was implemented in full.
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/lambda/LambdaInstructionDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/lambda/LambdaInstructionDesugaring.java
index dd386d5..6d0faf6 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/lambda/LambdaInstructionDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/lambda/LambdaInstructionDesugaring.java
@@ -30,6 +30,7 @@
 import com.android.tools.r8.ir.desugar.LambdaClass;
 import com.android.tools.r8.ir.desugar.LambdaDescriptor;
 import com.android.tools.r8.ir.desugar.LocalStackAllocator;
+import com.android.tools.r8.ir.desugar.ProgramAdditions;
 import com.android.tools.r8.utils.Box;
 import com.google.common.collect.ImmutableList;
 import java.util.ArrayDeque;
@@ -61,7 +62,10 @@
   }
 
   @Override
-  public void scan(ProgramMethod method, CfInstructionDesugaringEventConsumer eventConsumer) {
+  public void prepare(
+      ProgramMethod method,
+      CfInstructionDesugaringEventConsumer eventConsumer,
+      ProgramAdditions programAdditions) {
     CfCode code = method.getDefinition().getCode().asCfCode();
     for (CfInstruction instruction : code.getInstructions()) {
       if (instruction.isInvokeSpecial()) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordFullInstructionDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordFullInstructionDesugaring.java
index ee8f224..cd55a87 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordFullInstructionDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordFullInstructionDesugaring.java
@@ -19,6 +19,7 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.ProgramAdditions;
 
 public class RecordFullInstructionDesugaring extends RecordInstructionDesugaring {
 
@@ -27,11 +28,14 @@
   }
 
   @Override
-  public void scan(
-      ProgramMethod programMethod, CfInstructionDesugaringEventConsumer eventConsumer) {
-    CfCode cfCode = programMethod.getDefinition().getCode().asCfCode();
+  public void prepare(
+      ProgramMethod method,
+      CfInstructionDesugaringEventConsumer eventConsumer,
+      ProgramAdditions programAdditions) {
+    super.prepare(method, eventConsumer, programAdditions);
+    CfCode cfCode = method.getDefinition().getCode().asCfCode();
     for (CfInstruction instruction : cfCode.getInstructions()) {
-      scanInstruction(instruction, eventConsumer, programMethod);
+      scanInstruction(instruction, eventConsumer, method);
     }
   }
 
@@ -43,27 +47,21 @@
       CfInstruction instruction,
       CfInstructionDesugaringEventConsumer eventConsumer,
       ProgramMethod context) {
-    assert !instruction.isInitClass();
     if (instruction.isInvoke()) {
       CfInvoke cfInvoke = instruction.asInvoke();
       if (refersToRecord(cfInvoke.getMethod(), factory)) {
         ensureRecordClass(eventConsumer, context, appView);
       }
-      return;
-    }
-    if (instruction.isFieldInstruction()) {
+    } else if (instruction.isFieldInstruction()) {
       CfFieldInstruction fieldInstruction = instruction.asFieldInstruction();
       if (refersToRecord(fieldInstruction.getField(), factory)) {
         ensureRecordClass(eventConsumer, context, appView);
       }
-      return;
-    }
-    if (instruction.isTypeInstruction()) {
+    } else if (instruction.isTypeInstruction()) {
       CfTypeInstruction typeInstruction = instruction.asTypeInstruction();
       if (refersToRecord(typeInstruction.getType(), factory)) {
         ensureRecordClass(eventConsumer, context, appView);
       }
-      return;
     }
     // TODO(b/179146128): Analyse MethodHandle and MethodType.
   }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/varhandle/VarHandleDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/varhandle/VarHandleDesugaring.java
index 766dbf4..52499aa 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/varhandle/VarHandleDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/varhandle/VarHandleDesugaring.java
@@ -36,6 +36,7 @@
 import com.android.tools.r8.ir.desugar.DesugarDescription;
 import com.android.tools.r8.ir.desugar.FreshLocalProvider;
 import com.android.tools.r8.ir.desugar.LocalStackAllocator;
+import com.android.tools.r8.ir.desugar.ProgramAdditions;
 import com.android.tools.r8.utils.BitUtils;
 import com.google.common.collect.ImmutableList;
 import java.util.ArrayList;
@@ -75,17 +76,17 @@
   }
 
   @Override
-  @SuppressWarnings("ReferenceEquality")
-  public void scan(
-      ProgramMethod programMethod, CfInstructionDesugaringEventConsumer eventConsumer) {
-    if (programMethod.getHolderType() == factory.varHandleType) {
+  public void prepare(
+      ProgramMethod method,
+      CfInstructionDesugaringEventConsumer eventConsumer,
+      ProgramAdditions programAdditions) {
+    if (method.getHolderType().isIdenticalTo(factory.varHandleType)) {
       return;
     }
-    CfCode cfCode = programMethod.getDefinition().getCode().asCfCode();
+    CfCode cfCode = method.getDefinition().getCode().asCfCode();
     int synthesizedClasses = 0;
     for (CfInstruction instruction : cfCode.getInstructions()) {
-      synthesizedClasses =
-          scanInstruction(instruction, eventConsumer, programMethod, synthesizedClasses);
+      synthesizedClasses = scanInstruction(instruction, eventConsumer, method, synthesizedClasses);
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/UtilityMethodsForCodeOptimizations.java b/src/main/java/com/android/tools/r8/ir/optimize/UtilityMethodsForCodeOptimizations.java
index 0e4d1bc..26818b6 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/UtilityMethodsForCodeOptimizations.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/UtilityMethodsForCodeOptimizations.java
@@ -126,6 +126,39 @@
         .CfUtilityMethodsForCodeOptimizationsTemplates_isNonNull(dexItemFactory, method);
   }
 
+  public static UtilityMethodForCodeOptimizations synthesizeThrowAbstractMethodErrorMethod(
+      AppView<?> appView,
+      UtilityMethodsForCodeOptimizationsEventConsumer eventConsumer,
+      MethodProcessingContext methodProcessingContext) {
+    DexItemFactory dexItemFactory = appView.dexItemFactory();
+    DexProto proto = dexItemFactory.createProto(dexItemFactory.abstractMethodErrorType);
+    SyntheticItems syntheticItems = appView.getSyntheticItems();
+    ProgramMethod syntheticMethod =
+        syntheticItems.createMethod(
+            kinds -> kinds.THROW_AME,
+            methodProcessingContext.createUniqueContext(),
+            appView,
+            builder ->
+                builder
+                    .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
+                    .setClassFileVersion(CfVersion.V1_8)
+                    .setApiLevelForDefinition(appView.computedMinApiLevel())
+                    .setApiLevelForCode(appView.computedMinApiLevel())
+                    .setCode(
+                        method -> getThrowAbstractMethodErrorCodeTemplate(method, dexItemFactory))
+                    .setProto(proto));
+    eventConsumer.acceptUtilityThrowAbstractMethodErrorMethod(
+        syntheticMethod, methodProcessingContext.getMethodContext());
+    return new UtilityMethodForCodeOptimizations(syntheticMethod);
+  }
+
+  private static CfCode getThrowAbstractMethodErrorCodeTemplate(
+      DexMethod method, DexItemFactory dexItemFactory) {
+    return CfUtilityMethodsForCodeOptimizations
+        .CfUtilityMethodsForCodeOptimizationsTemplates_throwAbstractMethodError(
+            dexItemFactory, method);
+  }
+
   public static UtilityMethodForCodeOptimizations synthesizeThrowIllegalAccessErrorMethod(
       AppView<?> appView,
       UtilityMethodsForCodeOptimizationsEventConsumer eventConsumer,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/UtilityMethodsForCodeOptimizationsEventConsumer.java b/src/main/java/com/android/tools/r8/ir/optimize/UtilityMethodsForCodeOptimizationsEventConsumer.java
index 90bad5f..40d5e11 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/UtilityMethodsForCodeOptimizationsEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/UtilityMethodsForCodeOptimizationsEventConsumer.java
@@ -13,6 +13,8 @@
   void acceptUtilityThrowClassCastExceptionIfNotNullMethod(
       ProgramMethod method, ProgramMethod context);
 
+  void acceptUtilityThrowAbstractMethodErrorMethod(ProgramMethod method, ProgramMethod context);
+
   void acceptUtilityThrowIllegalAccessErrorMethod(ProgramMethod method, ProgramMethod context);
 
   void acceptUtilityThrowIncompatibleClassChangeErrorMethod(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/templates/CfUtilityMethodsForCodeOptimizations.java b/src/main/java/com/android/tools/r8/ir/optimize/templates/CfUtilityMethodsForCodeOptimizations.java
index cc004df..831f03a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/templates/CfUtilityMethodsForCodeOptimizations.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/templates/CfUtilityMethodsForCodeOptimizations.java
@@ -34,6 +34,7 @@
 public final class CfUtilityMethodsForCodeOptimizations {
 
   public static void registerSynthesizedCodeReferences(DexItemFactory factory) {
+    factory.createSynthesizedType("Ljava/lang/AbstractMethodError;");
     factory.createSynthesizedType("Ljava/lang/ClassCastException;");
     factory.createSynthesizedType("Ljava/lang/IllegalAccessError;");
     factory.createSynthesizedType("Ljava/lang/IncompatibleClassChangeError;");
@@ -75,6 +76,29 @@
         ImmutableList.of());
   }
 
+  public static CfCode CfUtilityMethodsForCodeOptimizationsTemplates_throwAbstractMethodError(
+      DexItemFactory factory, DexMethod method) {
+    CfLabel label0 = new CfLabel();
+    return new CfCode(
+        method.holder,
+        2,
+        0,
+        ImmutableList.of(
+            label0,
+            new CfNew(factory.createType("Ljava/lang/AbstractMethodError;")),
+            new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+            new CfInvoke(
+                183,
+                factory.createMethod(
+                    factory.createType("Ljava/lang/AbstractMethodError;"),
+                    factory.createProto(factory.voidType),
+                    factory.createString("<init>")),
+                false),
+            new CfThrow()),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
+
   public static CfCode
       CfUtilityMethodsForCodeOptimizationsTemplates_throwClassCastExceptionIfNotNull(
           DexItemFactory factory, DexMethod method) {
diff --git a/src/main/java/com/android/tools/r8/profile/rewriting/ProfileRewritingCfInstructionDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/profile/rewriting/ProfileRewritingCfInstructionDesugaringEventConsumer.java
index 8007796..d549537 100644
--- a/src/main/java/com/android/tools/r8/profile/rewriting/ProfileRewritingCfInstructionDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/profile/rewriting/ProfileRewritingCfInstructionDesugaringEventConsumer.java
@@ -425,6 +425,13 @@
   }
 
   @Override
+  public void acceptUtilityThrowAbstractMethodErrorMethod(
+      ProgramMethod method, ProgramMethod context) {
+    additionsCollection.addMethodAndHolderIfContextIsInProfile(method, context);
+    parent.acceptUtilityThrowAbstractMethodErrorMethod(method, context);
+  }
+
+  @Override
   public void acceptUtilityThrowIllegalAccessErrorMethod(
       ProgramMethod method, ProgramMethod context) {
     additionsCollection.addMethodAndHolderIfContextIsInProfile(method, context);
diff --git a/src/main/java/com/android/tools/r8/profile/rewriting/ProfileRewritingMethodProcessorEventConsumer.java b/src/main/java/com/android/tools/r8/profile/rewriting/ProfileRewritingMethodProcessorEventConsumer.java
index 5df02c0..21c6b0b 100644
--- a/src/main/java/com/android/tools/r8/profile/rewriting/ProfileRewritingMethodProcessorEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/profile/rewriting/ProfileRewritingMethodProcessorEventConsumer.java
@@ -102,6 +102,14 @@
   }
 
   @Override
+  public void acceptUtilityThrowAbstractMethodErrorMethod(
+      ProgramMethod method, ProgramMethod context) {
+    additionsCollection.applyIfContextIsInProfile(
+        context, additionsBuilder -> additionsBuilder.addRule(method).addRule(method.getHolder()));
+    parent.acceptUtilityThrowAbstractMethodErrorMethod(method, context);
+  }
+
+  @Override
   public void acceptUtilityThrowIllegalAccessErrorMethod(
       ProgramMethod method, ProgramMethod context) {
     additionsCollection.applyIfContextIsInProfile(
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 0b4dc3a..12eecf3 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -71,6 +71,7 @@
 import com.android.tools.r8.graph.GenericSignatureEnqueuerAnalysis;
 import com.android.tools.r8.graph.InnerClassAttribute;
 import com.android.tools.r8.graph.InvalidCode;
+import com.android.tools.r8.graph.LazyCfCode;
 import com.android.tools.r8.graph.LookupLambdaTarget;
 import com.android.tools.r8.graph.LookupMethodTarget;
 import com.android.tools.r8.graph.LookupResult;
@@ -136,6 +137,7 @@
 import com.android.tools.r8.shaking.EnqueuerEvent.LiveClassEnqueuerEvent;
 import com.android.tools.r8.shaking.EnqueuerEvent.UnconditionalKeepInfoEvent;
 import com.android.tools.r8.shaking.EnqueuerWorklist.EnqueuerAction;
+import com.android.tools.r8.shaking.EnqueuerWorklist.NonPushableEnqueuerWorklist;
 import com.android.tools.r8.shaking.EnqueuerWorklist.TraceInstanceFieldReadAction;
 import com.android.tools.r8.shaking.EnqueuerWorklist.TraceInstanceFieldWriteAction;
 import com.android.tools.r8.shaking.EnqueuerWorklist.TraceStaticFieldReadAction;
@@ -1073,7 +1075,7 @@
             field.getDefinition()));
   }
 
-  private void enqueueMethodDueToNoShrinkingRule(
+  public void enqueueMethodDueToNoShrinkingRule(
       ProgramMethod method,
       KeepMethodInfo.Joiner minimumKeepInfo,
       EnqueuerEvent preconditionEvent) {
@@ -4221,6 +4223,11 @@
   }
 
   private boolean addToPendingDesugaring(ProgramMethod method, Timing timing) {
+    // DEX code is not a supported input and can be ignored. Some legacy tests still pass DEX as
+    // input (smali tests).
+    if (!method.getDefinition().hasCode() || method.getDefinition().getCode().isDexCode()) {
+      return false;
+    }
     try (Timing t0 = timing.begin("Analyze needs desugaring")) {
       try (Timing t1 = timing.begin("Analyze interface method desugaring")) {
         if (options.isInterfaceMethodDesugaringEnabled()) {
@@ -4239,16 +4246,16 @@
             assert !InvalidCode.isInvalidCode(nonMovedMethod.getDefinition().getCode());
             pendingMethodMove.add(nonMovedMethod);
             return true;
+          }
         }
       }
+      if (worklist instanceof NonPushableEnqueuerWorklist) {
+        assert !method.getDefinition().getCode().isLazyCfCode();
+        assert !desugaring.needsDesugaring(method);
+        return false;
       }
-      try (Timing t2 = timing.begin("Analyze instruction desugaring")) {
-        if (desugaring.needsDesugaring(method)) {
-          pendingCodeDesugaring.add(method);
-          return true;
-        }
-      }
-      return false;
+      pendingCodeDesugaring.add(method);
+      return true;
     }
   }
 
@@ -4261,9 +4268,7 @@
     pendingCodeDesugaring.forEach(additions::addMethodWithDesugaredCodeForTracing);
     // Then amend the desugar set with the move methods that need desugaring.
     for (ProgramMethod method : pendingMethodMove) {
-      if (desugaring.needsDesugaring(method)) {
-        pendingCodeDesugaring.add(method);
-      }
+      pendingCodeDesugaring.add(method);
     }
 
     BiConsumer<LambdaClass, ProgramMethod> lambdaCallback = this::recordLambdaSynthesizingContext;
@@ -4304,11 +4309,28 @@
               }
             });
 
+    // Concurrently parse code.
+    Set<DexProgramClass> reparseContexts = ConcurrentHashMap.newKeySet();
+    ThreadUtils.processItems(
+        pendingCodeDesugaring,
+        method -> {
+          LazyCfCode lazyCfCode = method.getDefinition().getCode().asLazyCfCode();
+          if (lazyCfCode != null && reparseContexts.add(method.getHolder())) {
+            lazyCfCode.parseCodeConcurrently();
+          }
+        },
+        appView.options().getThreadingModule(),
+        executorService);
+
     // Prepare desugaring by collecting all the synthetic methods required on program classes.
     ProgramAdditions programAdditions = new ProgramAdditions();
     ThreadUtils.processItems(
         pendingCodeDesugaring,
-        method -> desugaring.prepare(method, eventConsumer, programAdditions),
+        method -> {
+          if (method.getDefinition().getCode().isCfCode()) {
+            desugaring.prepare(method, eventConsumer, programAdditions);
+          }
+        },
         appView.options().getThreadingModule(),
         executorService);
     programAdditions.apply(appView.options().getThreadingModule(), executorService);
@@ -4316,7 +4338,11 @@
     // Then do the actual desugaring.
     ThreadUtils.processItems(
         pendingCodeDesugaring,
-        method -> desugaring.desugar(method, additions.getMethodContext(method), eventConsumer),
+        method -> {
+          if (desugaring.needsDesugaring(method)) {
+            desugaring.desugar(method, additions.getMethodContext(method), eventConsumer);
+          }
+        },
         appView.options().getThreadingModule(),
         executorService);
 
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
index c2b122a..4ad6f93 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
@@ -86,6 +86,7 @@
   public final SyntheticKind THROW_CCE_IF_NOT_NULL =
       generator.forSingleMethodWithGlobalMerging("ThrowCCEIfNotNull");
   public final SyntheticKind NON_NULL = generator.forSingleMethodWithGlobalMerging("NonNull");
+  public final SyntheticKind THROW_AME = generator.forSingleMethodWithGlobalMerging("ThrowAME");
   public final SyntheticKind THROW_IAE = generator.forSingleMethodWithGlobalMerging("ThrowIAE");
   public final SyntheticKind THROW_ICCE = generator.forSingleMethodWithGlobalMerging("ThrowICCE");
   public final SyntheticKind THROW_NSME = generator.forSingleMethodWithGlobalMerging("ThrowNSME");
diff --git a/src/test/java/com/android/tools/r8/desugar/constantdynamic/ConstantDynamicHolderTest.java b/src/test/java/com/android/tools/r8/desugar/constantdynamic/ConstantDynamicHolderTest.java
index c601b56..77c7bfa 100644
--- a/src/test/java/com/android/tools/r8/desugar/constantdynamic/ConstantDynamicHolderTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/constantdynamic/ConstantDynamicHolderTest.java
@@ -96,21 +96,27 @@
   @Test
   public void testR8() throws Exception {
     parameters.assumeDexRuntime();
-
     testForR8(parameters.getBackend())
         .addProgramClassFileData(getTransformedMain())
         .setMinApi(parameters)
         .addKeepMainRule(Main.class)
         .allowDiagnosticWarningMessages()
+        .setDiagnosticsLevelModifier(
+            (level, diagnostic) ->
+                (diagnostic instanceof UnsupportedFeatureDiagnostic
+                        || diagnostic instanceof ConstantDynamicDesugarDiagnostic)
+                    ? DiagnosticsLevel.WARNING
+                    : level)
         .compileWithExpectedDiagnostics(
-            diagnostics -> {
-              if (parameters.isDexRuntime()) {
+            diagnostics ->
                 diagnostics.assertWarningsMatch(
+                    diagnosticType(UnsupportedConstDynamicDiagnostic.class),
                     allOf(
-                        diagnosticType(UnsupportedFeatureDiagnostic.class),
-                        diagnosticMessage(containsString("const-dynamic"))));
-              }
-            })
+                        diagnosticType(ConstantDynamicDesugarDiagnostic.class),
+                        diagnosticMessage(
+                            containsString(
+                                "Unsupported dynamic constant (runtime provided bootstrap"
+                                    + " method)")))))
         .run(parameters.getRuntime(), Main.class)
         .assertFailureWithErrorThatThrows(RuntimeException.class)
         .assertFailureWithErrorThatMatches(containsString("const-dynamic"));
diff --git a/src/test/java/com/android/tools/r8/desugar/constantdynamic/SharedBootstrapMethodConstantDynamicTest.java b/src/test/java/com/android/tools/r8/desugar/constantdynamic/SharedBootstrapMethodConstantDynamicTest.java
index f10d9e8..9fc702e 100644
--- a/src/test/java/com/android/tools/r8/desugar/constantdynamic/SharedBootstrapMethodConstantDynamicTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/constantdynamic/SharedBootstrapMethodConstantDynamicTest.java
@@ -164,6 +164,12 @@
         .addLibraryFiles(ToolHelper.getMostRecentAndroidJar())
         .setMinApi(parameters)
         .addKeepMainRule(MAIN_CLASS)
+        .setDiagnosticsLevelModifier(
+            (level, diagnostic) ->
+                (diagnostic instanceof ConstantDynamicDesugarDiagnostic
+                        || diagnostic instanceof UnsupportedFeatureDiagnostic)
+                    ? DiagnosticsLevel.WARNING
+                    : level)
         .allowDiagnosticMessages()
         .compileWithExpectedDiagnostics(
             diagnostics -> {
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/DefaultMethodInvokeSuperOnDefaultLibraryMethodTest.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/DefaultMethodInvokeSuperOnDefaultLibraryMethodTest.java
index 0cb64c8..377f0d6 100644
--- a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/DefaultMethodInvokeSuperOnDefaultLibraryMethodTest.java
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/DefaultMethodInvokeSuperOnDefaultLibraryMethodTest.java
@@ -11,6 +11,7 @@
 import static org.junit.Assume.assumeTrue;
 
 import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestDiagnosticMessages;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.ToolHelper;
@@ -31,15 +32,16 @@
 @RunWith(Parameterized.class)
 public class DefaultMethodInvokeSuperOnDefaultLibraryMethodTest extends TestBase {
 
-  @Parameter() public TestParameters parameters;
+  private static final String EXPECTED_OUTPUT = StringUtils.lines("1", "2");
+
+  @Parameter(0)
+  public TestParameters parameters;
 
   @Parameters(name = "{0}")
   public static TestParametersCollection data() {
-    return getTestParameters().withDexRuntimes().build();
+    return getTestParameters().withDexRuntimes().withPartialCompilation().build();
   }
 
-  private static final String EXPECTED_OUTPUT = StringUtils.lines("1", "2");
-
   private boolean runtimeHasConsumerInterface(TestParameters parameters) {
     // java,util.function.Consumer was introduced at API level 24.
     return parameters.asDexRuntime().getMinApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N);
@@ -47,22 +49,11 @@
 
   @Test
   public void testD8WithDefaultInterfaceMethodDesugaringWithAPIInLibrary() throws Exception {
-    testForD8(parameters.getBackend())
+    testForD8(parameters)
         .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.T))
         .addInnerClasses(getClass())
         .setMinApi(AndroidApiLevel.I_MR1)
-        .compileWithExpectedDiagnostics(
-            diagnostics ->
-                diagnostics
-                    .assertOnlyWarnings()
-                    .assertWarningsMatch(
-                        allOf(
-                            diagnosticType(StringDiagnostic.class),
-                            diagnosticMessage(
-                                containsString(
-                                    "Interface method desugaring has inserted NoSuchMethodError"
-                                        + " replacing a super call in")),
-                            diagnosticMessage(containsString("forEachPrint")))))
+        .compileWithExpectedDiagnostics(this::verifyOnlyNoSuchMethodErrorDiagnostic)
         .run(parameters.getRuntime(), TestClass.class)
         .applyIf(
             // If the platform does not have java.util.function.Consumer the lambda instantiation
@@ -75,7 +66,7 @@
 
   @Test
   public void testD8WithDefaultInterfaceMethodDesugaringWithoutAPIInLibrary() throws Exception {
-    testForD8(parameters.getBackend())
+    testForD8(parameters)
         .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.M))
         .addInnerClasses(getClass())
         .setMinApi(AndroidApiLevel.I_MR1)
@@ -99,33 +90,39 @@
   public void testD8WithDefaultInterfaceMethodSupport() throws Exception {
     assumeTrue(
         parameters.asDexRuntime().getMinApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N));
-    testForD8(parameters.getBackend())
+    testForD8(parameters)
         .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.T))
         .addInnerClasses(getClass())
         .setMinApi(AndroidApiLevel.N)
+        .compileWithExpectedDiagnostics(TestDiagnosticMessages::assertNoMessages)
         .run(parameters.getRuntime(), TestClass.class)
         .assertSuccessWithOutput(EXPECTED_OUTPUT);
   }
 
   @Test
   public void testR8WithDefaultInterfaceMethodDesugaringWithAPIInLibrary() throws Exception {
-    testForR8(parameters.getBackend())
+    parameters.assumeNoPartialCompilation("Requires native multi dex");
+    testForR8(parameters)
         .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.T))
         .addInnerClasses(getClass())
         .setMinApi(AndroidApiLevel.I_MR1)
         .addKeepMainRule(TestClass.class)
+        .allowDiagnosticWarningMessages()
+        .compileWithExpectedDiagnostics(this::verifyOnlyNoSuchMethodErrorDiagnostic)
         .run(parameters.getRuntime(), TestClass.class)
         .assertFailureWithErrorThatThrows(NoSuchMethodError.class);
   }
 
   @Test
   public void testR8WithDefaultInterfaceMethodDesugaringWithoutAPIInLibrary() throws Exception {
-    testForR8(parameters.getBackend())
+    parameters.assumeNoPartialCompilation("Requires native multi dex");
+    testForR8(parameters)
         .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.M))
         .addInnerClasses(getClass())
         .setMinApi(AndroidApiLevel.I_MR1)
         .addKeepMainRule(TestClass.class)
         .addDontWarn(Consumer.class)
+        .compile()
         .run(parameters.getRuntime(), TestClass.class)
         .applyIf(
             // If the platform does not have java.util.function.Consumer the lambda instantiation
@@ -140,15 +137,29 @@
   public void testR8WithDefaultInterfaceMethodSupport() throws Exception {
     assumeTrue(
         parameters.asDexRuntime().getMinApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N));
-    testForR8(parameters.getBackend())
+    testForR8(parameters)
         .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.T))
         .addInnerClasses(getClass())
         .setMinApi(AndroidApiLevel.N)
         .addKeepMainRule(TestClass.class)
+        .compile()
         .run(parameters.getRuntime(), TestClass.class)
         .assertSuccessWithOutput(EXPECTED_OUTPUT);
   }
 
+  private void verifyOnlyNoSuchMethodErrorDiagnostic(TestDiagnosticMessages diagnostics) {
+    diagnostics
+        .assertOnlyWarnings()
+        .assertWarningsMatch(
+            allOf(
+                diagnosticType(StringDiagnostic.class),
+                diagnosticMessage(
+                    containsString(
+                        "Interface method desugaring has inserted NoSuchMethodError"
+                            + " replacing a super call in")),
+                diagnosticMessage(containsString("forEachPrint"))));
+  }
+
   interface IntegerIterable extends Iterable<Integer> {
     default void forEachPrint() {
       Iterable.super.forEach(System.out::println);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/templates/CfUtilityMethodsForCodeOptimizationsTemplates.java b/src/test/java/com/android/tools/r8/ir/optimize/templates/CfUtilityMethodsForCodeOptimizationsTemplates.java
index c582d4d..de5de0e 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/templates/CfUtilityMethodsForCodeOptimizationsTemplates.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/templates/CfUtilityMethodsForCodeOptimizationsTemplates.java
@@ -22,6 +22,10 @@
     }
   }
 
+  public static AbstractMethodError throwAbstractMethodError() {
+    throw new AbstractMethodError();
+  }
+
   public static IllegalAccessError throwIllegalAccessError() {
     throw new IllegalAccessError();
   }
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingKeepAttributesKotlinStyleTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingKeepAttributesKotlinStyleTest.java
index 81f6750..d0faa2b 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingKeepAttributesKotlinStyleTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingKeepAttributesKotlinStyleTest.java
@@ -9,7 +9,6 @@
 import static com.android.tools.r8.shaking.ProguardKeepAttributes.INNER_CLASSES;
 import static com.android.tools.r8.shaking.ProguardKeepAttributes.SIGNATURE;
 import static junit.framework.TestCase.assertEquals;
-import static org.hamcrest.CoreMatchers.containsString;
 import static org.junit.Assume.assumeFalse;
 import static org.junit.Assume.assumeTrue;
 
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingAmbiguousDispatchTest.java b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingAmbiguousDispatchTest.java
index 61b73d3..50ea902 100644
--- a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingAmbiguousDispatchTest.java
+++ b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingAmbiguousDispatchTest.java
@@ -1,20 +1,12 @@
 // Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
-
 package com.android.tools.r8.memberrebinding;
 
-import static org.junit.Assert.assertThrows;
-import static org.junit.Assume.assumeFalse;
-import static org.junit.Assume.assumeTrue;
-
-import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.DiagnosticsMatcher;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestBuilder;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestRunResult;
-import com.android.tools.r8.ToolHelper.DexVm.Version;
 import com.android.tools.r8.utils.BooleanUtils;
 import java.util.List;
 import org.junit.Test;
@@ -26,7 +18,8 @@
 @RunWith(Parameterized.class)
 public class MemberRebindingAmbiguousDispatchTest extends TestBase {
 
-  @Parameter() public TestParameters parameters;
+  @Parameter(0)
+  public TestParameters parameters;
 
   @Parameter(1)
   public boolean abstractMethodOnSuperClass;
@@ -37,7 +30,7 @@
   @Parameters(name = "{0}, abstractMethodOnSuperClass: {1}, interfaceAsSymbolicReference {2}")
   public static List<Object[]> data() {
     return buildParameters(
-        getTestParameters().withAllRuntimesAndApiLevels().build(),
+        getTestParameters().withAllRuntimesAndApiLevels().withPartialCompilation().build(),
         BooleanUtils.values(),
         BooleanUtils.values());
   }
@@ -56,58 +49,34 @@
   }
 
   @Test
-  public void testRuntime() throws Exception {
-    testForRuntime(parameters)
+  public void testJvm() throws Exception {
+    parameters.assumeJvmTestParameters();
+    testForJvm(parameters)
         .apply(this::setupInput)
         .run(parameters.getRuntime(), Main.class)
         .apply(this::checkOutput);
   }
 
-  private boolean desugaringWithoutSupport() {
-    return parameters.isDexRuntime()
-        && interfaceAsSymbolicReference
-        && !parameters.canUseDefaultAndStaticInterfaceMethods();
+  @Test
+  public void testD8() throws Exception {
+    parameters.assumeDexRuntime();
+    testForD8(parameters)
+        .apply(this::setupInput)
+        .run(parameters.getRuntime(), Main.class)
+        .apply(this::checkOutput);
   }
 
   @Test
   public void testR8() throws Exception {
-    assumeFalse(desugaringWithoutSupport());
-    testForR8(parameters.getBackend())
+    testForR8(parameters)
         .apply(this::setupInput)
-        .setMinApi(parameters)
         .addKeepMainRule(Main.class)
         .run(parameters.getRuntime(), Main.class)
         .apply(this::checkOutput);
   }
 
-  @Test
-  public void testR8AssertionError() {
-    assumeTrue(desugaringWithoutSupport());
-    // TODO(b/259227990): We should not fail compilation.
-    assertThrows(
-        CompilationFailedException.class,
-        () ->
-            testForR8(parameters.getBackend())
-                .apply(this::setupInput)
-                .setMinApi(parameters)
-                .addKeepMainRule(Main.class)
-                .compileWithExpectedDiagnostics(
-                    diagnostics ->
-                        diagnostics.assertErrorThatMatches(
-                            DiagnosticsMatcher.diagnosticException(AssertionError.class))));
-  }
-
   private void checkOutput(TestRunResult<?> result) {
-    if (parameters.isDexRuntime()
-        && parameters.getDexRuntimeVersion().isDalvik()
-        && interfaceAsSymbolicReference) {
-      result.assertFailureWithErrorThatThrows(VerifyError.class);
-    } else if (parameters.isDexRuntime()
-        && parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V7_0_0)
-        && interfaceAsSymbolicReference
-        && !parameters.canUseDefaultAndStaticInterfaceMethods()) {
-      result.assertFailureWithErrorThatThrows(ClassNotFoundException.class);
-    } else if (abstractMethodOnSuperClass || interfaceAsSymbolicReference) {
+    if (abstractMethodOnSuperClass || interfaceAsSymbolicReference) {
       result.assertFailureWithErrorThatThrows(AbstractMethodError.class);
     } else {
       result.assertSuccessWithOutputLines("SuperClass::foo");
diff --git a/src/test/java17/com/android/tools/r8/jdk17/records/RecordOnlyReferencedFromCodeTest.java b/src/test/java17/com/android/tools/r8/jdk17/records/RecordOnlyReferencedFromCodeTest.java
index 82c3935..b115700 100644
--- a/src/test/java17/com/android/tools/r8/jdk17/records/RecordOnlyReferencedFromCodeTest.java
+++ b/src/test/java17/com/android/tools/r8/jdk17/records/RecordOnlyReferencedFromCodeTest.java
@@ -7,7 +7,6 @@
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.utils.AndroidApiLevel;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -46,14 +45,7 @@
         .setMinApi(parameters)
         .compile()
         .run(parameters.getRuntime(), Main.class)
-        .applyIf(
-            parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.V),
-            rr -> rr.assertSuccessWithOutputLines("false"),
-            rr ->
-                rr.assertFailureWithErrorThatThrows(
-                    parameters.getDexRuntimeVersion().isDalvik()
-                        ? NoClassDefFoundError.class
-                        : ClassNotFoundException.class));
+        .assertSuccessWithOutputLines("false");
   }
 
   static class Main {
diff --git a/src/test/testbase/java/com/android/tools/r8/TestBase.java b/src/test/testbase/java/com/android/tools/r8/TestBase.java
index b9bb920..43792e9 100644
--- a/src/test/testbase/java/com/android/tools/r8/TestBase.java
+++ b/src/test/testbase/java/com/android/tools/r8/TestBase.java
@@ -50,6 +50,7 @@
 import com.android.tools.r8.jasmin.JasminBuilder;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.origin.PathOrigin;
+import com.android.tools.r8.partial.R8PartialCompilationConfiguration;
 import com.android.tools.r8.profile.rewriting.ProfileCollectionAdditions;
 import com.android.tools.r8.references.ClassReference;
 import com.android.tools.r8.references.FieldReference;
@@ -231,7 +232,7 @@
 
   public R8TestBuilder<?, ?, ?> testForR8(TestParameters parameters) {
     return testForR8(parameters.getBackend(), parameters.getPartialCompilationTestParameters())
-        .setMinApi(parameters);
+        .applyIf(parameters.hasApiLevel(), b -> b.setMinApi(parameters));
   }
 
   public R8TestBuilder<?, ?, ?> testForR8(
@@ -283,7 +284,7 @@
 
   public TestCompilerBuilder<?, ?, ?, ?, ?> testForD8(TestParameters parameters) {
     return testForD8(parameters.getBackend(), parameters.getPartialCompilationTestParameters())
-        .setMinApi(parameters);
+        .applyIf(parameters.hasApiLevel(), b -> b.setMinApi(parameters));
   }
 
   public TestCompilerBuilder<?, ?, ?, ?, ?> testForD8(
@@ -292,7 +293,9 @@
       return testForD8(backend);
     }
     assumeTrue(partialCompilationTestParameters.isExcludeAll());
-    return testForR8Partial(backend).setR8PartialConfiguration(builder -> builder.excludeAll());
+    return testForR8Partial(backend)
+        .allowDiagnosticMessages()
+        .setR8PartialConfiguration(R8PartialCompilationConfiguration.Builder::excludeAll);
   }
 
   public AssistantTestBuilder testForAssistant() {
diff --git a/src/test/testbase/java/com/android/tools/r8/TestParameters.java b/src/test/testbase/java/com/android/tools/r8/TestParameters.java
index 9f3a57b..25702c9 100644
--- a/src/test/testbase/java/com/android/tools/r8/TestParameters.java
+++ b/src/test/testbase/java/com/android/tools/r8/TestParameters.java
@@ -183,6 +183,10 @@
     testCompilerBuilder.setMinApi(apiLevel);
   }
 
+  public boolean hasApiLevel() {
+    return apiLevel != null;
+  }
+
   // TODO(b/270021825): Tests should not access the underlying API level directly, but may be
   //  allowed to query if the api level satisfies some condition.
   @Deprecated
@@ -271,6 +275,11 @@
     return this;
   }
 
+  public TestParameters assumeNoPartialCompilation(String unusedReason) {
+    assumeTrue(getPartialCompilationTestParameters().isNone());
+    return this;
+  }
+
   public boolean isJvmTestParameters() {
     if (isCfRuntime()) {
       return apiLevel == null || representativeApiLevelForRuntime;
@@ -309,7 +318,8 @@
   }
 
   public boolean canUseR8Partial() {
-    return isDexRuntime() && apiLevel.isGreaterThanOrEqualTo(AndroidApiLevel.L);
+    return isDexRuntime()
+        && (apiLevel == null || apiLevel.isGreaterThanOrEqualTo(AndroidApiLevel.L));
   }
 
   public TestParameters assumeRuntimeTestParameters() {