Merge commit '0b8f92c5d3b57c23eb2e0b0c7abf016c9451ee3f' into dev-release
diff --git a/.gitignore b/.gitignore
index 13e30c3..b8c394a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,10 +18,12 @@
 build/
 buildSrc/out/
 src/*/out
+d8_r8/*/out
 gradle-app.setting
 gradle/*
 gradlew
 gradlew.bat
+d8_r8/gradle*
 local.properties
 r8.ipr
 r8.iws
@@ -295,7 +297,5 @@
 tools/*/dalvik-4.0.4
 tools/*/dalvik-4.0.4.tar.gz
 tools/*/dalvik.tar.gz
-tools/*/dx
-tools/*/dx.tar.gz
 tools/startup/__pycache__
 venv/
diff --git a/build.gradle b/build.gradle
index 11cc609..280db74 100644
--- a/build.gradle
+++ b/build.gradle
@@ -5,8 +5,6 @@
 
 
 import desugaredlibrary.CustomConversionAsmRewriterTask
-import dx.DexMergerTask
-import dx.DxTask
 import net.ltgt.gradle.errorprone.CheckSeverity
 import org.gradle.internal.os.OperatingSystem
 import tasks.DownloadDependency
@@ -397,7 +395,6 @@
                 "linux/host/art-master",
                 "linux/dalvik",
                 "linux/dalvik-4.0.4",
-                "${osString}/dx",
         ]
 ]
 
@@ -509,16 +506,6 @@
     }
 }
 
-task downloadDx {
-    cloudDependencies.each { entry ->
-        entry.value.each { entryFile ->
-            if (entryFile.endsWith("/dx")) {
-                dependsOn "${getDownloadDepsTaskName(entry.key, entryFile)}"
-            }
-        }
-    }
-}
-
 task downloadAndroidCts {
     cloudDependencies.each { entry ->
         entry.value.each { entryFile ->
@@ -700,21 +687,17 @@
         options.errorprone.check('MultipleTopLevelClasses', CheckSeverity.ERROR)
         options.errorprone.check('NarrowingCompoundAssignment', CheckSeverity.ERROR)
 
-        // TODO(b/270534077): These should likely be fixed/suppressed and become hard failures.
-        options.errorprone.check('JdkObsolete', CheckSeverity.OFF)
+        // TODO(b/270510095): These should likely be fixed/suppressed and become hard failures.
         options.errorprone.check('UnusedVariable', CheckSeverity.OFF)
         options.errorprone.check('EqualsUnsafeCast', CheckSeverity.OFF)
         options.errorprone.check('TypeParameterUnusedInFormals', CheckSeverity.OFF)
-        options.errorprone.check('LoopOverCharArray', CheckSeverity.OFF)
         options.errorprone.check('ImmutableEnumChecker', CheckSeverity.OFF)
         options.errorprone.check('BadImport', CheckSeverity.OFF)
-        options.errorprone.check('StringSplitter', CheckSeverity.OFF)
         options.errorprone.check('ComplexBooleanConstant', CheckSeverity.OFF)
         options.errorprone.check('StreamToIterable', CheckSeverity.OFF)
         options.errorprone.check('HidingField', CheckSeverity.OFF)
         options.errorprone.check('StreamResourceLeak', CheckSeverity.OFF)
         options.errorprone.check('CatchAndPrintStackTrace', CheckSeverity.OFF)
-        options.errorprone.check('ShortCircuitBoolean', CheckSeverity.OFF)
         options.errorprone.check('NonCanonicalType', CheckSeverity.OFF)
         options.errorprone.check('UnusedNestedClass', CheckSeverity.OFF)
         options.errorprone.check('AmbiguousMethodReference', CheckSeverity.OFF)
@@ -1711,26 +1694,10 @@
     dependsOn buildExampleJava18Jars
     dependsOn buildExampleAndroidApi
     def examplesDir = file("src/test/examples")
-    def noDexTests = [
-        "multidex",
-        "multidex002",
-        "multidex004",
-    ]
     examplesDir.eachDir { dir ->
-        def name = dir.getName();
-        if (!(name in noDexTests)) {
-            dependsOn "dex_example_${name}"
-            def exampleOutputDir = file("build/test/examples/" + name);
-            def dexPath = file("${exampleOutputDir}")
-            def debug = (name == "throwing")
-            if (!dexPath.exists()) {
-                dexPath.mkdirs()
-            }
-            task "dex_example_${name}"(type: DxTask, dependsOn: "jar_example_${name}") {
-                source = files(tasks.getByPath("jar_example_${name}")).asFileTree
-                destination = dexPath
-                debug = debug
-            }
+        def exampleOutputDir = file("build/test/examples/" + dir.getName());
+        if (!exampleOutputDir.exists()) {
+          exampleOutputDir.mkdirs()
         }
     }
 }
@@ -1759,10 +1726,11 @@
                 }
             }
         } else {
-            dependsOn "${taskName}_dexmerger"
+            dependsOn "${taskName}_smali"
+            dependsOn "${taskName}_jar"
             task "${taskName}_smali"(type: SmaliTask) {
                 source = smaliFiles
-                destination = intermediateFile
+                destination = destFile
             }
             task "${taskName}_java"(type: JavaCompile) {
                 source = javaFiles
@@ -1777,15 +1745,6 @@
                 destinationDir = destDir
                 from fileTree(dir: destDir, include: 'Test.class')
             }
-            task "${taskName}_dx"(type: DxTask, dependsOn: "${taskName}_jar") {
-                source = fileTree(dir: destDir, include: 'Test.jar')
-                destination = destDir
-            }
-            task "${taskName}_dexmerger"(
-                    type: DexMergerTask, dependsOn: ["${taskName}_dx", "${taskName}_smali"]) {
-                source = fileTree(dir: destDir, include: ["classes.dex", intermediateFileName])
-                destination = destFile
-            }
         }
     }
 }
diff --git a/buildSrc/src/main/java/dx/DexMergerTask.java b/buildSrc/src/main/java/dx/DexMergerTask.java
deleted file mode 100644
index 409337c..0000000
--- a/buildSrc/src/main/java/dx/DexMergerTask.java
+++ /dev/null
@@ -1,98 +0,0 @@
-// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package dx;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import javax.inject.Inject;
-import org.gradle.api.DefaultTask;
-import org.gradle.api.file.FileCollection;
-import org.gradle.api.file.RegularFileProperty;
-import org.gradle.api.provider.SetProperty;
-import org.gradle.api.tasks.InputFiles;
-import org.gradle.api.tasks.OutputFile;
-import org.gradle.api.tasks.TaskAction;
-import org.gradle.workers.WorkAction;
-import org.gradle.workers.WorkParameters;
-import org.gradle.workers.WorkerExecutor;
-import utils.Utils;
-
-public class DexMergerTask extends DefaultTask {
-
-  private final WorkerExecutor workerExecutor;
-
-  private FileCollection source;
-  private File destination;
-
-  @Inject
-  public DexMergerTask(WorkerExecutor workerExecutor) {
-    this.workerExecutor = workerExecutor;
-  }
-
-  @InputFiles
-  public FileCollection getSource() {
-    return source;
-  }
-
-  public void setSource(FileCollection source) {
-    this.source = source;
-  }
-
-  @OutputFile
-  public File getDestination() {
-    return destination;
-  }
-
-  public void setDestination(File destination) {
-    this.destination = destination;
-  }
-
-  @TaskAction
-  void exec() {
-    workerExecutor
-        .noIsolation()
-        .submit(
-            RunDexMerger.class,
-            parameters -> {
-              parameters.getSources().set(source.getFiles());
-              parameters.getDestination().set(destination);
-            });
-  }
-
-  public interface RunDexMergerParameters extends WorkParameters {
-
-    SetProperty<File> getSources();
-
-    RegularFileProperty getDestination();
-
-    RegularFileProperty getDexMergerExecutable();
-  }
-
-  public abstract static class RunDexMerger implements WorkAction<RunDexMergerParameters> {
-
-    @Override
-    public void execute() {
-      try {
-        RunDexMergerParameters parameters = getParameters();
-        List<String> command = new ArrayList<>();
-        command.add(Utils.dexMergerExecutable().toString());
-        command.add(parameters.getDestination().getAsFile().get().getCanonicalPath());
-        for (File source : parameters.getSources().get()) {
-          command.add(source.getCanonicalPath());
-        }
-        Process dexMerger = new ProcessBuilder(command).inheritIO().start();
-        int exitCode = dexMerger.waitFor();
-        if (exitCode != 0) {
-          throw new RuntimeException("Dex merger failed with code " + exitCode);
-        }
-      } catch (IOException e) {
-        throw new java.io.UncheckedIOException(e);
-      } catch (InterruptedException e) {
-        throw new RuntimeException(e);
-      }
-    }
-  }
-}
diff --git a/buildSrc/src/main/java/dx/DxTask.java b/buildSrc/src/main/java/dx/DxTask.java
deleted file mode 100644
index a66733a..0000000
--- a/buildSrc/src/main/java/dx/DxTask.java
+++ /dev/null
@@ -1,118 +0,0 @@
-// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package dx;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.UncheckedIOException;
-import java.util.ArrayList;
-import java.util.List;
-import javax.inject.Inject;
-import org.gradle.api.DefaultTask;
-import org.gradle.api.file.FileCollection;
-import org.gradle.api.file.RegularFileProperty;
-import org.gradle.api.provider.Property;
-import org.gradle.api.provider.SetProperty;
-import org.gradle.api.tasks.Input;
-import org.gradle.api.tasks.InputFiles;
-import org.gradle.api.tasks.OutputDirectory;
-import org.gradle.api.tasks.TaskAction;
-import org.gradle.workers.WorkAction;
-import org.gradle.workers.WorkParameters;
-import org.gradle.workers.WorkerExecutor;
-import utils.Utils;
-
-public class DxTask extends DefaultTask {
-
-  private final WorkerExecutor workerExecutor;
-
-  private FileCollection source;
-  private File destination;
-  private boolean debug;
-
-  @Inject
-  public DxTask(WorkerExecutor workerExecutor) {
-    this.workerExecutor = workerExecutor;
-  }
-
-  @InputFiles
-  public FileCollection getSource() {
-    return source;
-  }
-
-  public void setSource(FileCollection source) {
-    this.source = source;
-  }
-
-  @OutputDirectory
-  public File getDestination() {
-    return destination;
-  }
-
-  public void setDestination(File destination) {
-    this.destination = destination;
-  }
-
-  @Input
-  public boolean isDebug() {
-    return debug;
-  }
-
-  public void setDebug(boolean debug) {
-    this.debug = debug;
-  }
-
-  @TaskAction
-  void exec() {
-    workerExecutor
-        .noIsolation()
-        .submit(
-            RunDx.class,
-            parameters -> {
-              parameters.getSources().set(source.getFiles());
-              parameters.getDestination().set(destination);
-              parameters.getDebug().set(debug);
-            });
-  }
-
-  public interface RunDxParameters extends WorkParameters {
-
-    SetProperty<File> getSources();
-
-    RegularFileProperty getDestination();
-
-    Property<Boolean> getDebug();
-  }
-
-  public abstract static class RunDx implements WorkAction<RunDxParameters> {
-
-    @Override
-    public void execute() {
-      RunDxParameters parameters = getParameters();
-      try {
-        List<String> command = new ArrayList<>();
-        command.add(Utils.dxExecutable().toString());
-        command.add("--dex");
-        command.add("--output");
-        command.add(parameters.getDestination().getAsFile().get().getCanonicalPath());
-        if (parameters.getDebug().get()) {
-          command.add("--debug");
-        }
-        for (File source : parameters.getSources().get()) {
-          command.add(source.getCanonicalPath());
-        }
-
-        Process dx = new ProcessBuilder(command).inheritIO().start();
-        int exitCode = dx.waitFor();
-        if (exitCode != 0) {
-          throw new RuntimeException("dx failed with code " + exitCode);
-        }
-      } catch (IOException e) {
-        throw new UncheckedIOException(e);
-      } catch (InterruptedException e) {
-        throw new RuntimeException(e);
-      }
-    }
-  }
-}
diff --git a/commonBuildSrc/src/main/kotlin/Dependencies.kt b/commonBuildSrc/src/main/kotlin/Dependencies.kt
index b93dbcf..c10a576 100644
--- a/commonBuildSrc/src/main/kotlin/Dependencies.kt
+++ b/commonBuildSrc/src/main/kotlin/Dependencies.kt
@@ -44,6 +44,7 @@
   const val kotlinVersion = "1.8.0"
   const val kotlinMetadataVersion = "0.6.0"
   const val smaliVersion = "2.2b4"
+  const val errorproneVersion = "2.18.0"
 }
 
 object Deps {
@@ -60,4 +61,5 @@
   val kotlinStdLib by lazy { "org.jetbrains.kotlin:kotlin-stdlib:${Versions.kotlinVersion}" }
   val kotlinReflect by lazy { "org.jetbrains.kotlin:kotlin-reflect:${Versions.kotlinVersion}" }
   val smali by lazy { "org.smali:smali:${Versions.smaliVersion}" }
+  val errorprone by lazy { "com.google.errorprone:error_prone_core:${Versions.errorproneVersion}" }
 }
diff --git a/d8_r8/main/build.gradle.kts b/d8_r8/main/build.gradle.kts
index e664753..10d4b2b 100644
--- a/d8_r8/main/build.gradle.kts
+++ b/d8_r8/main/build.gradle.kts
@@ -2,9 +2,12 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+import net.ltgt.gradle.errorprone.errorprone
+
 plugins {
   `kotlin-dsl`
   id("dependencies-plugin")
+  id("net.ltgt.errorprone") version "3.0.1"
 }
 
 java {
@@ -26,8 +29,77 @@
   implementation(Deps.guava)
   implementation(Deps.joptSimple)
   implementation(Deps.kotlinMetadata)
+  errorprone(Deps.errorprone)
 }
 
 tasks.withType<JavaCompile> {
   println("NOTE: Running with JDK: " + org.gradle.internal.jvm.Jvm.current().javaHome)
+
+  // Enable error prone for D8/R8 main sources and make all warnings errors.
+  // Warnings that we have chosen not to fix (or suppress) are disabled outright below.
+  options.compilerArgs.add("-Werror")
+  options.errorprone.isEnabled.set(true)
+
+  // Non-default / Experimental checks - explicitly enforced.
+  options.errorprone.error("RemoveUnusedImports")
+  options.errorprone.error("InconsistentOverloads")
+  options.errorprone.error("MissingDefault")
+  options.errorprone.error("MultipleTopLevelClasses")
+  options.errorprone.error("NarrowingCompoundAssignment")
+
+  // TODO(b/270510095): These should likely be fixed/suppressed and become hard failures.
+  options.errorprone.disable("UnusedVariable")
+  options.errorprone.disable("EqualsUnsafeCast")
+  options.errorprone.disable("TypeParameterUnusedInFormals")
+  options.errorprone.disable("ImmutableEnumChecker")
+  options.errorprone.disable("BadImport")
+  options.errorprone.disable("ComplexBooleanConstant")
+  options.errorprone.disable("StreamToIterable")
+  options.errorprone.disable("HidingField")
+  options.errorprone.disable("StreamResourceLeak")
+  options.errorprone.disable("CatchAndPrintStackTrace")
+  options.errorprone.disable("NonCanonicalType")
+  options.errorprone.disable("UnusedNestedClass")
+  options.errorprone.disable("AmbiguousMethodReference")
+  options.errorprone.disable("InvalidParam")
+  options.errorprone.disable("CharacterGetNumericValue")
+  options.errorprone.disable("ModifyCollectionInEnhancedForLoop")
+  options.errorprone.disable("EmptyCatch")
+  options.errorprone.disable("ArgumentSelectionDefectChecker")
+  options.errorprone.disable("ImmutableAnnotationChecker")
+  options.errorprone.disable("ObjectToString")
+  options.errorprone.disable("DoNotClaimAnnotations")
+  options.errorprone.disable("AnnotateFormatMethod")
+
+  // TODO(b/270537614): Remove finalize uses.
+  options.errorprone.disable("Finalize")
+
+  // The following warnings could/should be active but are hit by R8 now so silence them.
+  options.errorprone.disable("EqualsGetClass")
+  options.errorprone.disable("MixedMutabilityReturnType")
+  options.errorprone.disable("UnnecessaryParentheses")
+  options.errorprone.disable("DoNotCallSuggester")
+  options.errorprone.disable("InlineMeSuggester")
+  options.errorprone.disable("MutablePublicArray")
+  options.errorprone.disable("DefaultCharset")
+  options.errorprone.disable("InconsistentCapitalization")
+  options.errorprone.disable("InlineFormatString")
+  options.errorprone.disable("MissingImplementsComparable")
+
+  // Warnings that cause unwanted edits (e.g., inability to write informative asserts).
+  options.errorprone.disable("AlreadyChecked")
+
+  // JavaDoc related warnings. Would be nice to resolve but of no real consequence.
+  options.errorprone.disable("InvalidLink")
+  options.errorprone.disable("InvalidBlockTag")
+  options.errorprone.disable("InvalidInlineTag")
+  options.errorprone.disable("EmptyBlockTag")
+  options.errorprone.disable("MissingSummary")
+  options.errorprone.disable("UnrecognisedJavadocTag")
+  options.errorprone.disable("AlmostJavadoc")
+
+  // Moving away from identity and canonical items is not planned.
+  options.errorprone.disable("ReferenceEquality")
+  options.errorprone.disable("IdentityHashMapUsage")
+
 }
diff --git a/scripts/update-host-dx.sh b/scripts/update-host-dx.sh
deleted file mode 100755
index 6d4a3a8..0000000
--- a/scripts/update-host-dx.sh
+++ /dev/null
@@ -1,51 +0,0 @@
-#! /bin/bash
-# Copyright (c) 2016, 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.
-
-# This script will update the host dx and dexmerger tools in tools/linux/dx.
-
-# Before running this script make sure the dx and dexmerger versions required are built
-# in ~/android/master (There are probably simpler ways to just build dx and dexmerger):
-#
-#  m -j24 build-art
-#
-# Maybe also run the Art host tests:
-#
-#  mm -j24 art-test-host
-#
-
-ANDROID_CHECKOUT=~/android/master
-ANDROID_HOST_BUILD=$ANDROID_CHECKOUT/out/host/linux-x86
-LINUX_TOOLS=tools/linux
-DX_DIR=dx
-DEST=$LINUX_TOOLS/$DX_DIR
-
-# Clean out the previous version of Art
-rm -rf $DEST
-
-# Required binaries and scripts.
-mkdir -p $DEST/bin
-cp $ANDROID_HOST_BUILD/bin/dx $DEST/bin
-cp $ANDROID_HOST_BUILD/bin/dexmerger $DEST/bin
-
-# Required framework files.
-mkdir -p $DEST/framework
-cp $ANDROID_HOST_BUILD/framework/dx.jar $DEST/framework
-
-# Build the tar to upload to Google Cloud Storage.
-DX_ARCHIVE=$DX_DIR.tar.gz
-pushd $LINUX_TOOLS > /dev/null
-rm -f $DX_ARCHIVE
-rm -f $DX_ARCHIVE.sha1
-tar caf $DX_ARCHIVE $DX_DIR
-popd  > /dev/null
-
-echo "New $LINUX_TOOLS/$DX_ARCHIVE archive created."
-echo ""
-echo "Now run:"
-echo ""
-echo "  cd $LINUX_TOOLS"
-echo "  upload_to_google_storage.py --bucket r8-deps $DX_DIR.tar.gz"
-echo ""
-echo "to upload to Google Cloud Storage"
diff --git a/src/main/java/com/android/tools/r8/ClassFileConsumer.java b/src/main/java/com/android/tools/r8/ClassFileConsumer.java
index 79330fb..fa3e5c4 100644
--- a/src/main/java/com/android/tools/r8/ClassFileConsumer.java
+++ b/src/main/java/com/android/tools/r8/ClassFileConsumer.java
@@ -4,7 +4,6 @@
 package com.android.tools.r8;
 
 import com.android.tools.r8.utils.ArchiveBuilder;
-import com.android.tools.r8.utils.ClassFileConsumerDataImpl;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.DirectoryBuilder;
 import com.android.tools.r8.utils.OutputBuilder;
@@ -37,28 +36,22 @@
    * {@param handler}. If an error is reported via {@param handler} and no exceptions are thrown,
    * then the compiler guaranties to exit with an error.
    *
-   * <p>The {@link ByteDataView} obtained by way of {@param data} object can only be assumed valid
-   * during the duration of the accept. If the bytes are needed beyond that, a copy must be made
-   * elsewhere.
+   * <p>The {@link ByteDataView} {@param data} object can only be assumed valid during the duration
+   * of the accept. If the bytes are needed beyond that, a copy must be made elsewhere.
    *
-   * @param data Java class-file encoded data and its meta-data.
+   * @param data Java class-file encoded data.
+   * @param descriptor Class descriptor of the class the data pertains to.
+   * @param handler Diagnostics handler for reporting.
    */
-  default void acceptClassFile(ClassFileConsumerData data) {
-    accept(data.getByteDataView(), data.getClassDescriptor(), data.getDiagnosticsHandler());
-  }
-
-  @Deprecated
-  default void accept(ByteDataView data, String descriptor, DiagnosticsHandler handler) {
-    acceptClassFile(new ClassFileConsumerDataImpl(data, descriptor, handler));
-  }
+  void accept(ByteDataView data, String descriptor, DiagnosticsHandler handler);
 
   /** Empty consumer to request the production of the resource but ignore its value. */
   static ClassFileConsumer emptyConsumer() {
     return ForwardingConsumer.EMPTY_CONSUMER;
   }
 
+  /** Forwarding consumer to delegate to an optional existing consumer. */
   @Keep
-  @Deprecated
   class ForwardingConsumer implements ClassFileConsumer {
 
     private static final ClassFileConsumer EMPTY_CONSUMER = new ForwardingConsumer(null);
@@ -75,9 +68,9 @@
     }
 
     @Override
-    public void acceptClassFile(ClassFileConsumerData data) {
+    public void accept(ByteDataView data, String descriptor, DiagnosticsHandler handler) {
       if (consumer != null) {
-        consumer.acceptClassFile(data);
+        consumer.accept(data, descriptor, handler);
       }
     }
 
@@ -124,12 +117,9 @@
     }
 
     @Override
-    public void acceptClassFile(ClassFileConsumerData data) {
-      super.acceptClassFile(data);
-      outputBuilder.addFile(
-          DescriptorUtils.getClassFileName(data.getClassDescriptor()),
-          data.getByteDataView(),
-          data.getDiagnosticsHandler());
+    public void accept(ByteDataView data, String descriptor, DiagnosticsHandler handler) {
+      super.accept(data, descriptor, handler);
+      outputBuilder.addFile(DescriptorUtils.getClassFileName(descriptor), data, handler);
     }
 
     @Override
@@ -204,12 +194,9 @@
     }
 
     @Override
-    public void acceptClassFile(ClassFileConsumerData data) {
-      super.acceptClassFile(data);
-      outputBuilder.addFile(
-          DescriptorUtils.getClassFileName(data.getClassDescriptor()),
-          data.getByteDataView(),
-          data.getDiagnosticsHandler());
+    public void accept(ByteDataView data, String descriptor, DiagnosticsHandler handler) {
+      super.accept(data, descriptor, handler);
+      outputBuilder.addFile(DescriptorUtils.getClassFileName(descriptor), data, handler);
     }
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/ClassFileConsumerData.java b/src/main/java/com/android/tools/r8/ClassFileConsumerData.java
deleted file mode 100644
index eeef801..0000000
--- a/src/main/java/com/android/tools/r8/ClassFileConsumerData.java
+++ /dev/null
@@ -1,26 +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;
-
-/** Data provided in the primary callback of {@link ClassFileConsumer}. */
-@Keep
-public interface ClassFileConsumerData {
-
-  /**
-   * View of the Java class-file encoded data.
-   *
-   * <p>The obtained {@link ByteDataView} object can only be assumed valid during the duration of
-   * the accept method. If the bytes are needed beyond that, a copy must be made elsewhere.
-   */
-  ByteDataView getByteDataView();
-
-  /** Copy of the bytes for the Java class-file encoded data. */
-  byte[] getByteDataCopy();
-
-  /** Class descriptor of the class the data pertains to. */
-  String getClassDescriptor();
-
-  /** Diagnostics handler for reporting. */
-  DiagnosticsHandler getDiagnosticsHandler();
-}
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 85a0fbf..52735a5 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -53,6 +53,7 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 
@@ -323,6 +324,8 @@
 
       timing.end(); // post-converter
 
+      reportSyntheticInformation(appView);
+
       if (options.isGeneratingClassFiles()) {
         new CfApplicationWriter(appView, marker).write(options.getClassFileConsumer(), inputApp);
       } else {
@@ -341,6 +344,15 @@
     }
   }
 
+  private static void reportSyntheticInformation(AppView<?> appView) {
+    SyntheticInfoConsumer consumer = appView.options().getSyntheticInfoConsumer();
+    if (consumer == null || !appView.options().intermediate) {
+      return;
+    }
+    appView.getSyntheticItems().reportSyntheticsInformation(consumer);
+    consumer.finished();
+  }
+
   private static void initializeAssumeInfoCollection(AppView<AppInfo> appView) {
     AssumeInfoCollection.Builder assumeInfoCollectionBuilder = AssumeInfoCollection.builder();
     AbstractValueFactory abstractValueFactory = appView.abstractValueFactory();
@@ -440,14 +452,12 @@
     private final List<ProgramResource> resources = new ArrayList<>();
 
     @Override
-    public synchronized void acceptDexIndexedFile(DexIndexedConsumerData data) {
+    public synchronized void accept(
+        int fileIndex, ByteDataView data, Set<String> descriptors, DiagnosticsHandler handler) {
       // TODO(b/154106502): Map Origin information.
       resources.add(
           ProgramResource.fromBytes(
-              Origin.unknown(),
-              ProgramResource.Kind.DEX,
-              data.getByteDataCopy(),
-              data.getClassDescriptors()));
+              Origin.unknown(), ProgramResource.Kind.DEX, data.copyByteData(), descriptors));
     }
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index 13c12a2..d1495c2 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -90,6 +90,7 @@
     private List<GlobalSyntheticsResourceProvider> globalSyntheticsResourceProviders =
         new ArrayList<>();
     private DesugarGraphConsumer desugarGraphConsumer = null;
+    private SyntheticInfoConsumer syntheticInfoConsumer = null;
     private StringConsumer desugaredLibraryKeepRuleConsumer = null;
     private String synthesizedClassPrefix = "";
     private boolean enableMainDexListCheck = true;
@@ -268,6 +269,21 @@
       return self();
     }
 
+    /** Get the consumer that will receive information about compiler synthesized classes. */
+    public SyntheticInfoConsumer getSyntheticInfoConsumer() {
+      return syntheticInfoConsumer;
+    }
+
+    /**
+     * Set the consumer that will receive information about compiler synthesized classes.
+     *
+     * <p>Setting the consumer will clear any previously set consumer.
+     */
+    public Builder setSyntheticInfoConsumer(SyntheticInfoConsumer syntheticInfoConsumer) {
+      this.syntheticInfoConsumer = syntheticInfoConsumer;
+      return self();
+    }
+
     /**
      * Add a collection of startup profile providers that should be used for distributing the
      * program classes in dex.
@@ -456,6 +472,7 @@
           getIncludeClassesChecksum(),
           getDexClassChecksumFilter(),
           getDesugarGraphConsumer(),
+          getSyntheticInfoConsumer(),
           desugaredLibraryKeepRuleConsumer,
           desugaredLibrarySpecification,
           getAssertionsConfiguration(),
@@ -479,6 +496,7 @@
 
   private final boolean intermediate;
   private final GlobalSyntheticsConsumer globalSyntheticsConsumer;
+  private final SyntheticInfoConsumer syntheticInfoConsumer;
   private final DesugarGraphConsumer desugarGraphConsumer;
   private final StringConsumer desugaredLibraryKeepRuleConsumer;
   private final DesugaredLibrarySpecification desugaredLibrarySpecification;
@@ -549,6 +567,7 @@
       boolean encodeChecksum,
       BiPredicate<String, Long> dexClassChecksumFilter,
       DesugarGraphConsumer desugarGraphConsumer,
+      SyntheticInfoConsumer syntheticInfoConsumer,
       StringConsumer desugaredLibraryKeepRuleConsumer,
       DesugaredLibrarySpecification desugaredLibrarySpecification,
       List<AssertionsConfiguration> assertionsConfiguration,
@@ -590,6 +609,7 @@
         classConflictResolver);
     this.intermediate = intermediate;
     this.globalSyntheticsConsumer = globalSyntheticsConsumer;
+    this.syntheticInfoConsumer = syntheticInfoConsumer;
     this.desugarGraphConsumer = desugarGraphConsumer;
     this.desugaredLibraryKeepRuleConsumer = desugaredLibraryKeepRuleConsumer;
     this.desugaredLibrarySpecification = desugaredLibrarySpecification;
@@ -606,6 +626,7 @@
     super(printHelp, printVersion);
     intermediate = false;
     globalSyntheticsConsumer = null;
+    syntheticInfoConsumer = null;
     desugarGraphConsumer = null;
     desugaredLibraryKeepRuleConsumer = null;
     desugaredLibrarySpecification = null;
@@ -641,6 +662,7 @@
     internal.intermediate = intermediate;
     internal.retainCompileTimeAnnotations = intermediate;
     internal.setGlobalSyntheticsConsumer(globalSyntheticsConsumer);
+    internal.setSyntheticInfoConsumer(syntheticInfoConsumer);
     internal.desugarGraphConsumer = desugarGraphConsumer;
     internal.mainDexKeepRules = mainDexKeepRules;
     internal.proguardMapConsumer = proguardMapConsumer;
diff --git a/src/main/java/com/android/tools/r8/DexFilePerClassFileConsumer.java b/src/main/java/com/android/tools/r8/DexFilePerClassFileConsumer.java
index 68e6f12..1675cb2 100644
--- a/src/main/java/com/android/tools/r8/DexFilePerClassFileConsumer.java
+++ b/src/main/java/com/android/tools/r8/DexFilePerClassFileConsumer.java
@@ -34,7 +34,7 @@
 @KeepForSubclassing
 public interface DexFilePerClassFileConsumer extends ProgramConsumer, ByteBufferProvider {
 
-  boolean SHOULD_COMBINE_SYNTHETIC_CLASSES = true;
+  static final boolean SHOULD_COMBINE_SYNTHETIC_CLASSES = true;
 
   /**
    * Callback to receive DEX data for a single Java class-file input and its companion classes.
@@ -42,30 +42,24 @@
    * <p>There is no guaranteed order and files might be written concurrently.
    *
    * <p>The consumer is expected not to throw, but instead report any errors via the diagnostics
-   * {@link DiagnosticsHandler} in the callback data. If an error is reported via handler and no
-   * exceptions are thrown, then the compiler guaranties to exit with an error.
+   * {@param handler}. If an error is reported via {@param handler} and no exceptions are thrown,
+   * then the compiler guaranties to exit with an error.
    *
-   * <p>The {@link ByteDataView} obtained from the {@param data} object can only be assumed valid
-   * during the duration of the accept. If the bytes are needed beyond that, a copy must be made
-   * elsewhere.
+   * <p>The {@link ByteDataView} {@param data} object can only be assumed valid during the duration
+   * of the accept. If the bytes are needed beyond that, a copy must be made elsewhere.
+   *
+   * @param primaryClassDescriptor Class descriptor of the class from the input class-file.
+   * @param data DEX encoded data in a ByteDataView wrapper.
+   * @param descriptors Class descriptors for all classes defined in the DEX data.
+   * @param handler Diagnostics handler for reporting.
    */
-  default void acceptDexFile(DexFilePerClassFileConsumerData data) {
-    accept(
-        data.getPrimaryClassDescriptor(),
-        data.getByteDataView(),
-        data.getClassDescriptors(),
-        data.getDiagnosticsHandler());
-  }
-
-  // Any new implementation should not use or call the deprecated accept method.
-  @Deprecated
   default void accept(
       String primaryClassDescriptor,
       ByteDataView data,
       Set<String> descriptors,
       DiagnosticsHandler handler) {
-    // To avoid breaking binary compatibility, old consumers not implementing the new API will be
-    // forwarded to. New consumers must implement the accept method on the data object.
+    // To avoid breaking binary compatiblity, old consumers not implementing the new API will be
+    // forwarded to. New consumers must implement the accept on ByteDataView.
     accept(primaryClassDescriptor, data.copyByteData(), descriptors, handler);
   }
 
@@ -98,8 +92,8 @@
     return ForwardingConsumer.EMPTY_CONSUMER;
   }
 
+  /** Forwarding consumer to delegate to an optional existing consumer. */
   @Keep
-  @Deprecated
   class ForwardingConsumer implements DexFilePerClassFileConsumer {
 
     private static final DexFilePerClassFileConsumer EMPTY_CONSUMER = new ForwardingConsumer(null);
@@ -116,9 +110,13 @@
     }
 
     @Override
-    public void acceptDexFile(DexFilePerClassFileConsumerData data) {
+    public void accept(
+        String primaryClassDescriptor,
+        ByteDataView data,
+        Set<String> descriptors,
+        DiagnosticsHandler handler) {
       if (consumer != null) {
-        consumer.acceptDexFile(data);
+        consumer.accept(primaryClassDescriptor, data, descriptors, handler);
       }
     }
 
@@ -180,12 +178,13 @@
     }
 
     @Override
-    public void acceptDexFile(DexFilePerClassFileConsumerData data) {
-      super.acceptDexFile(data);
-      outputBuilder.addFile(
-          getDexFileName(data.getPrimaryClassDescriptor()),
-          data.getByteDataView(),
-          data.getDiagnosticsHandler());
+    public void accept(
+        String primaryClassDescriptor,
+        ByteDataView data,
+        Set<String> descriptors,
+        DiagnosticsHandler handler) {
+      super.accept(primaryClassDescriptor, data, descriptors, handler);
+      outputBuilder.addFile(getDexFileName(primaryClassDescriptor), data, handler);
     }
 
     @Override
@@ -263,12 +262,13 @@
     }
 
     @Override
-    public void acceptDexFile(DexFilePerClassFileConsumerData data) {
-      super.acceptDexFile(data);
-      outputBuilder.addFile(
-          getDexFileName(data.getPrimaryClassDescriptor()),
-          data.getByteDataView(),
-          data.getDiagnosticsHandler());
+    public void accept(
+        String primaryClassDescriptor,
+        ByteDataView data,
+        Set<String> descriptors,
+        DiagnosticsHandler handler) {
+      super.accept(primaryClassDescriptor, data, descriptors, handler);
+      outputBuilder.addFile(getDexFileName(primaryClassDescriptor), data, handler);
     }
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/DexFilePerClassFileConsumerData.java b/src/main/java/com/android/tools/r8/DexFilePerClassFileConsumerData.java
index fba6b8d..e69de29 100644
--- a/src/main/java/com/android/tools/r8/DexFilePerClassFileConsumerData.java
+++ b/src/main/java/com/android/tools/r8/DexFilePerClassFileConsumerData.java
@@ -1,35 +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;
-
-import java.util.Set;
-
-/** Data provided in the primary callback of {@link DexFilePerClassFileConsumer}. */
-@Keep
-public interface DexFilePerClassFileConsumerData {
-
-  /** Class descriptor of the class from the input class-file. */
-  String getPrimaryClassDescriptor();
-
-  /** DEX encoded data in a ByteDataView wrapper. */
-  ByteDataView getByteDataView();
-
-  /** Copy of the bytes for the DEX encoded data. */
-  byte[] getByteDataCopy();
-
-  /** Class descriptors for all classes defined in the DEX data. */
-  Set<String> getClassDescriptors();
-
-  /** Diagnostics handler for reporting. */
-  DiagnosticsHandler getDiagnosticsHandler();
-
-  /**
-   * Class descriptor of the primary-class's synthetic context.
-   *
-   * <p>If primary class is a compiler-synthesized class (i.e. it is an input that was synthesized
-   * by a prior D8 intermediate compilation) then the value is the descriptor of the class that
-   * caused the primary class to be synthesized. The value is null in all other cases.
-   */
-  String getSynthesizingContextForPrimaryClass();
-}
diff --git a/src/main/java/com/android/tools/r8/DexIndexedConsumer.java b/src/main/java/com/android/tools/r8/DexIndexedConsumer.java
index cbccf59..39f5b2e 100644
--- a/src/main/java/com/android/tools/r8/DexIndexedConsumer.java
+++ b/src/main/java/com/android/tools/r8/DexIndexedConsumer.java
@@ -46,22 +46,17 @@
    * <p>There is no guaranteed order and files might be written concurrently.
    *
    * <p>The consumer is expected not to throw, but instead report any errors via the diagnostics
-   * {@link DiagnosticsHandler} in the callback data. If an error is reported via handler and no
-   * exceptions are thrown, then the compiler guaranties to exit with an error.
+   * {@param handler}. If an error is reported via {@param handler} and no exceptions are thrown,
+   * then the compiler guaranties to exit with an error.
    *
-   * <p>The {@link ByteDataView} obtained from the {@param data} object can only be assumed valid
-   * during the duration of the accept. If the bytes are needed beyond that, a copy must be made
-   * elsewhere.
+   * <p>The {@link ByteDataView} {@param data} object can only be assumed valid during the duration
+   * of the accept. If the bytes are needed beyond that, a copy must be made elsewhere.
+   *
+   * @param fileIndex Index of the DEX file for multi-dexing. Files are zero-indexed.
+   * @param data DEX encoded data in a ByteDataView wrapper.
+   * @param descriptors Class descriptors for all classes defined in the DEX data.
+   * @param handler Diagnostics handler for reporting.
    */
-  default void acceptDexIndexedFile(DexIndexedConsumerData data) {
-    accept(
-        data.getFileIndex(),
-        data.getByteDataView(),
-        data.getClassDescriptors(),
-        data.getDiagnosticsHandler());
-  }
-
-  @Deprecated
   default void accept(
       int fileIndex, ByteDataView data, Set<String> descriptors, DiagnosticsHandler handler) {
     // To avoid breaking binary compatiblity, old consumers not implementing the new API will be
@@ -82,8 +77,8 @@
     return ForwardingConsumer.EMPTY_CONSUMER;
   }
 
+  /** Forwarding consumer to delegate to an optional existing consumer. */
   @Keep
-  @Deprecated
   class ForwardingConsumer implements DexIndexedConsumer {
 
     private static final DexIndexedConsumer EMPTY_CONSUMER = new ForwardingConsumer(null);
@@ -100,9 +95,10 @@
     }
 
     @Override
-    public void acceptDexIndexedFile(DexIndexedConsumerData data) {
+    public void accept(
+        int fileIndex, ByteDataView data, Set<String> descriptors, DiagnosticsHandler handler) {
       if (consumer != null) {
-        consumer.acceptDexIndexedFile(data);
+        consumer.accept(fileIndex, data, descriptors, handler);
       }
     }
 
@@ -154,13 +150,11 @@
     }
 
     @Override
-    public void acceptDexIndexedFile(DexIndexedConsumerData data) {
-      super.acceptDexIndexedFile(data);
+    public void accept(
+        int fileIndex, ByteDataView data, Set<String> descriptors, DiagnosticsHandler handler) {
+      super.accept(fileIndex, data, descriptors, handler);
       outputBuilder.addIndexedClassFile(
-          data.getFileIndex(),
-          DexUtils.getDefaultDexFileName(data.getFileIndex()),
-          data.getByteDataView(),
-          data.getDiagnosticsHandler());
+          fileIndex, DexUtils.getDefaultDexFileName(fileIndex), data, handler);
     }
 
     @Override
@@ -251,17 +245,15 @@
     }
 
     @Override
-    public void acceptDexIndexedFile(DexIndexedConsumerData data) {
-      super.acceptDexIndexedFile(data);
+    public void accept(
+        int fileIndex, ByteDataView data, Set<String> descriptors, DiagnosticsHandler handler) {
+      super.accept(fileIndex, data, descriptors, handler);
       try {
         prepareDirectory();
       } catch (IOException e) {
-        data.getDiagnosticsHandler().error(new ExceptionDiagnostic(e, new PathOrigin(directory)));
+        handler.error(new ExceptionDiagnostic(e, new PathOrigin(directory)));
       }
-      outputBuilder.addFile(
-          DexUtils.getDefaultDexFileName(data.getFileIndex()),
-          data.getByteDataView(),
-          data.getDiagnosticsHandler());
+      outputBuilder.addFile(DexUtils.getDefaultDexFileName(fileIndex), data, handler);
     }
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/DexIndexedConsumerData.java b/src/main/java/com/android/tools/r8/DexIndexedConsumerData.java
deleted file mode 100644
index 1c46f8c..0000000
--- a/src/main/java/com/android/tools/r8/DexIndexedConsumerData.java
+++ /dev/null
@@ -1,26 +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;
-
-import java.util.Set;
-
-/** Data provided in the primary callback of {@link DexIndexedConsumer}. */
-@Keep
-public interface DexIndexedConsumerData {
-
-  /** Index of the DEX file for multi-dexing (Files are zero-indexed). */
-  int getFileIndex();
-
-  /** DEX encoded data in a ByteDataView wrapper. */
-  ByteDataView getByteDataView();
-
-  /** Copy of the bytes for the DEX encoded data. */
-  byte[] getByteDataCopy();
-
-  /** Class descriptors for all classes defined in the DEX data. */
-  Set<String> getClassDescriptors();
-
-  /** Diagnostics handler for reporting. */
-  DiagnosticsHandler getDiagnosticsHandler();
-}
diff --git a/src/main/java/com/android/tools/r8/SyntheticInfoConsumer.java b/src/main/java/com/android/tools/r8/SyntheticInfoConsumer.java
new file mode 100644
index 0000000..2bfc5b2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/SyntheticInfoConsumer.java
@@ -0,0 +1,34 @@
+// 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;
+
+@Keep
+public interface SyntheticInfoConsumer {
+
+  /**
+   * Callback with information about a compiler synthesized class.
+   *
+   * <p>This callback is only used in intermediate mode builds where the compiler may synthesize
+   * intermediate classes that can be de-duplicated and merged in a later non-incremental step.
+   *
+   * <p>This callback will always be called before the synthetic class is included in the data to
+   * any compiler outputs. Thus, it is safe to assume the information provided here is present when
+   * any compiler output consumers are called for the synthetic class. E.g., in the callbacks of
+   * {@link ClassFileConsumer} or {@link DexFilePerClassFileConsumer}.
+   *
+   * <p>Note: this callback may be called on multiple threads.
+   *
+   * <p>Note: this callback places no guarantees on order of calls or on duplicate calls.
+   *
+   * @param data Information about the synthetic class.
+   */
+  void acceptSyntheticInfo(SyntheticInfoConsumerData data);
+
+  /**
+   * Callback indicating no more synthetics will be generated for the active compilation unit.
+   *
+   * <p>Note: this callback places no other guarantees on number of calls or on which threads.
+   */
+  void finished();
+}
diff --git a/src/main/java/com/android/tools/r8/SyntheticInfoConsumerData.java b/src/main/java/com/android/tools/r8/SyntheticInfoConsumerData.java
new file mode 100644
index 0000000..be28596
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/SyntheticInfoConsumerData.java
@@ -0,0 +1,17 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8;
+
+import com.android.tools.r8.references.ClassReference;
+
+/** Information about a compiler synthesized class. */
+@Keep
+public interface SyntheticInfoConsumerData {
+
+  /** Get the reference for the compiler synthesized class. */
+  ClassReference getSyntheticClass();
+
+  /** Get the reference for the context that gave rise to the synthesized class. */
+  ClassReference getSynthesizingContextClass();
+}
diff --git a/src/main/java/com/android/tools/r8/cf/CfRegisterAllocator.java b/src/main/java/com/android/tools/r8/cf/CfRegisterAllocator.java
index 6fd3574..8ec6ba4d 100644
--- a/src/main/java/com/android/tools/r8/cf/CfRegisterAllocator.java
+++ b/src/main/java/com/android/tools/r8/cf/CfRegisterAllocator.java
@@ -96,9 +96,13 @@
   private final List<LiveIntervals> liveIntervals = new ArrayList<>();
 
   // List of active intervals.
+  // TODO(b/270398965): Replace LinkedList.
+  @SuppressWarnings("JdkObsolete")
   private final List<LiveIntervals> active = new LinkedList<>();
 
   // List of intervals where the current instruction falls into one of their live range holes.
+  // TODO(b/270398965): Replace LinkedList.
+  @SuppressWarnings("JdkObsolete")
   private final List<LiveIntervals> inactive = new LinkedList<>();
 
   // List of intervals that no register has been allocated to sorted by first live range.
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 d0daf58..99cb304 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -58,8 +58,6 @@
 import com.android.tools.r8.utils.ArrayUtils;
 import com.android.tools.r8.utils.Box;
 import com.android.tools.r8.utils.DescriptorUtils;
-import com.android.tools.r8.utils.DexFilePerClassFileConsumerDataImpl;
-import com.android.tools.r8.utils.DexIndexedConsumerDataImpl;
 import com.android.tools.r8.utils.ExceptionUtils;
 import com.android.tools.r8.utils.IntBox;
 import com.android.tools.r8.utils.InternalGlobalSyntheticsProgramConsumer;
@@ -588,14 +586,13 @@
     ProgramConsumer consumer;
     ByteBufferProvider byteBufferProvider;
 
-    String primaryClassDescriptor = virtualFile.getPrimaryClassDescriptor();
     if (globalSyntheticFiles != null && globalSyntheticFiles.contains(virtualFile)) {
       consumer = globalsSyntheticsConsumer;
       byteBufferProvider = globalsSyntheticsConsumer;
     } else if (programConsumer != null) {
       consumer = programConsumer;
       byteBufferProvider = programConsumer;
-    } else if (primaryClassDescriptor != null) {
+    } else if (virtualFile.getPrimaryClassDescriptor() != null) {
       consumer = options.getDexFilePerClassFileConsumer();
       byteBufferProvider = options.getDexFilePerClassFileConsumer();
     } else {
@@ -623,18 +620,14 @@
     timing.begin("Pass bytes to consumer");
     if (consumer instanceof DexFilePerClassFileConsumer) {
       ((DexFilePerClassFileConsumer) consumer)
-          .acceptDexFile(
-              new DexFilePerClassFileConsumerDataImpl(
-                  primaryClassDescriptor,
-                  virtualFile.getPrimaryClassSynthesizingContextDescriptor(),
-                  data,
-                  virtualFile.getClassDescriptors(),
-                  options.reporter));
+          .accept(
+              virtualFile.getPrimaryClassDescriptor(),
+              data,
+              virtualFile.getClassDescriptors(),
+              options.reporter);
     } else {
       ((DexIndexedConsumer) consumer)
-          .acceptDexIndexedFile(
-              new DexIndexedConsumerDataImpl(
-                  virtualFile.getId(), data, virtualFile.getClassDescriptors(), options.reporter));
+          .accept(virtualFile.getId(), data, virtualFile.getClassDescriptors(), options.reporter);
     }
     timing.end();
     // Release use of the backing buffer now that accept has returned.
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriterExperimental.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriterExperimental.java
index cdb74b5..0a81bd2 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriterExperimental.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriterExperimental.java
@@ -18,7 +18,6 @@
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.ObjectToOffsetMapping;
 import com.android.tools.r8.utils.BitUtils;
-import com.android.tools.r8.utils.DexIndexedConsumerDataImpl;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.ThreadUtils;
@@ -173,9 +172,7 @@
     if (consumer instanceof DexFilePerClassFileConsumer) {
       assert false;
     } else {
-      ((DexIndexedConsumer) consumer)
-          .acceptDexIndexedFile(
-              new DexIndexedConsumerDataImpl(0, data, Sets.newIdentityHashSet(), options.reporter));
+      ((DexIndexedConsumer) consumer).accept(0, data, Sets.newIdentityHashSet(), options.reporter);
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/dex/InheritanceClassInDexDistributor.java b/src/main/java/com/android/tools/r8/dex/InheritanceClassInDexDistributor.java
index 5a62bfb..4d10674 100644
--- a/src/main/java/com/android/tools/r8/dex/InheritanceClassInDexDistributor.java
+++ b/src/main/java/com/android/tools/r8/dex/InheritanceClassInDexDistributor.java
@@ -514,6 +514,8 @@
    * I5 --> I1, I2<br>
    * I3 and I4 will be in the same group even if they have no relation with each other.
    */
+  // TODO(b/270398965): Replace LinkedList.
+  @SuppressWarnings("JdkObsolete")
   private List<ClassGroup> collectInheritanceGroups() {
     // Considering classes are the nodes of a graph which edges are the inheritance relation between
     // classes. We just want to isolate every connected sub-graphs.
diff --git a/src/main/java/com/android/tools/r8/dex/JumboStringRewriter.java b/src/main/java/com/android/tools/r8/dex/JumboStringRewriter.java
index 785158d..a68f99f 100644
--- a/src/main/java/com/android/tools/r8/dex/JumboStringRewriter.java
+++ b/src/main/java/com/android/tools/r8/dex/JumboStringRewriter.java
@@ -252,6 +252,8 @@
     return code.getDebugInfo();
   }
 
+  // TODO(b/270398965): Replace LinkedList.
+  @SuppressWarnings("JdkObsolete")
   private List<DexInstruction> expandCode() {
     LinkedList<DexInstruction> instructions = new LinkedList<>();
     Collections.addAll(instructions, method.getCode().asDexCode().instructions);
diff --git a/src/main/java/com/android/tools/r8/dump/DumpOptions.java b/src/main/java/com/android/tools/r8/dump/DumpOptions.java
index 0c538fa..9779fcf 100644
--- a/src/main/java/com/android/tools/r8/dump/DumpOptions.java
+++ b/src/main/java/com/android/tools/r8/dump/DumpOptions.java
@@ -14,6 +14,7 @@
 import com.android.tools.r8.shaking.ProguardConfigurationRule;
 import com.android.tools.r8.startup.StartupProfileProvider;
 import com.android.tools.r8.utils.InternalOptions.DesugarState;
+import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.ThreadUtils;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -179,17 +180,22 @@
   }
 
   public static void parse(String content, DumpOptions.Builder builder) {
-    String[] lines = content.split("\n");
-    for (String line : lines) {
-      String trimmed = line.trim();
-      int i = trimmed.indexOf('=');
-      if (i < 0) {
-        throw new RuntimeException("Invalid dump line. Expected = in line: '" + trimmed + "'");
-      }
-      String key = trimmed.substring(0, i).trim();
-      String value = trimmed.substring(i + 1).trim();
-      parseKeyValue(builder, key, value);
-    }
+    StringUtils.splitForEach(
+        content,
+        '\n',
+        line -> {
+          String trimmed = line.trim();
+          if (trimmed.isEmpty()) {
+            return;
+          }
+          int i = trimmed.indexOf('=');
+          if (i < 0) {
+            throw new RuntimeException("Invalid dump line. Expected = in line: '" + trimmed + "'");
+          }
+          String key = trimmed.substring(0, i).trim();
+          String value = trimmed.substring(i + 1).trim();
+          parseKeyValue(builder, key, value);
+        });
   }
 
   private static void parseKeyValue(Builder builder, String key, String value) {
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassInstanceFieldsMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassInstanceFieldsMerger.java
index 195a8fc..f43d5ec 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassInstanceFieldsMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassInstanceFieldsMerger.java
@@ -103,6 +103,8 @@
     }
   }
 
+  // TODO(b/270398965): Replace LinkedList.
+  @SuppressWarnings("JdkObsolete")
   static Map<InstanceFieldInfo, LinkedList<DexEncodedField>> getAvailableFieldsByExactInfo(
       DexProgramClass target) {
     Map<InstanceFieldInfo, LinkedList<DexEncodedField>> availableFieldsByInfo =
@@ -115,6 +117,8 @@
     return availableFieldsByInfo;
   }
 
+  // TODO(b/270398965): Replace LinkedList.
+  @SuppressWarnings("JdkObsolete")
   static Map<InstanceFieldInfo, LinkedList<DexEncodedField>> getAvailableFieldsByRelaxedInfo(
       AppView<? extends AppInfoWithClassHierarchy> appView,
       Map<InstanceFieldInfo, LinkedList<DexEncodedField>> availableFieldsByExactInfo) {
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
index 299c516..8fc788b 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
@@ -332,6 +332,8 @@
     }
   }
 
+  // TODO(b/270398965): Replace LinkedList.
+  @SuppressWarnings("JdkObsolete")
   private List<MergeGroup> getInitialGroups() {
     MergeGroup initialClassGroup = new MergeGroup();
     MergeGroup initialInterfaceGroup = new MergeGroup();
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/MergeGroup.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/MergeGroup.java
index fe08b37..e0f2b3b 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/MergeGroup.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/MergeGroup.java
@@ -31,6 +31,8 @@
 
   public static class Metadata {}
 
+  // TODO(b/270398965): Replace LinkedList.
+  @SuppressWarnings("JdkObsolete")
   private final LinkedList<DexProgramClass> classes;
 
   private DexField classIdField;
@@ -39,6 +41,8 @@
 
   private BidirectionalManyToOneMap<DexEncodedField, DexEncodedField> instanceFieldMap;
 
+  // TODO(b/270398965): Replace LinkedList.
+  @SuppressWarnings("JdkObsolete")
   public MergeGroup() {
     this.classes = new LinkedList<>();
   }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyExecutor.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyExecutor.java
index c206e75..28ec89d 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyExecutor.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyExecutor.java
@@ -34,6 +34,8 @@
     }
   }
 
+  // TODO(b/270398965): Replace LinkedList.
+  @SuppressWarnings("JdkObsolete")
   private LinkedList<MergeGroup> applyMultiClassPolicy(
       MultiClassPolicy policy, LinkedList<MergeGroup> groups) {
     // For each group apply the multi class policy and add all the new groups together.
@@ -51,6 +53,8 @@
     return newGroups;
   }
 
+  // TODO(b/270398965): Replace LinkedList.
+  @SuppressWarnings("JdkObsolete")
   private <T> LinkedList<MergeGroup> applyMultiClassPolicyWithPreprocessing(
       MultiClassPolicyWithPreprocessing<T> policy,
       LinkedList<MergeGroup> groups,
@@ -77,6 +81,8 @@
    * policies registered to this policy executor on the class groups yielding a new collection of
    * class groups.
    */
+  // TODO(b/270398965): Replace LinkedList.
+  @SuppressWarnings("JdkObsolete")
   public Collection<MergeGroup> run(
       Collection<MergeGroup> inputGroups,
       Collection<Policy> policies,
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/AtMostOneClassThatMatchesPolicy.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/AtMostOneClassThatMatchesPolicy.java
index c73675f..17bb034 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/AtMostOneClassThatMatchesPolicy.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/AtMostOneClassThatMatchesPolicy.java
@@ -17,6 +17,8 @@
 
 public abstract class AtMostOneClassThatMatchesPolicy extends MultiClassPolicy {
 
+  // TODO(b/270398965): Replace LinkedList.
+  @SuppressWarnings("JdkObsolete")
   @Override
   public Collection<MergeGroup> apply(MergeGroup group) {
     // Create a new merge group for each class that we want at most one of.
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/LimitClassGroups.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/LimitClassGroups.java
index 55d0472..15070c2 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/LimitClassGroups.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/LimitClassGroups.java
@@ -24,6 +24,8 @@
     assert maxGroupSize >= 2;
   }
 
+  // TODO(b/270398965): Replace LinkedList.
+  @SuppressWarnings("JdkObsolete")
   @Override
   public Collection<MergeGroup> apply(MergeGroup group) {
     if (group.size() <= maxGroupSize || group.isInterfaceGroup()) {
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoClassInitializerCycles.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoClassInitializerCycles.java
index 744154c..b1ad665 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoClassInitializerCycles.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoClassInitializerCycles.java
@@ -106,6 +106,8 @@
     this.appView = appView;
   }
 
+  // TODO(b/270398965): Replace LinkedList.
+  @SuppressWarnings("JdkObsolete")
   @Override
   public Collection<MergeGroup> apply(MergeGroup group, Void nothing) {
     // Partition the merge group into smaller groups that may be merged. If the class initialization
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDeadLocks.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDeadLocks.java
index 576e0ef..f120117 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDeadLocks.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDeadLocks.java
@@ -25,6 +25,8 @@
     return appView.appInfo().isLockCandidate(clazz.type) || clazz.hasStaticSynchronizedMethods();
   }
 
+  // TODO(b/270398965): Replace LinkedList.
+  @SuppressWarnings("JdkObsolete")
   @Override
   public Collection<MergeGroup> apply(MergeGroup group) {
     // Gather all synchronized classes.
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoWeakerAccessPrivileges.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoWeakerAccessPrivileges.java
index 600b5a8..2305438 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoWeakerAccessPrivileges.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoWeakerAccessPrivileges.java
@@ -43,6 +43,8 @@
     this.immediateSubtypingInfo = immediateSubtypingInfo;
   }
 
+  // TODO(b/270398965): Replace LinkedList.
+  @SuppressWarnings("JdkObsolete")
   @Override
   public Collection<MergeGroup> apply(MergeGroup group) {
     List<MergeGroup> newMergeGroups = new LinkedList<>();
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/OnlyDirectlyConnectedOrUnrelatedInterfaces.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/OnlyDirectlyConnectedOrUnrelatedInterfaces.java
index 6d54364..fd24d1f 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/OnlyDirectlyConnectedOrUnrelatedInterfaces.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/OnlyDirectlyConnectedOrUnrelatedInterfaces.java
@@ -70,6 +70,8 @@
     this.mode = mode;
   }
 
+  // TODO(b/270398965): Replace LinkedList.
+  @SuppressWarnings("JdkObsolete")
   @Override
   public Collection<MergeGroup> apply(MergeGroup group, SubtypingInfo subtypingInfo) {
     if (!group.isInterfaceGroup()) {
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreserveMethodCharacteristics.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreserveMethodCharacteristics.java
index a6e27f5..60f57e4 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreserveMethodCharacteristics.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreserveMethodCharacteristics.java
@@ -135,6 +135,8 @@
     }
   }
 
+  // TODO(b/270398965): Replace LinkedList.
+  @SuppressWarnings("JdkObsolete")
   @Override
   public Collection<MergeGroup> apply(MergeGroup group) {
     List<TargetGroup> groups = new ArrayList<>();
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/constant/SparseConditionalConstantPropagation.java b/src/main/java/com/android/tools/r8/ir/analysis/constant/SparseConditionalConstantPropagation.java
index 0ac3c7e..69ef71c 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/constant/SparseConditionalConstantPropagation.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/constant/SparseConditionalConstantPropagation.java
@@ -36,8 +36,13 @@
   private final AppView<?> appView;
   private final IRCode code;
   private final Map<Value, LatticeElement> mapping = new HashMap<>();
+  // TODO(b/270398965): Replace LinkedList.
+  @SuppressWarnings("JdkObsolete")
   private final Deque<Value> ssaEdges = new LinkedList<>();
+  // TODO(b/270398965): Replace LinkedList.
+  @SuppressWarnings("JdkObsolete")
   private final Deque<BasicBlock> flowEdges = new LinkedList<>();
+
   private final int maxBlockNumber;
   private final BitSet[] executableFlowEdges;
   private final BitSet visitedBlocks;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoMessageInfo.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoMessageInfo.java
index 69d0ae9..631502d 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoMessageInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoMessageInfo.java
@@ -26,8 +26,14 @@
 
     private int flags;
 
+    // TODO(b/270398965): Replace LinkedList.
+    @SuppressWarnings("JdkObsolete")
     private LinkedList<ProtoFieldInfo> fields;
+    // TODO(b/270398965): Replace LinkedList.
+    @SuppressWarnings("JdkObsolete")
     private LinkedList<ProtoFieldObject> hasBitsObjects;
+    // TODO(b/270398965): Replace LinkedList.
+    @SuppressWarnings("JdkObsolete")
     private LinkedList<ProtoOneOfObjectPair> oneOfObjects;
 
     private Builder(ProgramMethod dynamicMethod) {
@@ -38,6 +44,8 @@
       this.flags = value;
     }
 
+    // TODO(b/270398965): Replace LinkedList.
+    @SuppressWarnings("JdkObsolete")
     public void addField(ProtoFieldInfo field) {
       if (fields == null) {
         fields = new LinkedList<>();
@@ -45,6 +53,8 @@
       fields.add(field);
     }
 
+    // TODO(b/270398965): Replace LinkedList.
+    @SuppressWarnings("JdkObsolete")
     public void addHasBitsObject(ProtoFieldObject hasBitsObject) {
       if (hasBitsObjects == null) {
         hasBitsObjects = new LinkedList<>();
@@ -52,6 +62,8 @@
       hasBitsObjects.add(hasBitsObject);
     }
 
+    // TODO(b/270398965): Replace LinkedList.
+    @SuppressWarnings("JdkObsolete")
     public void addOneOfObject(ProtoFieldObject oneOfObject, ProtoFieldObject oneOfCaseObject) {
       if (oneOfObjects == null) {
         oneOfObjects = new LinkedList<>();
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
index 3827b37..276150a 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
@@ -169,7 +169,10 @@
   // Catch handler information about which successors are catch handlers and what their guards are.
   private CatchHandlers<Integer> catchHandlers = CatchHandlers.EMPTY_INDICES;
 
+  // TODO(b/270398965): Replace LinkedList.
+  @SuppressWarnings("JdkObsolete")
   private LinkedList<Instruction> instructions = new LinkedList<>();
+
   private int number = -1;
   private List<Phi> phis = new ArrayList<>();
 
@@ -1413,6 +1416,8 @@
    * Remove a number of instructions. The instructions to remove are given as indexes in the
    * instruction stream.
    */
+  // TODO(b/270398965): Replace LinkedList.
+  @SuppressWarnings("JdkObsolete")
   public void removeInstructions(List<Integer> toRemove) {
     if (!toRemove.isEmpty()) {
       LinkedList<Instruction> newInstructions = new LinkedList<>();
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCode.java b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
index 56f1150..a46b0a2 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRCode.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
@@ -447,10 +447,11 @@
   }
 
   /**
-   * Trace blocks and attempt to put fallthrough blocks immediately after the block that
-   * falls through. When we fail to do that we create a new fallthrough block with an explicit
-   * goto to the actual fallthrough block.
+   * Trace blocks and attempt to put fallthrough blocks immediately after the block that falls
+   * through. When we fail to do that we create a new fallthrough block with an explicit goto to the
+   * actual fallthrough block.
    */
+  @SuppressWarnings("JdkObsolete") // Consider replacing the use of LinkedList.
   public void traceBlocks() {
     // Get the blocks first, as calling topologicallySortedBlocks also sets marks.
     ImmutableList<BasicBlock> sorted = topologicallySortedBlocks();
diff --git a/src/main/java/com/android/tools/r8/ir/code/Value.java b/src/main/java/com/android/tools/r8/ir/code/Value.java
index f4c211f..d7b86fe 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Value.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Value.java
@@ -167,9 +167,17 @@
 
   protected final int number;
   public Instruction definition = null;
+
+  // TODO(b/270398965): Replace LinkedList.
+  @SuppressWarnings("JdkObsolete")
   private LinkedList<Instruction> users = new LinkedList<>();
+
   private Set<Instruction> uniqueUsers = null;
+
+  // TODO(b/270398965): Replace LinkedList.
+  @SuppressWarnings("JdkObsolete")
   private LinkedList<Phi> phiUsers = new LinkedList<>();
+
   private Set<Phi> uniquePhiUsers = null;
   private Value nextConsecutive = null;
   private Value previousConsecutive = null;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index 35c6667..c9bc10c 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -375,6 +375,8 @@
   private final Reference2IntMap<BasicBlock> offsets = new Reference2IntOpenHashMap<>();
 
   // Worklist of reachable blocks.
+  // TODO(b/270398965): Replace LinkedList.
+  @SuppressWarnings("JdkObsolete")
   private final Queue<Integer> traceBlocksWorklist = new LinkedList<>();
 
   // Bitmap to ensure we don't process an instruction more than once.
@@ -384,9 +386,13 @@
   private Set<Integer> processedSubroutineInstructions = null;
 
   // Worklist for SSA construction.
+  // TODO(b/270398965): Replace LinkedList.
+  @SuppressWarnings("JdkObsolete")
   private final Queue<WorklistItem> ssaWorklist = new LinkedList<>();
 
   // Basic blocks. Added after processing from the worklist.
+  // TODO(b/270398965): Replace LinkedList.
+  @SuppressWarnings("JdkObsolete")
   private final LinkedList<BasicBlock> blocks = new LinkedList<>();
 
   private BasicBlock entryBlock = null;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index c21f00e..0050541 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
@@ -65,7 +65,8 @@
 import com.android.tools.r8.lightir.Lir2IRConverter;
 import com.android.tools.r8.lightir.LirCode;
 import com.android.tools.r8.lightir.LirStrategy;
-import com.android.tools.r8.lightir.LirStrategy.PhiInInstructionsStrategy;
+import com.android.tools.r8.lightir.LirStrategy.ExternalPhisStrategy;
+import com.android.tools.r8.lightir.PhiInInstructionsStrategy;
 import com.android.tools.r8.naming.IdentifierNameStringMarker;
 import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagatorIROptimizer;
 import com.android.tools.r8.position.MethodPosition;
@@ -1055,12 +1056,18 @@
       OptimizationFeedback feedback,
       BytecodeMetadataProvider bytecodeMetadataProvider,
       Timing timing) {
-    LirStrategy<Value, Integer> strategy = new PhiInInstructionsStrategy();
-    timing.begin("IR->LIR");
-    LirCode<Integer> lirCode =
+    IRCode round1 = doRoundtripWithStrategy(code, new ExternalPhisStrategy(), "indirect phis");
+    IRCode round2 = doRoundtripWithStrategy(round1, new PhiInInstructionsStrategy(), "inline phis");
+    return round2;
+  }
+
+  private <EV, S extends LirStrategy<Value, EV>> IRCode doRoundtripWithStrategy(
+      IRCode code, S strategy, String name) {
+    timing.begin("IR->LIR (" + name + ")");
+    LirCode<EV> lirCode =
         IR2LirConverter.translate(code, strategy.getEncodingStrategy(), appView.dexItemFactory());
     timing.end();
-    timing.begin("LIR->IR");
+    timing.begin("LIR->IR (" + name + ")");
     IRCode irCode =
         Lir2IRConverter.translate(
             code.context(), lirCode, strategy.getDecodingStrategy(lirCode), appView);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
index 80abf84..362d883 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
@@ -867,6 +867,8 @@
   //   argument instruction
   // - Removes unused arguments
   // - Updates the type of arguments whose type has been strengthened
+  // TODO(b/270398965): Replace LinkedList.
+  @SuppressWarnings("JdkObsolete")
   private void rewriteArguments(
       IRCode code,
       DexMethod originalMethodReference,
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 39fc23a..cf7a083 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
@@ -229,12 +229,7 @@
         int limit = 11;
         for (DexProgramClass clazz : appView.appInfo().classesWithDeterministicOrder()) {
           if (!clazz.type.descriptor.startsWith(neverMergePrefix)) {
-            boolean hasExceptionPrefix = false;
-            for (DexString exceptionPrefix : neverMerge.getExceptionPrefixes()) {
-              hasExceptionPrefix =
-                  hasExceptionPrefix | clazz.type.descriptor.startsWith(exceptionPrefix);
-            }
-            if (hasExceptionPrefix) {
+            if (hasExceptionPrefix(clazz)) {
               continue;
             }
             if (limit-- < 0) {
@@ -255,6 +250,15 @@
     }
   }
 
+  private boolean hasExceptionPrefix(DexProgramClass clazz) {
+    for (DexString exceptionPrefix : neverMerge.getExceptionPrefixes()) {
+      if (clazz.type.descriptor.startsWith(exceptionPrefix)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
   void classSynthesisDesugaring(
       ExecutorService executorService,
       CfClassSynthesizerDesugaringEventConsumer classSynthesizerEventConsumer)
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/callgraph/CycleEliminator.java b/src/main/java/com/android/tools/r8/ir/conversion/callgraph/CycleEliminator.java
index dbae4a9..52dd9bc 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/callgraph/CycleEliminator.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/callgraph/CycleEliminator.java
@@ -393,6 +393,8 @@
     return false;
   }
 
+  // TODO(b/270398965): Replace LinkedList.
+  @SuppressWarnings("JdkObsolete")
   private LinkedList<Node> extractCycle(Node entry) {
     LinkedList<Node> cycle = new LinkedList<>();
     do {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java b/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
index 788afd1..8ac1b02 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
@@ -54,18 +54,18 @@
 // the contained CovariantReturnType annotations.
 public final class CovariantReturnTypeAnnotationTransformer {
 
-  private final AppView<?> appView;
   private final IRConverter converter;
   private final MethodProcessorEventConsumer methodProcessorEventConsumer =
       MethodProcessorEventConsumer.empty();
   private final DexItemFactory factory;
 
   public CovariantReturnTypeAnnotationTransformer(AppView<?> appView, IRConverter converter) {
-    this.appView = appView;
     this.converter = converter;
     this.factory = appView.dexItemFactory();
   }
 
+  // TODO(b/270398965): Replace LinkedList.
+  @SuppressWarnings("JdkObsolete")
   public void process(
       DexApplication.Builder<?> builder,
       CovariantReturnTypeAnnotationTransformerEventConsumer eventConsumer) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateHtmlDoc.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateHtmlDoc.java
index d502339..45aa53f 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateHtmlDoc.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateHtmlDoc.java
@@ -17,6 +17,8 @@
 import com.android.tools.r8.ir.desugar.desugaredlibrary.lint.SupportedClasses.MethodAnnotation;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.lint.SupportedClasses.SupportedClass;
 import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.StringUtils;
 import java.io.PrintStream;
 import java.nio.file.Files;
 import java.util.ArrayList;
@@ -124,10 +126,10 @@
       if (rewritten != null) {
         return rewritten;
       }
-      String[] split = packageName.split("\\.");
-      if (split.length > 2) {
-        String prevPackage =
-            packageName.substring(0, packageName.length() - split[split.length - 1].length() - 1);
+      List<String> split = StringUtils.split(packageName, '.');
+      if (split.size() > 2) {
+        String last = ListUtils.last(split);
+        String prevPackage = packageName.substring(0, packageName.length() - last.length() - 1);
         return typeInPackage(typeName, prevPackage);
       }
       return null;
@@ -303,14 +305,14 @@
     }
 
     private String format(String s, int i) {
-      String[] regexpSplit = s.split("\\.");
-      if (regexpSplit.length < i) {
+      List<String> split = StringUtils.split(s, '.');
+      if (split.size() < i) {
         return s;
       }
       int splitIndex = 0;
       int mid = i / 2;
       for (int j = 0; j < mid; j++) {
-        splitIndex += regexpSplit[j].length();
+        splitIndex += split.get(j).length();
       }
       splitIndex += mid;
       return s.substring(0, splitIndex) + HTML_SPLIT + s.substring(splitIndex);
@@ -340,17 +342,17 @@
         return appendLiCode(s);
       }
       StringBuilder sb = new StringBuilder();
-      String[] split = s.split("\\(");
-      sb.append(split[0]).append('(').append(HTML_SPLIT);
-      if (split[1].length() < MAX_LINE_CHARACTERS - 2) {
-        sb.append(split[1]);
+      List<String> split = StringUtils.split(s, '(');
+      sb.append(split.get(0)).append('(').append(HTML_SPLIT);
+      if (split.get(1).length() < MAX_LINE_CHARACTERS - 2) {
+        sb.append(split.get(1));
         return appendLiCode(sb.toString());
       }
-      String[] secondSplit = split[1].split(",");
+      List<String> secondSplit = StringUtils.split(split.get(1), ',');
       sb.append("&nbsp;");
-      for (int i = 0; i < secondSplit.length; i++) {
-        sb.append(secondSplit[i]);
-        if (i != secondSplit.length - 1) {
+      for (int i = 0; i < secondSplit.size(); i++) {
+        sb.append(secondSplit.get(i));
+        if (i != secondSplit.size() - 1) {
           sb.append(',');
           sb.append(HTML_SPLIT);
         }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java
index c357c2a..31baed9 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java
@@ -14,7 +14,9 @@
 import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion.LibraryValidator;
 import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.SemanticVersion;
+import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.Timing;
 import java.util.List;
 import java.util.Map;
@@ -251,8 +253,9 @@
     if (leadingVersionNumberCache != -1) {
       return leadingVersionNumberCache;
     }
-    String[] split = topLevelFlags.getIdentifier().split(":");
-    return leadingVersionNumberCache = SemanticVersion.parse(split[split.length - 1]).getMajor();
+    List<String> split = StringUtils.split(topLevelFlags.getIdentifier(), ':');
+    String last = ListUtils.last(split);
+    return leadingVersionNumberCache = SemanticVersion.parse(last).getMajor();
   }
 
   public boolean includesJDK11Methods() {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineWrapperConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineWrapperConverter.java
index e471367..e8c8073 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineWrapperConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineWrapperConverter.java
@@ -136,6 +136,8 @@
             });
   }
 
+  // TODO(b/270398965): Replace LinkedList.
+  @SuppressWarnings("JdkObsolete")
   private LinkedHashMap<DexType, WrapperDescriptorBuilder> orderDescriptors(
       Map<DexType, WrapperDescriptorBuilder> descriptors) {
     LinkedHashMap<DexType, WrapperDescriptorBuilder> orderedDescriptors = new LinkedHashMap<>();
@@ -155,6 +157,8 @@
     return orderedDescriptors;
   }
 
+  // TODO(b/270398965): Replace LinkedList.
+  @SuppressWarnings("JdkObsolete")
   private void finalizeWrapperDescriptors(
       LinkedHashMap<DexType, WrapperDescriptorBuilder> descriptors,
       MachineRewritingFlags.Builder builder) {
@@ -171,6 +175,8 @@
         });
   }
 
+  // TODO(b/270398965): Replace LinkedList.
+  @SuppressWarnings("JdkObsolete")
   private void fillDescriptors(
       DexClass wrapperClass,
       Set<DexMethod> excludedMethods,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index cd2c596..9f985e9 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -636,6 +636,8 @@
    * Covert the switch instruction to a sequence of if instructions checking for a specified set of
    * keys, followed by a new switch with the remaining keys.
    */
+  // TODO(b/270398965): Replace LinkedList.
+  @SuppressWarnings("JdkObsolete")
   void convertSwitchToSwitchAndIfs(
       IRCode code,
       ListIterator<BasicBlock> blocksIterator,
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
index 8d146ad..90c6c2b 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
@@ -151,8 +151,12 @@
   // List of all top-level live intervals for all SSA values.
   private List<LiveIntervals> liveIntervals = new ArrayList<>();
   // List of active intervals.
+  // TODO(b/270398965): Replace LinkedList.
+  @SuppressWarnings("JdkObsolete")
   private List<LiveIntervals> active = new LinkedList<>();
   // List of intervals where the current instruction falls into one of their live range holes.
+  // TODO(b/270398965): Replace LinkedList.
+  @SuppressWarnings("JdkObsolete")
   protected List<LiveIntervals> inactive = new LinkedList<>();
   // List of intervals that no register has been allocated to sorted by first live range.
   protected PriorityQueue<LiveIntervals> unhandled = new PriorityQueue<>();
@@ -243,6 +247,8 @@
     clearState();
   }
 
+  // TODO(b/270398965): Replace LinkedList.
+  @SuppressWarnings("JdkObsolete")
   public static void computeDebugInfo(
       IRCode code,
       ImmutableList<BasicBlock> blocks,
@@ -1112,6 +1118,8 @@
    * allocated and have been moved from unhandled to inactive. The move sources have their hints
    * updated. The rest of the register allocation state is unchanged.
    */
+  // TODO(b/270398965): Replace LinkedList.
+  @SuppressWarnings("JdkObsolete")
   private void allocateArgumentIntervalsWithSrc(LiveIntervals srcInterval, ArgumentReuseMode mode) {
     Value value = srcInterval.getValue();
     for (Instruction instruction : value.uniqueUsers()) {
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMoveScheduler.java b/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMoveScheduler.java
index f7e377d..5251d17 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMoveScheduler.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMoveScheduler.java
@@ -60,6 +60,8 @@
     valueMap.put(move.dst, move.dst);
   }
 
+  // TODO(b/270398965): Replace LinkedList.
+  @SuppressWarnings("JdkObsolete")
   public void schedule() {
     assert everyDestinationOnlyWrittenOnce();
 
diff --git a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
index 49f2fbb..cc88ca9 100644
--- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -47,7 +47,6 @@
 import com.android.tools.r8.synthesis.SyntheticNaming;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.AsmUtils;
-import com.android.tools.r8.utils.ClassFileConsumerDataImpl;
 import com.android.tools.r8.utils.ComparatorUtils;
 import com.android.tools.r8.utils.ExceptionUtils;
 import com.android.tools.r8.utils.InternalGlobalSyntheticsProgramConsumer.InternalGlobalSyntheticsCfConsumer;
@@ -335,10 +334,7 @@
       verifyCf(result);
     }
     ExceptionUtils.withConsumeResourceHandler(
-        options.reporter,
-        handler ->
-            consumer.acceptClassFile(
-                new ClassFileConsumerDataImpl(ByteDataView.of(result), desc, handler)));
+        options.reporter, handler -> consumer.accept(ByteDataView.of(result), desc, handler));
   }
 
   private int compareTypesThroughLens(DexType a, DexType b) {
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinSourceDebugExtensionParser.java b/src/main/java/com/android/tools/r8/kotlin/KotlinSourceDebugExtensionParser.java
index 065ff92..6cf38cb 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinSourceDebugExtensionParser.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinSourceDebugExtensionParser.java
@@ -6,6 +6,7 @@
 
 import com.android.tools.r8.naming.Range;
 import com.android.tools.r8.utils.SegmentTree;
+import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.ThrowingConsumer;
 import java.io.BufferedReader;
 import java.io.Closeable;
@@ -216,8 +217,8 @@
       throws KotlinSourceDebugExtensionParserException {
     // + <file_number_i> <file_name_i>
     // <file_path_i>
-    String[] entries = entryLine.trim().split(" ");
-    if (entries.length != 3 || !entries[0].equals("+")) {
+    String[] entries = StringUtils.splitKnownSize(entryLine.trim(), ' ', 3);
+    if (entries == null || !entries[0].equals("+")) {
       throw new KotlinSourceDebugExtensionParserException(
           "Wrong number of entries on line " + entryLine);
     }
diff --git a/src/main/java/com/android/tools/r8/lightir/ByteUtils.java b/src/main/java/com/android/tools/r8/lightir/ByteUtils.java
index f719f11..5d34483 100644
--- a/src/main/java/com/android/tools/r8/lightir/ByteUtils.java
+++ b/src/main/java/com/android/tools/r8/lightir/ByteUtils.java
@@ -51,6 +51,15 @@
     return (value >= 0) && (value <= 0xFFFF);
   }
 
+  private static int truncateToU2(int value) {
+    return value & 0xFFFF;
+  }
+
+  public static int ensureU2(int value) {
+    assert isU2(value);
+    return truncateToU2(value);
+  }
+
   public static int unsetBitAtIndex(int value, int index) {
     return value & ~(1 << (index - 1));
   }
diff --git a/src/main/java/com/android/tools/r8/lightir/IR2LirConverter.java b/src/main/java/com/android/tools/r8/lightir/IR2LirConverter.java
index 21af715..ee53ceb 100644
--- a/src/main/java/com/android/tools/r8/lightir/IR2LirConverter.java
+++ b/src/main/java/com/android/tools/r8/lightir/IR2LirConverter.java
@@ -48,6 +48,11 @@
     strategy.defineBlock(block, blockIndex);
   }
 
+  private boolean recordPhi(Phi phi, int valueIndex) {
+    recordValue(phi, valueIndex);
+    return strategy.isPhiInlineInstruction();
+  }
+
   private void recordValue(Value value, int valueIndex) {
     EV encodedValue = strategy.defineValue(value, valueIndex);
     if (value.hasLocalInfo()) {
@@ -69,16 +74,8 @@
     BasicBlockIterator blockIt = irCode.listIterator();
     while (blockIt.hasNext()) {
       BasicBlock block = blockIt.next();
-      if (block.hasPhis()) {
-        // The block order of the predecessors may change, since the LIR does not encode the
-        // direct links, the block order is used to determine predecessor order.
-        int[] permutation = computePermutation(block.getPredecessors(), strategy::getBlockIndex);
-        Value[] operands = new Value[block.getPredecessors().size()];
-        for (Phi phi : block.getPhis()) {
-          permuteOperands(phi.getOperands(), permutation, operands);
-          builder.addPhi(phi.getType(), Arrays.asList(operands));
-          currentValueIndex++;
-        }
+      if (strategy.isPhiInlineInstruction()) {
+        currentValueIndex += computePhis(block);
       }
       if (block.hasCatchHandlers()) {
         CatchHandlers<BasicBlock> handlers = block.getCatchHandlers();
@@ -112,6 +109,25 @@
       }
       assert builder.verifyCurrentValueIndex(currentValueIndex);
     }
+    if (!strategy.isPhiInlineInstruction()) {
+      irCode.listIterator().forEachRemaining(this::computePhis);
+    }
+  }
+
+  private int computePhis(BasicBlock block) {
+    int valuesOffset = 0;
+    if (block.hasPhis()) {
+      // The block order of the predecessors may change, since the LIR does not encode the
+      // direct links, the block order is used to determine predecessor order.
+      int[] permutation = computePermutation(block.getPredecessors(), strategy::getBlockIndex);
+      Value[] operands = new Value[block.getPredecessors().size()];
+      for (Phi phi : block.getPhis()) {
+        permuteOperands(phi.getOperands(), permutation, operands);
+        builder.addPhi(phi.getType(), Arrays.asList(operands));
+        valuesOffset++;
+      }
+    }
+    return valuesOffset;
   }
 
   private void computeBlockAndValueTables() {
@@ -120,9 +136,10 @@
     for (BasicBlock block : irCode.blocks) {
       recordBlock(block, instructionIndex);
       for (Phi phi : block.getPhis()) {
-        recordValue(phi, valueIndex);
-        valueIndex++;
-        instructionIndex++;
+        if (recordPhi(phi, valueIndex)) {
+          valueIndex++;
+          instructionIndex++;
+        }
       }
       for (Instruction instruction : block.getInstructions()) {
         if (instruction.hasOutValue()) {
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 cc64c4e..e09e9f5 100644
--- a/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java
+++ b/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java
@@ -4,7 +4,6 @@
 package com.android.tools.r8.lightir;
 
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DebugLocalInfo;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexString;
@@ -58,6 +57,7 @@
       AppView<?> appView) {
     Parser<EV> parser = new Parser<>(lirCode, method.getReference(), appView, strategy);
     parser.parseArguments(method);
+    parser.ensureDebugInfo();
     lirCode.forEach(view -> view.accept(parser));
     return parser.getIRCode(method);
   }
@@ -157,6 +157,23 @@
       advanceNextPositionEntry();
     }
 
+    public void ensureDebugInfo() {
+      if (code.getDebugLocalInfoTable() == null) {
+        return;
+      }
+      code.getDebugLocalInfoTable()
+          .forEachLocalDefinition(
+              (encodedValue, localInfo) -> {
+                Value value = getValue(encodedValue);
+                if (!value.hasLocalInfo()) {
+                  value.setLocalInfo(localInfo);
+                }
+                assert value.getLocalInfo() == localInfo;
+              });
+    }
+
+    // TODO(b/270398965): Replace LinkedList.
+    @SuppressWarnings("JdkObsolete")
     public IRCode getIRCode(ProgramMethod method) {
       LinkedList<BasicBlock> blockList = new LinkedList<>();
       IntList blockIndices = new IntArrayList(blocks.keySet());
@@ -192,7 +209,7 @@
     }
 
     public Value getValue(EV encodedValue) {
-      return strategy.getValue(encodedValue);
+      return strategy.getValue(encodedValue, code.getStrategyInfo());
     }
 
     public List<Value> getValues(List<EV> indices) {
@@ -213,20 +230,34 @@
 
     public Value getOutValueForNextInstruction(TypeElement type) {
       int valueIndex = toInstructionIndexInIR(peekNextInstructionIndex());
-      DebugLocalInfo localInfo = code.getDebugLocalInfo(valueIndex);
-      return strategy.getValueDefinitionForInstructionIndex(valueIndex, type, localInfo);
+      return strategy.getValueDefinitionForInstructionIndex(
+          valueIndex, type, code::getDebugLocalInfo);
     }
 
     public Phi getPhiForNextInstructionAndAdvanceState(TypeElement type) {
-      int valueIndex = toInstructionIndexInIR(peekNextInstructionIndex());
-      DebugLocalInfo localInfo = code.getDebugLocalInfo(valueIndex);
-      // TODO(b/225838009): The phi constructor implicitly adds to the block, so we need to ensure
-      //  the block. However, we must grab the index above. Find a way to clean this up so it is
-      //  uniform with instructions.
-      advanceInstructionState();
-      // Creating the phi implicitly adds it to currentBlock.
-      return strategy.getPhiDefinitionForInstructionIndex(
-          valueIndex, currentBlock, type, localInfo);
+      int instructionIndex = peekNextInstructionIndex();
+      int valueIndex = toInstructionIndexInIR(instructionIndex);
+      Phi phi =
+          strategy.getPhiDefinitionForInstructionIndex(
+              valueIndex,
+              blockIndex -> getBasicBlockOrEnsureCurrentBlock(blockIndex, instructionIndex),
+              type,
+              code::getDebugLocalInfo,
+              code.getStrategyInfo());
+      ensureCurrentPosition();
+      ++nextInstructionIndex;
+      return phi;
+    }
+
+    private BasicBlock getBasicBlockOrEnsureCurrentBlock(int index, int currentInstructionIndex) {
+      // If the index is at current or past it ensure the block.
+      if (index >= currentInstructionIndex) {
+        ensureCurrentBlock();
+        return currentBlock;
+      }
+      // Otherwise we assume the index is an exact block index for an existing block.
+      assert blocks.containsKey(index);
+      return getBasicBlock(index);
     }
 
     private void advanceInstructionState() {
@@ -260,8 +291,9 @@
       // Arguments are not included in the "instructions" so this does not call "addInstruction"
       // which would otherwise advance the state.
       TypeElement typeElement = type.toTypeElement(appView);
-      DebugLocalInfo localInfo = code.getDebugLocalInfo(index);
-      Value dest = strategy.getValueDefinitionForInstructionIndex(index, typeElement, localInfo);
+      Value dest =
+          strategy.getValueDefinitionForInstructionIndex(
+              index, typeElement, code::getDebugLocalInfo);
       Argument argument = new Argument(dest, index, type.isBooleanType());
       assert currentBlock != null;
       assert currentPosition.isSyntheticPosition();
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 b2fe3ba..fd4113a 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirBuilder.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirBuilder.java
@@ -433,6 +433,6 @@
         instructionCount,
         new TryCatchTable(tryCatchRanges),
         debugTable,
-        strategy.getSsaValueStrategy());
+        strategy.getStrategyInfo());
   }
 }
diff --git a/src/main/java/com/android/tools/r8/lightir/LirCode.java b/src/main/java/com/android/tools/r8/lightir/LirCode.java
index f62d9f6..3f84cdf 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirCode.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirCode.java
@@ -12,6 +12,7 @@
 import com.android.tools.r8.ir.code.Position;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
 import java.util.Map;
+import java.util.function.BiConsumer;
 
 public class LirCode<EV> implements Iterable<LirInstructionView> {
 
@@ -48,9 +49,13 @@
       this.valueToLocalMap = valueToLocalMap;
       this.instructionToEndUseMap = instructionToEndUseMap;
     }
+
+    public void forEachLocalDefinition(BiConsumer<EV, DebugLocalInfo> fn) {
+      valueToLocalMap.forEach(fn);
+    }
   }
 
-  private final LirSsaValueStrategy<EV> ssaValueStrategy;
+  private final LirStrategyInfo<EV> strategyInfo;
 
   private final IRMetadata metadata;
 
@@ -89,7 +94,7 @@
       int instructionCount,
       TryCatchTable tryCatchTable,
       DebugLocalInfoTable<EV> debugLocalInfoTable,
-      LirSsaValueStrategy<EV> ssaValueStrategy) {
+      LirStrategyInfo<EV> strategyInfo) {
     this.metadata = metadata;
     this.constants = constants;
     this.positionTable = positions;
@@ -98,11 +103,17 @@
     this.instructionCount = instructionCount;
     this.tryCatchTable = tryCatchTable;
     this.debugLocalInfoTable = debugLocalInfoTable;
-    this.ssaValueStrategy = ssaValueStrategy;
+    this.strategyInfo = strategyInfo;
   }
 
   public EV decodeValueIndex(int encodedValueIndex, int currentValueIndex) {
-    return ssaValueStrategy.decodeValueIndex(encodedValueIndex, currentValueIndex);
+    return strategyInfo
+        .getReferenceStrategy()
+        .decodeValueIndex(encodedValueIndex, currentValueIndex);
+  }
+
+  public LirStrategyInfo<EV> getStrategyInfo() {
+    return strategyInfo;
   }
 
   public int getArgumentCount() {
@@ -137,7 +148,7 @@
     return debugLocalInfoTable;
   }
 
-  public DebugLocalInfo getDebugLocalInfo(int valueIndex) {
+  public DebugLocalInfo getDebugLocalInfo(EV valueIndex) {
     return debugLocalInfoTable == null ? null : debugLocalInfoTable.valueToLocalMap.get(valueIndex);
   }
 
diff --git a/src/main/java/com/android/tools/r8/lightir/LirDecodingStrategy.java b/src/main/java/com/android/tools/r8/lightir/LirDecodingStrategy.java
index 1e25e3c..0b0a51d 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirDecodingStrategy.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirDecodingStrategy.java
@@ -7,15 +7,21 @@
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.Phi;
+import java.util.function.Function;
+import java.util.function.IntFunction;
 
 /** Abstraction for how to decode SSA values (and basic blocks) when reading LIR. */
 public abstract class LirDecodingStrategy<V, EV> {
 
-  public abstract V getValue(EV encodedValue);
+  public abstract V getValue(EV encodedValue, LirStrategyInfo<EV> strategyInfo);
 
   public abstract V getValueDefinitionForInstructionIndex(
-      int instructionIndex, TypeElement type, DebugLocalInfo localInfo);
+      int instructionIndex, TypeElement type, Function<EV, DebugLocalInfo> getLocalInfo);
 
   public abstract Phi getPhiDefinitionForInstructionIndex(
-      int instructionIndex, BasicBlock block, TypeElement type, DebugLocalInfo localInfo);
+      int valueIndex,
+      IntFunction<BasicBlock> getBlock,
+      TypeElement type,
+      Function<EV, DebugLocalInfo> getLocalInfo,
+      LirStrategyInfo<EV> strategyInfo);
 }
diff --git a/src/main/java/com/android/tools/r8/lightir/LirEncodingStrategy.java b/src/main/java/com/android/tools/r8/lightir/LirEncodingStrategy.java
index 8bbba90..eb814d1 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirEncodingStrategy.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirEncodingStrategy.java
@@ -14,13 +14,17 @@
 
   public abstract EV defineValue(V value, int index);
 
+  public abstract boolean isPhiInlineInstruction();
+
   public abstract boolean verifyValueIndex(V value, int expectedIndex);
 
   public abstract EV getEncodedValue(V value);
 
   public int getEncodedValueIndexForReference(EV encodedValue, int referencingValueIndex) {
-    return getSsaValueStrategy().encodeValueIndex(encodedValue, referencingValueIndex);
+    return getStrategyInfo()
+        .getReferenceStrategy()
+        .encodeValueIndex(encodedValue, referencingValueIndex);
   }
 
-  public abstract LirSsaValueStrategy<EV> getSsaValueStrategy();
+  public abstract LirStrategyInfo<EV> getStrategyInfo();
 }
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 20dfc30..f4a840b 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirPrinter.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirPrinter.java
@@ -53,7 +53,7 @@
   }
 
   private String fmtValueIndex(EV valueIndex) {
-    return "v" + valueIndex;
+    return valueIndex.toString();
   }
 
   private String fmtInsnIndex(int instructionIndex) {
diff --git a/src/main/java/com/android/tools/r8/lightir/LirStrategy.java b/src/main/java/com/android/tools/r8/lightir/LirStrategy.java
index 70cda39..108586f 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirStrategy.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirStrategy.java
@@ -3,6 +3,8 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.lightir;
 
+import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DebugLocalInfo;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.code.BasicBlock;
@@ -11,6 +13,11 @@
 import com.android.tools.r8.ir.code.Value;
 import it.unimi.dsi.fastutil.objects.Reference2IntMap;
 import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
+import java.util.ArrayList;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.function.IntFunction;
 
 /**
  * Abstraction for encoding and decoding LIR values.
@@ -26,114 +33,326 @@
 
   public abstract LirDecodingStrategy<V, EV> getDecodingStrategy(LirCode<EV> code);
 
-  // Strategy that implements the encoding of phi values as instructions in the LIR instruction
-  // stream.
-  public static class PhiInInstructionsStrategy extends LirStrategy<Value, Integer> {
+  /**
+   * Encoding of a value with a phi-bit.
+   *
+   * <p>Due to the generic signature the encoding is boxed so this just adds some convenient
+   * predicates and formatting since it is boxed anyway.
+   *
+   * <p>JVM code attribute has length u2 (16-bit / max 65536). Thus, the number of basic blocks and
+   * phi count is also bounded by the same value. The encoding here is taken to be
+   *
+   * <ul>
+   *   <li>1-bit for value/phi bit (sign bit / most significant bit),
+   *   <li>15-bit phi index (the following most significant bits).
+   *   <li>16-bit block index (the least significant bits).
+   * </ul>
+   *
+   * <p>TODO(b/225838009): Fix this encoding to support pathological block counts above 32k.
+   */
+  public static class PhiOrValue {
+    private final int value;
+
+    public static PhiOrValue forPhi(int blockIndex, int phiIndex) {
+      int sign = Integer.MIN_VALUE;
+      int block = ensure15bit(blockIndex) << 16;
+      int phi = ByteUtils.ensureU2(phiIndex);
+      int raw = sign | block | phi;
+      assert raw < 0;
+      return new PhiOrValue(raw);
+    }
+
+    private static int ensure15bit(int value) {
+      if (value >= (1 << 15)) {
+        // TODO(b/225838009): Support 16-bit values and inline this helper.
+        throw new Unimplemented("No support for more than 15-bit block index.");
+      }
+      return ByteUtils.ensureU2(value);
+    }
+
+    public static PhiOrValue forNonPhi(int index) {
+      assert index >= 0;
+      return new PhiOrValue(index);
+    }
+
+    private PhiOrValue(int value) {
+      this.value = value;
+    }
+
+    public boolean isPhi() {
+      return value < 0;
+    }
+
+    public boolean isNonPhi() {
+      return !isPhi();
+    }
+
+    public int getRawValue() {
+      return value;
+    }
+
+    public int getDecodedValue() {
+      assert isNonPhi();
+      return value;
+    }
+
+    public int getBlockIndex() {
+      assert isPhi();
+      return (value & ~Integer.MIN_VALUE) >> 16;
+    }
+
+    public int getPhiIndex() {
+      assert isPhi();
+      return value & 0xFFFF;
+    }
 
     @Override
-    public LirEncodingStrategy<Value, Integer> getEncodingStrategy() {
+    public String toString() {
+      if (isPhi()) {
+        return "phi(" + getBlockIndex() + "," + getPhiIndex() + ")";
+      }
+      return "v" + getDecodedValue();
+    }
+
+    @Override
+    public int hashCode() {
+      return value;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+      if (obj == null) {
+        return false;
+      }
+      if (obj == this) {
+        return true;
+      }
+      return obj instanceof PhiOrValue && (value == ((PhiOrValue) obj).value);
+    }
+  }
+
+  public static class ExternalPhisStrategy extends LirStrategy<Value, PhiOrValue> {
+
+    @Override
+    public LirEncodingStrategy<Value, PhiOrValue> getEncodingStrategy() {
       return new EncodingStrategy();
     }
 
     @Override
-    public LirDecodingStrategy<Value, Integer> getDecodingStrategy(LirCode<Integer> code) {
+    public LirDecodingStrategy<Value, PhiOrValue> getDecodingStrategy(LirCode<PhiOrValue> code) {
       return new DecodingStrategy(code);
     }
-  }
 
-  private static class EncodingStrategy extends LirEncodingStrategy<Value, Integer> {
+    private static class StrategyInfo extends LirStrategyInfo<PhiOrValue> {
+      private static final StrategyInfo EMPTY = new StrategyInfo(new int[0]);
 
-    // EV == Integer and its definition is equal to its shifted instruction index.
-    // The conversion for EV to its int-valued reference is determined by the 'valueStrategy'.
+      private final int[] phiTable;
 
-    private final LirSsaValueStrategy<Integer> valueStrategy = LirSsaValueStrategy.get();
-    private final Reference2IntMap<Value> values = new Reference2IntOpenHashMap<>();
-    private final Reference2IntMap<BasicBlock> blocks = new Reference2IntOpenHashMap<>();
-
-    @Override
-    public void defineBlock(BasicBlock block, int index) {
-      assert !blocks.containsKey(block);
-      blocks.put(block, index);
-    }
-
-    @Override
-    public Integer defineValue(Value value, int index) {
-      values.put(value, index);
-      return index;
-    }
-
-    @Override
-    public boolean verifyValueIndex(Value value, int expectedIndex) {
-      assert expectedIndex == values.getInt(value);
-      return true;
-    }
-
-    @Override
-    public Integer getEncodedValue(Value value) {
-      return values.getInt(value);
-    }
-
-    @Override
-    public int getBlockIndex(BasicBlock block) {
-      assert blocks.containsKey(block);
-      return blocks.getInt(block);
-    }
-
-    @Override
-    public LirSsaValueStrategy<Integer> getSsaValueStrategy() {
-      return valueStrategy;
-    }
-  }
-
-  private static class DecodingStrategy extends LirDecodingStrategy<Value, Integer> {
-
-    private final Value[] values;
-
-    DecodingStrategy(LirCode<Integer> code) {
-      values = new Value[code.getArgumentCount() + code.getInstructionCount()];
-    }
-
-    @Override
-    public Value getValue(Integer encodedValue) {
-      int index = encodedValue;
-      Value value = values[index];
-      if (value == null) {
-        value = new Value(index, TypeElement.getBottom(), null);
-        values[index] = value;
+      public StrategyInfo(int[] phiTable) {
+        this.phiTable = phiTable;
       }
-      return value;
+
+      @Override
+      public LirSsaValueStrategy<PhiOrValue> getReferenceStrategy() {
+        return ReferenceStrategy.INSTANCE;
+      }
     }
 
-    @Override
-    public Value getValueDefinitionForInstructionIndex(
-        int index, TypeElement type, DebugLocalInfo localInfo) {
-      Value value = values[index];
-      if (value == null) {
-        value = new Value(index, type, localInfo);
-        values[index] = value;
-      } else {
-        value.setType(type);
-        if (localInfo != null) {
-          value.setLocalInfo(localInfo);
+    private static class EncodingStrategy extends LirEncodingStrategy<Value, PhiOrValue> {
+      private final Map<Value, PhiOrValue> values = new IdentityHashMap<>();
+      private final Reference2IntMap<BasicBlock> blocks = new Reference2IntOpenHashMap<>();
+      private final ArrayList<Integer> phiTable = new ArrayList<>();
+
+      @Override
+      public boolean isPhiInlineInstruction() {
+        return false;
+      }
+
+      @Override
+      public void defineBlock(BasicBlock block, int index) {
+        assert !blocks.containsKey(block);
+        blocks.put(block, index);
+        if (block.getPhis().isEmpty()) {
+          return;
         }
+        int i = 0;
+        for (Phi phi : block.getPhis()) {
+          values.put(phi, PhiOrValue.forPhi(index, i++));
+        }
+        // Amend the phi table with the index of the basic block and the number of its phis.
+        phiTable.add(index);
+        phiTable.add(i);
       }
-      return value;
+
+      @Override
+      public PhiOrValue defineValue(Value value, int index) {
+        if (value.isPhi()) {
+          // Phis are defined as part of blocks.
+          PhiOrValue encodedValue = values.get(value);
+          assert encodedValue != null;
+          return encodedValue;
+        }
+        PhiOrValue encodedValue = PhiOrValue.forNonPhi(index);
+        values.put(value, encodedValue);
+        return encodedValue;
+      }
+
+      @Override
+      public boolean verifyValueIndex(Value value, int expectedIndex) {
+        PhiOrValue encodedValue = values.get(value);
+        assert encodedValue.isNonPhi();
+        assert expectedIndex == encodedValue.getDecodedValue();
+        return true;
+      }
+
+      @Override
+      public PhiOrValue getEncodedValue(Value value) {
+        return values.get(value);
+      }
+
+      @Override
+      public int getBlockIndex(BasicBlock block) {
+        assert blocks.containsKey(block);
+        return blocks.getInt(block);
+      }
+
+      @Override
+      public LirStrategyInfo<PhiOrValue> getStrategyInfo() {
+        if (phiTable.isEmpty()) {
+          return StrategyInfo.EMPTY;
+        }
+        int[] array = new int[phiTable.size()];
+        for (int i = 0; i < phiTable.size(); i++) {
+          array[i] = phiTable.get(i);
+        }
+        return new StrategyInfo(array);
+      }
     }
 
-    @Override
-    public Phi getPhiDefinitionForInstructionIndex(
-        int index, BasicBlock block, TypeElement type, DebugLocalInfo localInfo) {
-      Phi phi = new Phi(index, block, type, localInfo, RegisterReadType.NORMAL);
-      Value value = values[index];
-      if (value != null) {
-        // A fake ssa value has already been created, replace the users by the actual phi.
-        // TODO(b/225838009): We could consider encoding the value type as a bit in the value index
-        //  and avoid the overhead of replacing users at phi-definition time.
-        assert !value.isPhi();
-        value.replaceUsers(phi);
+    private static class DecodingStrategy extends LirDecodingStrategy<Value, PhiOrValue> {
+
+      private final Value[] values;
+      private final int firstPhiValueIndex;
+
+      DecodingStrategy(LirCode<PhiOrValue> code) {
+        values = new Value[code.getArgumentCount() + code.getInstructionCount()];
+        int phiValueIndex = -1;
+        for (LirInstructionView view : code) {
+          if (view.getOpcode() == LirOpcodes.PHI) {
+            phiValueIndex = code.getArgumentCount() + view.getInstructionIndex();
+            break;
+          }
+        }
+        this.firstPhiValueIndex = phiValueIndex;
       }
-      values[index] = phi;
-      return phi;
+
+      private int decode(PhiOrValue encodedValue, LirStrategyInfo<PhiOrValue> strategyInfo) {
+        if (encodedValue.isNonPhi()) {
+          return encodedValue.getDecodedValue();
+        }
+        StrategyInfo info = (StrategyInfo) strategyInfo;
+        int phiBlock = encodedValue.getBlockIndex();
+        int phiIndex = encodedValue.getPhiIndex();
+        assert firstPhiValueIndex != -1;
+        int index = firstPhiValueIndex;
+        for (int i = 0; i < info.phiTable.length; i++) {
+          int blockIndex = info.phiTable[i];
+          if (blockIndex == phiBlock) {
+            return index + phiIndex;
+          }
+          index += info.phiTable[++i];
+        }
+        throw new Unreachable("Unexpectedly fell off the end of the phi table");
+      }
+
+      @Override
+      public Value getValue(PhiOrValue encodedValue, LirStrategyInfo<PhiOrValue> strategyInfo) {
+        int index = decode(encodedValue, strategyInfo);
+        Value value = values[index];
+        if (value == null) {
+          value = new Value(index, TypeElement.getBottom(), null);
+          values[index] = value;
+        }
+        return value;
+      }
+
+      @Override
+      public Value getValueDefinitionForInstructionIndex(
+          int index, TypeElement type, Function<PhiOrValue, DebugLocalInfo> getLocalInfo) {
+        PhiOrValue encodedValue = new PhiOrValue(index);
+        assert encodedValue.isNonPhi();
+        DebugLocalInfo localInfo = getLocalInfo.apply(encodedValue);
+        Value value = values[index];
+        if (value == null) {
+          value = new Value(index, type, localInfo);
+          values[index] = value;
+        } else {
+          value.setType(type);
+          if (localInfo != null && !value.hasLocalInfo()) {
+            value.setLocalInfo(localInfo);
+          }
+          assert localInfo == value.getLocalInfo();
+        }
+        return value;
+      }
+
+      @Override
+      public Phi getPhiDefinitionForInstructionIndex(
+          int valueIndex,
+          IntFunction<BasicBlock> getBlock,
+          TypeElement type,
+          Function<PhiOrValue, DebugLocalInfo> getLocalInfo,
+          LirStrategyInfo<PhiOrValue> strategyInfo) {
+        PhiOrValue encodedValue = getEncodedPhiForAbsoluteValueIndex(valueIndex, strategyInfo);
+        BasicBlock block = getBlock.apply(encodedValue.getBlockIndex());
+        DebugLocalInfo localInfo = getLocalInfo.apply(encodedValue);
+        Phi phi = new Phi(valueIndex, block, type, localInfo, RegisterReadType.NORMAL);
+        Value value = values[valueIndex];
+        if (value != null) {
+          // A fake ssa value has already been created, replace the users by the actual phi.
+          // TODO(b/225838009): We could consider encoding the value phi-bit in the value index
+          //  and avoid the overhead of replacing users at phi-definition time.
+          assert !value.isPhi();
+          value.replaceUsers(phi);
+        }
+        values[valueIndex] = phi;
+        return phi;
+      }
+
+      private PhiOrValue getEncodedPhiForAbsoluteValueIndex(
+          int phiValueIndex, LirStrategyInfo<PhiOrValue> strategyInfo) {
+        StrategyInfo info = (StrategyInfo) strategyInfo;
+        int currentPhiValueIndex = firstPhiValueIndex;
+        for (int i = 0; i < info.phiTable.length; i += 2) {
+          assert currentPhiValueIndex <= phiValueIndex;
+          int blockIndex = info.phiTable[i];
+          int phiCount = info.phiTable[i + 1];
+          assert phiCount > 0;
+          if (phiValueIndex < currentPhiValueIndex + phiCount) {
+            int phiOffsetInBlock = phiValueIndex - currentPhiValueIndex;
+            return PhiOrValue.forPhi(blockIndex, phiOffsetInBlock);
+          }
+          currentPhiValueIndex += phiCount;
+        }
+        throw new Unreachable("Unexpected fall off the end of the phi table");
+      }
+    }
+
+    // TODO(b/225838009): Consider still encoding the local value refs as small relative indexes.
+    private static class ReferenceStrategy extends LirSsaValueStrategy<PhiOrValue> {
+
+      private static final ReferenceStrategy INSTANCE = new ReferenceStrategy();
+
+      @Override
+      public int encodeValueIndex(PhiOrValue value, int currentValueIndex) {
+        return value.getRawValue();
+      }
+
+      @Override
+      public PhiOrValue decodeValueIndex(int encodedValueIndex, int currentValueIndex) {
+        return new PhiOrValue(encodedValueIndex);
+      }
     }
   }
+
 }
diff --git a/src/main/java/com/android/tools/r8/lightir/LirStrategyInfo.java b/src/main/java/com/android/tools/r8/lightir/LirStrategyInfo.java
new file mode 100644
index 0000000..088786c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/lightir/LirStrategyInfo.java
@@ -0,0 +1,9 @@
+// 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.lightir;
+
+public abstract class LirStrategyInfo<EV> {
+
+  public abstract LirSsaValueStrategy<EV> getReferenceStrategy();
+}
diff --git a/src/main/java/com/android/tools/r8/lightir/PhiInInstructionsStrategy.java b/src/main/java/com/android/tools/r8/lightir/PhiInInstructionsStrategy.java
new file mode 100644
index 0000000..0b0d537
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/lightir/PhiInInstructionsStrategy.java
@@ -0,0 +1,145 @@
+// 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.lightir;
+
+import com.android.tools.r8.graph.DebugLocalInfo;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.Phi;
+import com.android.tools.r8.ir.code.Phi.RegisterReadType;
+import com.android.tools.r8.ir.code.Value;
+import it.unimi.dsi.fastutil.objects.Reference2IntMap;
+import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
+import java.util.function.Function;
+import java.util.function.IntFunction;
+
+/** Strategy encoding phi values as instructions in the LIR instruction stream. */
+public class PhiInInstructionsStrategy extends LirStrategy<Value, Integer> {
+
+  @Override
+  public LirEncodingStrategy<Value, Integer> getEncodingStrategy() {
+    return new EncodingStrategy();
+  }
+
+  @Override
+  public LirDecodingStrategy<Value, Integer> getDecodingStrategy(LirCode<Integer> code) {
+    return new DecodingStrategy(code);
+  }
+
+  private static class EncodingStrategy extends LirEncodingStrategy<Value, Integer> {
+
+    // EV == Integer and its definition is equal to its shifted instruction index.
+    // The conversion for EV to its int-valued reference is determined by the 'valueStrategy'.
+
+    private final LirSsaValueStrategy<Integer> referenceStrategy = LirSsaValueStrategy.get();
+    private final Reference2IntMap<Value> values = new Reference2IntOpenHashMap<>();
+    private final Reference2IntMap<BasicBlock> blocks = new Reference2IntOpenHashMap<>();
+
+    @Override
+    public boolean isPhiInlineInstruction() {
+      return true;
+    }
+
+    @Override
+    public void defineBlock(BasicBlock block, int index) {
+      assert !blocks.containsKey(block);
+      blocks.put(block, index);
+    }
+
+    @Override
+    public Integer defineValue(Value value, int index) {
+      values.put(value, index);
+      return index;
+    }
+
+    @Override
+    public boolean verifyValueIndex(Value value, int expectedIndex) {
+      assert expectedIndex == values.getInt(value);
+      return true;
+    }
+
+    @Override
+    public Integer getEncodedValue(Value value) {
+      return values.getInt(value);
+    }
+
+    @Override
+    public int getBlockIndex(BasicBlock block) {
+      assert blocks.containsKey(block);
+      return blocks.getInt(block);
+    }
+
+    @Override
+    public LirStrategyInfo<Integer> getStrategyInfo() {
+      return new LirStrategyInfo<Integer>() {
+        @Override
+        public LirSsaValueStrategy<Integer> getReferenceStrategy() {
+          return referenceStrategy;
+        }
+      };
+    }
+  }
+
+  private static class DecodingStrategy extends LirDecodingStrategy<Value, Integer> {
+
+    private final Value[] values;
+
+    DecodingStrategy(LirCode<Integer> code) {
+      values = new Value[code.getArgumentCount() + code.getInstructionCount()];
+    }
+
+    @Override
+    public Value getValue(Integer encodedValue, LirStrategyInfo<Integer> strategyInfo) {
+      int index = encodedValue;
+      Value value = values[index];
+      if (value == null) {
+        value = new Value(index, TypeElement.getBottom(), null);
+        values[index] = value;
+      }
+      return value;
+    }
+
+    @Override
+    public Value getValueDefinitionForInstructionIndex(
+        int index, TypeElement type, Function<Integer, DebugLocalInfo> getLocalInfo) {
+      DebugLocalInfo localInfo = getLocalInfo.apply(index);
+      Value value = values[index];
+      if (value == null) {
+        value = new Value(index, type, localInfo);
+        values[index] = value;
+      } else {
+        value.setType(type);
+        if (localInfo != null) {
+          if (!value.hasLocalInfo()) {
+            value.setLocalInfo(localInfo);
+          }
+          assert localInfo == value.getLocalInfo();
+        }
+      }
+      return value;
+    }
+
+    @Override
+    public Phi getPhiDefinitionForInstructionIndex(
+        int valueIndex,
+        IntFunction<BasicBlock> getBlock,
+        TypeElement type,
+        Function<Integer, DebugLocalInfo> getLocalInfo,
+        LirStrategyInfo<Integer> strategyInfo) {
+      BasicBlock block = getBlock.apply(valueIndex);
+      DebugLocalInfo localInfo = getLocalInfo.apply(valueIndex);
+      Phi phi = new Phi(valueIndex, block, type, localInfo, RegisterReadType.NORMAL);
+      Value value = values[valueIndex];
+      if (value != null) {
+        // A fake ssa value has already been created, replace the users by the actual phi.
+        // TODO(b/225838009): We could consider encoding the value type as a bit in the value index
+        //  and avoid the overhead of replacing users at phi-definition time.
+        assert !value.isPhi();
+        value.replaceUsers(phi);
+      }
+      values[valueIndex] = phi;
+      return phi;
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java b/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
index 15efe36..2a1408a 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
@@ -139,6 +139,10 @@
 
   /** List of MappedRanges that belong to the same renamed name. */
   public static class MappedRangesOfName {
+
+    private static final MappedRangesOfName EMPTY_INSTANCE =
+        new MappedRangesOfName(Collections.emptyList());
+
     private final List<MappedRange> mappedRanges;
 
     public MappedRangesOfName(List<MappedRange> mappedRanges) {
@@ -211,6 +215,10 @@
       return mappedRanges;
     }
 
+    public static MappedRangesOfName empty() {
+      return EMPTY_INSTANCE;
+    }
+
     @Override
     public boolean equals(Object o) {
       if (this == o) {
@@ -562,7 +570,7 @@
      */
     private List<MappingInformation> additionalMappingInformation = EMPTY_MAPPING_INFORMATION;
 
-    MappedRange(
+    public MappedRange(
         Range minifiedRange, MethodSignature signature, Range originalRange, String renamedName) {
       this.minifiedRange = minifiedRange;
       this.signature = signature;
diff --git a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringUtils.java b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringUtils.java
index 6e14907..3b022c7 100644
--- a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringUtils.java
+++ b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringUtils.java
@@ -34,6 +34,7 @@
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.naming.identifiernamestring.IdentifierNameStringLookupResult;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.StringUtils;
 import com.google.common.collect.Sets;
 import java.util.ArrayList;
 import java.util.List;
@@ -340,15 +341,15 @@
     String identifier = dexString.toString();
     String typeIdentifier = null;
     String memberIdentifier = null;
-    String[] items = identifier.split("#");
+    List<String> items = StringUtils.split(identifier, '#');
     // "x#y#z"
-    if (items.length > 2) {
+    if (items.size() > 2) {
       return null;
     }
     // "fully.qualified.ClassName#fieldOrMethodName"
-    if (items.length == 2) {
-      typeIdentifier = items[0];
-      memberIdentifier = items[1];
+    if (items.size() == 2) {
+      typeIdentifier = items.get(0);
+      memberIdentifier = items.get(1);
     } else {
       int lastDot = identifier.lastIndexOf(".");
       // "fully.qualified.ClassName.fieldOrMethodName"
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorIROptimizer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorIROptimizer.java
index f92af83..c76e842 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorIROptimizer.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorIROptimizer.java
@@ -33,6 +33,8 @@
    * {@link Assume} instructions when non-trivial information is known about non-constant arguments
    * such as their nullability, dynamic type, interval, etc.
    */
+  // TODO(b/270398965): Replace LinkedList.
+  @SuppressWarnings("JdkObsolete")
   public static void optimize(
       AppView<AppInfoWithLiveness> appView,
       IRCode code,
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/MemberNamingWithMappedRangesOfName.java b/src/main/java/com/android/tools/r8/retrace/internal/MemberNamingWithMappedRangesOfName.java
index 26d5a16..d60e6bf 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/MemberNamingWithMappedRangesOfName.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/MemberNamingWithMappedRangesOfName.java
@@ -34,7 +34,20 @@
     return mappedRangesOfName.getMappedRanges();
   }
 
+  List<MappedRange> getMappedRangesWithNoMinifiedRangeAndPositionZero() {
+    return mappedRangesOfName.allRangesForLine(0, true);
+  }
+
   public MemberNaming getMemberNaming() {
     return methodMemberNaming;
   }
+
+  public boolean isSingleCatchAllRange() {
+    if (getMappedRanges().size() == 1) {
+      MappedRange singleMappedRange = ListUtils.first(getMappedRanges());
+      return singleMappedRange.minifiedRange != null
+          && singleMappedRange.minifiedRange.isCatchAll();
+    }
+    return false;
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceFrameResultImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceFrameResultImpl.java
index b48e8c2..f82327a 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetraceFrameResultImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceFrameResultImpl.java
@@ -90,6 +90,7 @@
               RetraceClassElementImpl classElement = mappedRangeData.getRetraceClassElement();
               List<MemberNamingWithMappedRangesOfName> memberNamingWithMappedRangesOfNames =
                   mappedRangeData.getMemberNamingWithMappedRanges();
+              OptionalInt position = mappedRangeData.getPosition();
               if (memberNamingWithMappedRangesOfNames == null
                   || memberNamingWithMappedRangesOfNames.isEmpty()) {
                 return Stream.of(
@@ -101,7 +102,7 @@
                                 classElement.getRetracedClass().getClassReference())),
                         ImmutableList.of(),
                         Optional.empty(),
-                        mappedRangeData.getPosition(),
+                        position,
                         retracer));
               }
               // Iterate over mapped ranges that may have different positions than specified.
@@ -110,6 +111,23 @@
                   memberNamingWithMappedRangesOfNames) {
                 List<MappedRange> mappedRangesForMemberNaming =
                     memberNamingWithMappedRangesOfName.getMappedRanges();
+                MemberNaming memberNaming = memberNamingWithMappedRangesOfName.getMemberNaming();
+                if (mappedRangesForMemberNaming.isEmpty()) {
+                  assert memberNaming != null;
+                  MappedRange mappedRange =
+                      new MappedRange(
+                          null,
+                          memberNaming.getOriginalSignature().asMethodSignature(),
+                          null,
+                          memberNaming.getRenamedName());
+                  ambiguousFrames.add(
+                      elementFromMappedRanges(
+                          Collections.singletonList(MappedRangeForFrame.create(mappedRange)),
+                          Optional.of(memberNaming),
+                          classElement,
+                          position));
+                  continue;
+                }
                 MappedRange firstMappedRange = mappedRangesForMemberNaming.get(0);
                 Range minifiedRange = firstMappedRange.minifiedRange;
                 List<MappedRange> mappedRangesForElement = Lists.newArrayList(firstMappedRange);
@@ -119,10 +137,10 @@
                     // This is a new frame
                     separateAmbiguousOriginalPositions(
                         classElement,
-                        Optional.ofNullable(memberNamingWithMappedRangesOfName.getMemberNaming()),
+                        Optional.ofNullable(memberNaming),
                         mappedRangesForElement,
                         ambiguousFrames,
-                        mappedRangeData.getPosition());
+                        position);
                     mappedRangesForElement = new ArrayList<>();
                     minifiedRange = mappedRange.minifiedRange;
                   }
@@ -130,10 +148,10 @@
                 }
                 separateAmbiguousOriginalPositions(
                     classElement,
-                    Optional.ofNullable(memberNamingWithMappedRangesOfName.getMemberNaming()),
+                    Optional.ofNullable(memberNaming),
                     mappedRangesForElement,
                     ambiguousFrames,
-                    mappedRangeData.getPosition());
+                    position);
               }
               return ambiguousFrames.stream();
             });
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceMethodResultImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceMethodResultImpl.java
index 86af9c0..3a95c5f 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetraceMethodResultImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceMethodResultImpl.java
@@ -94,7 +94,7 @@
     if (context instanceof RetraceStackTraceContextImpl) {
       stackTraceContext = (RetraceStackTraceContextImpl) context;
     }
-    boolean hasPosition = position.isPresent() && position.getAsInt() >= 0;
+    boolean hasPosition = position.isPresent() && position.getAsInt() > 0;
     Function<MemberNamingWithMappedRangesOfName, List<MappedRange>> selector =
         hasPosition ? filterOnExistingPosition(position.getAsInt()) : filterOnNoPosition();
     for (Pair<RetraceClassElementImpl, List<MemberNamingWithMappedRangesOfName>> mappedRange :
@@ -114,9 +114,27 @@
       }
     }
     if (narrowedRanges.isEmpty()) {
+      boolean preamblePosition = position.isEmpty() || position.getAsInt() <= 0;
       for (Pair<RetraceClassElementImpl, List<MemberNamingWithMappedRangesOfName>> mappedRange :
           mappedRanges) {
-        narrowedRanges.add(new RetraceFrameResultData(mappedRange.getFirst(), null, position));
+        List<MemberNamingWithMappedRangesOfName> memberNamingWithMappedRanges = new ArrayList<>();
+        // If we could find a result, and we have observed a reported preamble position, we create a
+        // mapping containing only the member-naming.
+        if (mappedRange.getSecond() != null && preamblePosition) {
+          memberNamingWithMappedRanges =
+              ListUtils.map(
+                  mappedRange.getSecond(),
+                  m ->
+                      // Check if we have a catch-all range since that could map 0 to a non-zero
+                      // original line.
+                      m.isSingleCatchAllRange()
+                          ? m
+                          : new MemberNamingWithMappedRangesOfName(
+                              m.getMemberNaming(), MappedRangesOfName.empty()));
+        }
+        narrowedRanges.add(
+            new RetraceFrameResultData(
+                mappedRange.getFirst(), memberNamingWithMappedRanges, position));
       }
     }
     return new RetraceFrameResultImpl(
@@ -186,7 +204,7 @@
   }
 
   private Function<MemberNamingWithMappedRangesOfName, List<MappedRange>> filterOnNoPosition() {
-    return MemberNamingWithMappedRangesOfName::getMappedRanges;
+    return MemberNamingWithMappedRangesOfName::getMappedRangesWithNoMinifiedRangeAndPositionZero;
   }
 
   @Override
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 b46adc0..a30bcf4 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -3833,6 +3833,8 @@
     private Map<DexMethod, MethodProcessingContext> methodProcessingContexts =
         new ConcurrentHashMap<>();
 
+    // TODO(b/270398965): Replace LinkedList.
+    @SuppressWarnings("JdkObsolete")
     private final List<ProgramMethod> desugaredMethods = new LinkedList<>();
 
     private final Map<DexMethod, ProgramMethod> liveMethods = new ConcurrentHashMap<>();
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardClassSpecification.java b/src/main/java/com/android/tools/r8/shaking/ProguardClassSpecification.java
index e13dc82..1ce8f04 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardClassSpecification.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardClassSpecification.java
@@ -34,6 +34,8 @@
         ImmutableList.builder();
     protected ProguardTypeMatcher inheritanceClassName;
     protected boolean inheritanceIsExtends = false;
+    // TODO(b/270398965): Replace LinkedList.
+    @SuppressWarnings("JdkObsolete")
     protected List<ProguardMemberRule> memberRules = new LinkedList<>();
 
     protected Builder() {
@@ -348,10 +350,11 @@
 
   protected StringBuilder append(StringBuilder builder) {
     appendAnnotations(classAnnotations, builder);
-    boolean needsSpaceBeforeClassType =
-        StringUtils.appendNonEmpty(builder, null, classAccessFlags, null)
-            | StringUtils.appendNonEmpty(
-                builder, "!", negatedClassAccessFlags.toString().replace(" ", " !"), null);
+    boolean hasAccessFlags = StringUtils.appendNonEmpty(builder, null, classAccessFlags, null);
+    boolean hasNegatedAccessFlags =
+        StringUtils.appendNonEmpty(
+            builder, "!", negatedClassAccessFlags.toString().replace(" ", " !"), null);
+    boolean needsSpaceBeforeClassType = hasAccessFlags || hasNegatedAccessFlags;
     if (needsSpaceBeforeClassType) {
       builder.append(' ');
     }
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
index 907ed11..352facd 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
@@ -95,7 +95,6 @@
 import java.util.Map.Entry;
 import java.util.Queue;
 import java.util.Set;
-import java.util.Stack;
 import java.util.TreeSet;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentLinkedQueue;
@@ -512,7 +511,7 @@
         ProguardIfRule ifRule) {
       Set<Wrapper<DexMethod>> methodsMarked =
           options.forceProguardCompatibility ? null : new HashSet<>();
-      Stack<DexClass> worklist = new Stack<>();
+      Deque<DexClass> worklist = new ArrayDeque<>();
       worklist.add(clazz);
       while (!worklist.isEmpty()) {
         DexClass currentClass = worklist.pop();
diff --git a/src/main/java/com/android/tools/r8/shaking/WhyAreYouKeepingConsumer.java b/src/main/java/com/android/tools/r8/shaking/WhyAreYouKeepingConsumer.java
index d4f127a..483db26 100644
--- a/src/main/java/com/android/tools/r8/shaking/WhyAreYouKeepingConsumer.java
+++ b/src/main/java/com/android/tools/r8/shaking/WhyAreYouKeepingConsumer.java
@@ -25,10 +25,10 @@
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.StringUtils.BraceType;
 import java.io.PrintStream;
+import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Deque;
 import java.util.IdentityHashMap;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -125,7 +125,7 @@
       return null;
     }
     Map<GraphNode, GraphNode> seen = new IdentityHashMap<>();
-    Deque<GraphPath> queue = new LinkedList<>();
+    Deque<GraphPath> queue = new ArrayDeque<>();
     GraphPath path = null;
     GraphNode current = node;
     while (true) {
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
index 44f53b1..efe5d62 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
@@ -66,9 +66,6 @@
 
 public class SyntheticFinalization {
 
-  // TODO(b/237413146): Implement a non-quadratic grouping algorithm.
-  private static final int GROUP_COUNT_THRESHOLD = 10;
-
   public static class Result {
     public final CommittedItems commit;
     public final NonIdentityGraphLens lens;
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
index 49746f4..3bbb9ae 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -6,6 +6,8 @@
 import static com.android.tools.r8.utils.ConsumerUtils.emptyConsumer;
 
 import com.android.tools.r8.FeatureSplit;
+import com.android.tools.r8.SyntheticInfoConsumer;
+import com.android.tools.r8.SyntheticInfoConsumerData;
 import com.android.tools.r8.contexts.CompilationContext.UniqueContext;
 import com.android.tools.r8.errors.MissingGlobalSyntheticsConsumerDiagnostic;
 import com.android.tools.r8.errors.Unreachable;
@@ -38,6 +40,8 @@
 import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.synthesis.SyntheticFinalization.Result;
 import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
 import com.android.tools.r8.utils.Box;
@@ -1169,4 +1173,40 @@
     return new SyntheticFinalization(appView.options(), this, committed)
         .computeFinalSynthetics(appView, timing);
   }
+
+  public void reportSyntheticsInformation(SyntheticInfoConsumer consumer) {
+    assert isFinalized();
+    Map<DexType, DexType> seen = new IdentityHashMap<>();
+    committed.forEachItem(
+        ref -> {
+          DexType holder = ref.getHolder();
+          DexType context = ref.getContext().getSynthesizingContextType();
+          DexType old = seen.put(holder, context);
+          assert old == null || old == context;
+          if (old == null) {
+            consumer.acceptSyntheticInfo(new SyntheticInfoConsumerDataImpl(holder, context));
+          }
+        });
+  }
+
+  private static class SyntheticInfoConsumerDataImpl implements SyntheticInfoConsumerData {
+
+    private final DexType holder;
+    private final DexType context;
+
+    public SyntheticInfoConsumerDataImpl(DexType holder, DexType context) {
+      this.holder = holder;
+      this.context = context;
+    }
+
+    @Override
+    public ClassReference getSyntheticClass() {
+      return Reference.classFromDescriptor(holder.toDescriptorString());
+    }
+
+    @Override
+    public ClassReference getSynthesizingContextClass() {
+      return Reference.classFromDescriptor(context.toDescriptorString());
+    }
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidAppConsumers.java b/src/main/java/com/android/tools/r8/utils/AndroidAppConsumers.java
index 1c01a7b..7a77e25 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidAppConsumers.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidAppConsumers.java
@@ -4,16 +4,14 @@
 package com.android.tools.r8.utils;
 
 import com.android.tools.r8.BaseCompilerCommand;
+import com.android.tools.r8.ByteDataView;
 import com.android.tools.r8.ClassFileConsumer;
-import com.android.tools.r8.ClassFileConsumerData;
 import com.android.tools.r8.DataDirectoryResource;
 import com.android.tools.r8.DataEntryResource;
 import com.android.tools.r8.DataResourceConsumer;
 import com.android.tools.r8.DexFilePerClassFileConsumer;
-import com.android.tools.r8.DexFilePerClassFileConsumerData;
 import com.android.tools.r8.DexIndexedConsumer;
 import com.android.tools.r8.DexIndexedConsumer.ForwardingConsumer;
-import com.android.tools.r8.DexIndexedConsumerData;
 import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.ProgramConsumer;
 import com.android.tools.r8.ResourceException;
@@ -105,9 +103,13 @@
               new Int2ReferenceAVLTreeMap<>();
 
           @Override
-          public void acceptDexIndexedFile(DexIndexedConsumerData data) {
-            super.acceptDexIndexedFile(data);
-            addDexFile(data.getFileIndex(), data.getByteDataCopy(), data.getClassDescriptors());
+          public void accept(
+              int fileIndex,
+              ByteDataView data,
+              Set<String> descriptors,
+              DiagnosticsHandler handler) {
+            super.accept(fileIndex, data, descriptors, handler);
+            addDexFile(fileIndex, data.copyByteData(), descriptors);
           }
 
           @Override
@@ -180,12 +182,13 @@
           private TreeMap<String, DescriptorsWithContents> files = new TreeMap<>();
 
           @Override
-          public void acceptDexFile(DexFilePerClassFileConsumerData data) {
-            super.acceptDexFile(data);
-            addDexFile(
-                data.getPrimaryClassDescriptor(),
-                data.getByteDataCopy(),
-                data.getClassDescriptors());
+          public void accept(
+              String primaryClassDescriptor,
+              ByteDataView data,
+              Set<String> descriptors,
+              DiagnosticsHandler handler) {
+            super.accept(primaryClassDescriptor, data, descriptors, handler);
+            addDexFile(primaryClassDescriptor, data.copyByteData(), descriptors);
           }
 
           synchronized void addDexFile(
@@ -255,9 +258,9 @@
           private List<DescriptorsWithContents> files = new ArrayList<>();
 
           @Override
-          public void acceptClassFile(ClassFileConsumerData data) {
-            super.acceptClassFile(data);
-            addClassFile(data.getByteDataCopy(), data.getClassDescriptor());
+          public void accept(ByteDataView data, String descriptor, DiagnosticsHandler handler) {
+            super.accept(data, descriptor, handler);
+            addClassFile(data.copyByteData(), descriptor);
           }
 
           synchronized void addClassFile(byte[] data, String descriptor) {
diff --git a/src/main/java/com/android/tools/r8/utils/ClassFileConsumerDataImpl.java b/src/main/java/com/android/tools/r8/utils/ClassFileConsumerDataImpl.java
deleted file mode 100644
index b375086..0000000
--- a/src/main/java/com/android/tools/r8/utils/ClassFileConsumerDataImpl.java
+++ /dev/null
@@ -1,43 +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.utils;
-
-import com.android.tools.r8.ByteDataView;
-import com.android.tools.r8.ClassFileConsumerData;
-import com.android.tools.r8.DiagnosticsHandler;
-
-/** Internal implementation of the consumer data. */
-public class ClassFileConsumerDataImpl implements ClassFileConsumerData {
-
-  private final ByteDataView data;
-  private final String descriptor;
-  private final DiagnosticsHandler handler;
-
-  public ClassFileConsumerDataImpl(
-      ByteDataView data, String descriptor, DiagnosticsHandler handler) {
-    this.data = data;
-    this.descriptor = descriptor;
-    this.handler = handler;
-  }
-
-  @Override
-  public ByteDataView getByteDataView() {
-    return data;
-  }
-
-  @Override
-  public byte[] getByteDataCopy() {
-    return data.copyByteData();
-  }
-
-  @Override
-  public String getClassDescriptor() {
-    return descriptor;
-  }
-
-  @Override
-  public DiagnosticsHandler getDiagnosticsHandler() {
-    return handler;
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/utils/DeterminismChecker.java b/src/main/java/com/android/tools/r8/utils/DeterminismChecker.java
index 2f95428..c1817a7 100644
--- a/src/main/java/com/android/tools/r8/utils/DeterminismChecker.java
+++ b/src/main/java/com/android/tools/r8/utils/DeterminismChecker.java
@@ -121,7 +121,7 @@
       return;
     }
     if (method.hasCode()) {
-      String[] lines = method.getCode().toString().split("\n");
+      List<String> lines = StringUtils.splitLines(method.getCode().toString());
       for (String line : lines) {
         if (!callback.onLine(line)) {
           return;
diff --git a/src/main/java/com/android/tools/r8/utils/DexFilePerClassFileConsumerDataImpl.java b/src/main/java/com/android/tools/r8/utils/DexFilePerClassFileConsumerDataImpl.java
index 9dc9e8b..e69de29 100644
--- a/src/main/java/com/android/tools/r8/utils/DexFilePerClassFileConsumerDataImpl.java
+++ b/src/main/java/com/android/tools/r8/utils/DexFilePerClassFileConsumerDataImpl.java
@@ -1,61 +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.utils;
-
-import com.android.tools.r8.ByteDataView;
-import com.android.tools.r8.DexFilePerClassFileConsumerData;
-import com.android.tools.r8.DiagnosticsHandler;
-import java.util.Set;
-
-public class DexFilePerClassFileConsumerDataImpl implements DexFilePerClassFileConsumerData {
-
-  private final String primaryClassDescriptor;
-  private final String synthesizingContextDescriptor;
-  private final ByteDataView data;
-  private final Set<String> classDescriptors;
-  private final DiagnosticsHandler handler;
-
-  public DexFilePerClassFileConsumerDataImpl(
-      String primaryClassDescriptor,
-      String synthesizingContextDescriptor,
-      ByteDataView data,
-      Set<String> classDescriptors,
-      DiagnosticsHandler handler) {
-    this.primaryClassDescriptor = primaryClassDescriptor;
-    this.synthesizingContextDescriptor = synthesizingContextDescriptor;
-    this.data = data;
-    this.classDescriptors = classDescriptors;
-    this.handler = handler;
-  }
-
-  @Override
-  public String getPrimaryClassDescriptor() {
-    return primaryClassDescriptor;
-  }
-
-  @Override
-  public String getSynthesizingContextForPrimaryClass() {
-    return synthesizingContextDescriptor;
-  }
-
-  @Override
-  public ByteDataView getByteDataView() {
-    return data;
-  }
-
-  @Override
-  public byte[] getByteDataCopy() {
-    return data.copyByteData();
-  }
-
-  @Override
-  public Set<String> getClassDescriptors() {
-    return classDescriptors;
-  }
-
-  @Override
-  public DiagnosticsHandler getDiagnosticsHandler() {
-    return handler;
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/utils/DexIndexedConsumerDataImpl.java b/src/main/java/com/android/tools/r8/utils/DexIndexedConsumerDataImpl.java
deleted file mode 100644
index 2de21e4..0000000
--- a/src/main/java/com/android/tools/r8/utils/DexIndexedConsumerDataImpl.java
+++ /dev/null
@@ -1,50 +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.utils;
-
-import com.android.tools.r8.ByteDataView;
-import com.android.tools.r8.DexIndexedConsumerData;
-import com.android.tools.r8.DiagnosticsHandler;
-import java.util.Set;
-
-public class DexIndexedConsumerDataImpl implements DexIndexedConsumerData {
-
-  private final int fileIndex;
-  private final ByteDataView data;
-  private final Set<String> classDescriptors;
-  private final DiagnosticsHandler handler;
-
-  public DexIndexedConsumerDataImpl(
-      int fileIndex, ByteDataView data, Set<String> classDescriptors, DiagnosticsHandler handler) {
-    this.fileIndex = fileIndex;
-    this.data = data;
-    this.classDescriptors = classDescriptors;
-    this.handler = handler;
-  }
-
-  @Override
-  public int getFileIndex() {
-    return fileIndex;
-  }
-
-  @Override
-  public ByteDataView getByteDataView() {
-    return data;
-  }
-
-  @Override
-  public byte[] getByteDataCopy() {
-    return data.copyByteData();
-  }
-
-  @Override
-  public Set<String> getClassDescriptors() {
-    return classDescriptors;
-  }
-
-  @Override
-  public DiagnosticsHandler getDiagnosticsHandler() {
-    return handler;
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/utils/InternalGlobalSyntheticsProgramConsumer.java b/src/main/java/com/android/tools/r8/utils/InternalGlobalSyntheticsProgramConsumer.java
index 6fcc519..1428459 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalGlobalSyntheticsProgramConsumer.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalGlobalSyntheticsProgramConsumer.java
@@ -9,7 +9,6 @@
 import com.android.tools.r8.ByteDataView;
 import com.android.tools.r8.ClassFileConsumer;
 import com.android.tools.r8.DexFilePerClassFileConsumer;
-import com.android.tools.r8.DexFilePerClassFileConsumerData;
 import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.GlobalSyntheticsConsumer;
 import com.android.tools.r8.ProgramConsumer;
@@ -103,8 +102,12 @@
     }
 
     @Override
-    public synchronized void acceptDexFile(DexFilePerClassFileConsumerData data) {
-      builder.addGlobalSynthetic(data.getPrimaryClassDescriptor(), data.getByteDataCopy());
+    public synchronized void accept(
+        String primaryClassDescriptor,
+        ByteDataView data,
+        Set<String> descriptors,
+        DiagnosticsHandler handler) {
+      builder.addGlobalSynthetic(primaryClassDescriptor, data.copyByteData());
     }
 
     @Override
@@ -146,8 +149,12 @@
     }
 
     @Override
-    public void acceptDexFile(DexFilePerClassFileConsumerData data) {
-      addGlobal(data.getPrimaryClassDescriptor(), data.getByteDataView());
+    public void accept(
+        String primaryClassDescriptor,
+        ByteDataView data,
+        Set<String> descriptors,
+        DiagnosticsHandler handler) {
+      addGlobal(primaryClassDescriptor, data);
     }
 
     @Override
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 b26527a..0d7a71c 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -18,6 +18,7 @@
 import com.android.tools.r8.ProgramConsumer;
 import com.android.tools.r8.SourceFileProvider;
 import com.android.tools.r8.StringConsumer;
+import com.android.tools.r8.SyntheticInfoConsumer;
 import com.android.tools.r8.Version;
 import com.android.tools.r8.androidapi.ComputedApiLevel;
 import com.android.tools.r8.cf.CfVersion;
@@ -181,6 +182,7 @@
   public ProgramClassConflictResolver programClassConflictResolver = null;
 
   private GlobalSyntheticsConsumer globalSyntheticsConsumer = null;
+  private SyntheticInfoConsumer syntheticInfoConsumer = null;
 
   public DataResourceConsumer dataResourceConsumer;
   public FeatureSplitConfiguration featureSplitConfiguration;
@@ -517,6 +519,14 @@
     this.globalSyntheticsConsumer = globalSyntheticsConsumer;
   }
 
+  public void setSyntheticInfoConsumer(SyntheticInfoConsumer syntheticInfoConsumer) {
+    this.syntheticInfoConsumer = syntheticInfoConsumer;
+  }
+
+  public SyntheticInfoConsumer getSyntheticInfoConsumer() {
+    return syntheticInfoConsumer;
+  }
+
   public boolean isDesugaredLibraryCompilation() {
     return machineDesugaredLibrarySpecification.isLibraryCompilation();
   }
@@ -938,9 +948,7 @@
     String property = System.getProperty("com.android.tools.r8.extensiveLoggingFilter");
     if (property != null) {
       ImmutableSet.Builder<String> builder = ImmutableSet.builder();
-      for (String method : property.split(";")) {
-        builder.add(method);
-      }
+      StringUtils.splitForEach(property, ';', builder::add);
       return builder.build();
     }
     return ImmutableSet.of();
@@ -951,9 +959,7 @@
         System.getProperty("com.android.tools.r8.extensiveInterfaceMethodMinifierLoggingFilter");
     if (property != null) {
       ImmutableSet.Builder<String> builder = ImmutableSet.builder();
-      for (String method : property.split(";")) {
-        builder.add(method);
-      }
+      StringUtils.splitForEach(property, ';', builder::add);
       return builder.build();
     }
     return ImmutableSet.of();
diff --git a/src/main/java/com/android/tools/r8/utils/ListUtils.java b/src/main/java/com/android/tools/r8/utils/ListUtils.java
index 55fe509..47549a6 100644
--- a/src/main/java/com/android/tools/r8/utils/ListUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ListUtils.java
@@ -218,12 +218,16 @@
     return builder.build();
   }
 
+  // TODO(b/270398965): Replace LinkedList.
+  @SuppressWarnings("JdkObsolete")
   public static <T> LinkedList<T> newLinkedList(T element) {
     LinkedList<T> list = new LinkedList<>();
     list.add(element);
     return list;
   }
 
+  // TODO(b/270398965): Replace LinkedList.
+  @SuppressWarnings("JdkObsolete")
   public static <T> LinkedList<T> newLinkedList(ForEachable<T> forEachable) {
     LinkedList<T> list = new LinkedList<>();
     forEachable.forEach(list::add);
diff --git a/src/main/java/com/android/tools/r8/utils/StringUtils.java b/src/main/java/com/android/tools/r8/utils/StringUtils.java
index efe43d3..a561718 100644
--- a/src/main/java/com/android/tools/r8/utils/StringUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/StringUtils.java
@@ -14,6 +14,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -55,7 +56,8 @@
 
   public static String toASCIIString(String s) {
     StringBuilder builder = new StringBuilder();
-    for (char ch : s.toCharArray()) {
+    for (int i = 0; i < s.length(); i++) {
+      char ch = s.charAt(i);
       if (0x1f < ch && ch < 0x7f) {  // 0 - 0x1f and 0x7f are control characters.
         builder.append(ch);
       } else {
@@ -65,6 +67,68 @@
     return builder.toString();
   }
 
+  // Utilities for splitting (and avoiding errorprone String.split).
+
+  /**
+   * Iterate over the substrings of a string split by a single char separator.
+   *
+   * <p>No special treatment of whitespace. No occurrence of the separator will appear in any
+   * split-off substring. Given N occurrences of the separator, the resulting callback will be
+   * called N+1 times.
+   */
+  public static void splitForEach(String string, char separator, Consumer<String> fn) {
+    int length = string.length();
+    int start = 0;
+    for (int i = 0; i < length; i++) {
+      char c = string.charAt(i);
+      if (c == separator) {
+        fn.accept(string.substring(start, i));
+        start = i + 1;
+      }
+    }
+    fn.accept(string.substring(start));
+  }
+
+  /**
+   * Split a string by a single char separator.
+   *
+   * <p>No special treatment of whitespace. No occurrence of the separator will appear in any
+   * split-off substring. Given N occurrences of the separator, the resulting split list will have
+   * size N+1.
+   */
+  public static List<String> split(String string, char separator) {
+    List<String> result = new ArrayList<>();
+    splitForEach(string, separator, result::add);
+    return result;
+  }
+
+  /**
+   * Split a string by a single char separator with the requirement on the split size.
+   *
+   * <p>No special treatment of whitespace. No occurrence of the separator will appear in any
+   * split-off substring. Given N occurrences of the separator, the resulting split list will have
+   * size N+1.
+   *
+   * <p>Thus for a valid split with size=N, the result will be an array of length N if and only if
+   * the input string has exactly N-1 occurrences of the separator. In any other case the return
+   * value is null.
+   */
+  public static String[] splitKnownSize(String string, char separator, int size) {
+    assert size > 1;
+    String[] result = new String[size];
+    IntBox box = new IntBox(0);
+    splitForEach(
+        string,
+        separator,
+        part -> {
+          int i = box.getAndIncrement();
+          if (i < size) {
+            result[i] = part;
+          }
+        });
+    return size == box.get() ? result : null;
+  }
+
   public static boolean appendNonEmpty(
       StringBuilder builder, String pre, Object item, String post) {
     if (item == null) {
diff --git a/src/main/java/com/android/tools/r8/utils/Timing.java b/src/main/java/com/android/tools/r8/utils/Timing.java
index b4519a9..3947cae 100644
--- a/src/main/java/com/android/tools/r8/utils/Timing.java
+++ b/src/main/java/com/android/tools/r8/utils/Timing.java
@@ -20,7 +20,6 @@
 import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.Map.Entry;
-import java.util.Stack;
 
 public class Timing {
 
@@ -75,7 +74,7 @@
   }
 
   private final Node top;
-  private final Stack<Node> stack;
+  private final Deque<Node> stack;
   private final boolean trackMemory;
 
   @Deprecated
@@ -85,7 +84,7 @@
 
   private Timing(String title, boolean trackMemory) {
     this.trackMemory = trackMemory;
-    stack = new Stack<>();
+    stack = new ArrayDeque<>();
     top = new Node(title, trackMemory);
     stack.push(top);
   }
diff --git a/src/test/java/com/android/tools/r8/D8CommandTest.java b/src/test/java/com/android/tools/r8/D8CommandTest.java
index ff10302..11cebfd 100644
--- a/src/test/java/com/android/tools/r8/D8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/D8CommandTest.java
@@ -44,6 +44,7 @@
 import java.nio.file.Paths;
 import java.util.Collection;
 import java.util.List;
+import java.util.Set;
 import java.util.function.Predicate;
 import java.util.zip.ZipFile;
 import org.junit.Test;
@@ -465,10 +466,15 @@
     class MultiTypeConsumer implements DexIndexedConsumer, DexFilePerClassFileConsumer {
 
       @Override
-      public void acceptDexFile(DexFilePerClassFileConsumerData data) {}
+      public void accept(
+          String primaryClassDescriptor,
+          ByteDataView data,
+          Set<String> descriptors,
+          DiagnosticsHandler handler) {}
 
       @Override
-      public void acceptDexIndexedFile(DexIndexedConsumerData data) {}
+      public void accept(
+          int fileIndex, ByteDataView data, Set<String> descriptors, DiagnosticsHandler handler) {}
 
       @Override
       public void finished(DiagnosticsHandler handler) {
diff --git a/src/test/java/com/android/tools/r8/ExtractMarkerTest.java b/src/test/java/com/android/tools/r8/ExtractMarkerTest.java
index e8a4d1d..6ed73d8 100644
--- a/src/test/java/com/android/tools/r8/ExtractMarkerTest.java
+++ b/src/test/java/com/android/tools/r8/ExtractMarkerTest.java
@@ -14,6 +14,7 @@
 import com.android.tools.r8.utils.BooleanUtils;
 import java.nio.file.Paths;
 import java.util.Collection;
+import java.util.Set;
 import org.junit.Assume;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -59,11 +60,15 @@
             .setProgramConsumer(
                 new DexIndexedConsumer.ForwardingConsumer(null) {
                   @Override
-                  public void acceptDexIndexedFile(DexIndexedConsumerData data) {
+                  public void accept(
+                      int fileIndex,
+                      ByteDataView data,
+                      Set<String> descriptors,
+                      DiagnosticsHandler handler) {
                     Marker marker;
                     try {
                       Collection<Marker> markers =
-                          ExtractMarker.extractMarkerFromDexProgramData(data.getByteDataCopy());
+                          ExtractMarker.extractMarkerFromDexProgramData(data.copyByteData());
                       assertEquals(1, markers.size());
                       marker = markers.iterator().next();
                     } catch (Exception e) {
@@ -99,11 +104,12 @@
             .setProgramConsumer(
                 new ClassFileConsumer.ForwardingConsumer(null) {
                   @Override
-                  public void acceptClassFile(ClassFileConsumerData data) {
+                  public void accept(
+                      ByteDataView data, String descriptor, DiagnosticsHandler handler) {
                     Marker marker;
                     try {
                       Collection<Marker> markers =
-                          ExtractMarker.extractMarkerFromClassProgramData(data.getByteDataCopy());
+                          ExtractMarker.extractMarkerFromClassProgramData(data.copyByteData());
                       assertEquals(1, markers.size());
                       marker = markers.iterator().next();
                     } catch (Exception e) {
diff --git a/src/test/java/com/android/tools/r8/KotlinCompilerTool.java b/src/test/java/com/android/tools/r8/KotlinCompilerTool.java
index 259f1a0..642e319 100644
--- a/src/test/java/com/android/tools/r8/KotlinCompilerTool.java
+++ b/src/test/java/com/android/tools/r8/KotlinCompilerTool.java
@@ -17,6 +17,7 @@
 import com.android.tools.r8.utils.ArrayUtils;
 import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.structural.Ordered;
+import java.io.File;
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
@@ -367,6 +368,9 @@
     }
     cmdline.addAll(additionalArguments);
     ProcessBuilder builder = new ProcessBuilder(cmdline);
+    if (ToolHelper.isNewGradleSetup()) {
+      builder.directory(new File(ToolHelper.getProjectRoot()));
+    }
     return ToolHelper.runProcess(builder);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/ProguardMapMarkerTest.java b/src/test/java/com/android/tools/r8/ProguardMapMarkerTest.java
index 5be0095..f0b68de 100644
--- a/src/test/java/com/android/tools/r8/ProguardMapMarkerTest.java
+++ b/src/test/java/com/android/tools/r8/ProguardMapMarkerTest.java
@@ -63,11 +63,15 @@
             .setProgramConsumer(
                 new DexIndexedConsumer.ForwardingConsumer(null) {
                   @Override
-                  public void acceptDexIndexedFile(DexIndexedConsumerData data) {
+                  public void accept(
+                      int fileIndex,
+                      ByteDataView data,
+                      Set<String> descriptors,
+                      DiagnosticsHandler handler) {
                     Marker marker;
                     try {
                       Collection<Marker> markers =
-                          ExtractMarker.extractMarkerFromDexProgramData(data.getByteDataCopy());
+                          ExtractMarker.extractMarkerFromDexProgramData(data.copyByteData());
                       assertEquals(1, markers.size());
                       marker = markers.iterator().next();
                     } catch (Exception e) {
@@ -107,11 +111,12 @@
             .setProgramConsumer(
                 new ClassFileConsumer.ForwardingConsumer(null) {
                   @Override
-                  public void acceptClassFile(ClassFileConsumerData data) {
+                  public void accept(
+                      ByteDataView data, String descriptor, DiagnosticsHandler handler) {
                     Marker marker;
                     try {
                       Collection<Marker> markers =
-                          ExtractMarker.extractMarkerFromClassProgramData(data.getByteDataCopy());
+                          ExtractMarker.extractMarkerFromClassProgramData(data.copyByteData());
                       assertEquals(1, markers.size());
                       marker = markers.iterator().next();
                     } catch (Exception e) {
diff --git a/src/test/java/com/android/tools/r8/R8ModeMarkerTest.java b/src/test/java/com/android/tools/r8/R8ModeMarkerTest.java
index d4ab329..32f0871 100644
--- a/src/test/java/com/android/tools/r8/R8ModeMarkerTest.java
+++ b/src/test/java/com/android/tools/r8/R8ModeMarkerTest.java
@@ -8,6 +8,7 @@
 
 import com.android.tools.r8.dex.Marker;
 import java.util.Collection;
+import java.util.Set;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -39,10 +40,11 @@
     }
 
     @Override
-    public void acceptDexIndexedFile(DexIndexedConsumerData data) {
+    public void accept(
+        int fileIndex, ByteDataView data, Set<String> descriptors, DiagnosticsHandler handler) {
       try {
         Collection<Marker> markers =
-            ExtractMarker.extractMarkerFromDexProgramData(data.getByteDataCopy());
+            ExtractMarker.extractMarkerFromDexProgramData(data.copyByteData());
         assertEquals(1, markers.size());
         marker = markers.iterator().next();
       } catch (Exception e) {
@@ -65,10 +67,10 @@
     }
 
     @Override
-    public void acceptClassFile(ClassFileConsumerData data) {
+    public void accept(ByteDataView data, String descriptors, DiagnosticsHandler handler) {
       try {
         Collection<Marker> markers =
-            ExtractMarker.extractMarkerFromClassProgramData(data.getByteDataCopy());
+            ExtractMarker.extractMarkerFromClassProgramData(data.copyByteData());
         assertEquals(1, markers.size());
         marker = markers.iterator().next();
       } catch (Exception e) {
diff --git a/src/test/java/com/android/tools/r8/R8RunSmaliTestsTest.java b/src/test/java/com/android/tools/r8/R8RunSmaliTestsTest.java
index a89ee46..384043c 100644
--- a/src/test/java/com/android/tools/r8/R8RunSmaliTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunSmaliTestsTest.java
@@ -216,7 +216,6 @@
   @Test
   public void SmaliTest() throws Exception {
     Path originalDexFile = Paths.get(SMALI_DIR, directoryName, dexFileName);
-    // Path outputPath = temp.getRoot().toPath().resolve("classes.dex");
 
     if (failingOnX8.contains(directoryName)) {
       thrown.expect(CompilationFailedException.class);
@@ -229,9 +228,21 @@
     boolean originalFailing =
         (originalFailingOnArtVersions.containsKey(version)
             && originalFailingOnArtVersions.get(version).contains(directoryName));
+    Path testJar = Paths.get(SMALI_DIR, directoryName, "Test.jar");
+    boolean testJarExists = testJar.toFile().exists();
+    Path testJarDex = null;
+    if (testJarExists) {
+      testJarDex =
+          testForD8(Backend.DEX)
+              .setMinApi(parameters)
+              .addProgramFiles(testJar)
+              .compile()
+              .writeToZip();
+    }
     testForR8(parameters.getBackend())
         .addKeepAllClassesRule()
         .addProgramDexFileData(Files.readAllBytes(originalDexFile))
+        .applyIf(testJarExists, p -> p.addProgramFiles(testJar))
         .addDontWarn(missingClasses.getOrDefault(directoryName, Collections.emptySet()))
         .setMinApi(parameters)
         .compile()
@@ -243,12 +254,14 @@
 
     // Also run the original DEX if possible.
     if (!dalvikVerificationError && !originalFailing) {
+      ImmutableList.Builder<String> dexListBuilder = ImmutableList.builder();
+      dexListBuilder.add(originalDexFile.toString());
+      if (testJarExists) {
+        dexListBuilder.add(testJarDex.toString());
+      }
       String originalOutput =
           ToolHelper.runArtNoVerificationErrors(
-              ImmutableList.of(originalDexFile.toString()),
-              "Test",
-              null,
-              parameters.getRuntime().asDex().getVm());
+              dexListBuilder.build(), "Test", null, parameters.getRuntime().asDex().getVm());
       assertEquals(expectedOutput, originalOutput);
     }
   }
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 51ed733..592892a 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -97,9 +97,9 @@
   }
 
   public static String getProjectRoot() {
-    String property = System.getProperty("user.dir");
-    if (property.endsWith("d8_r8/test")) {
-      return "../../";
+    String userDirProperty = System.getProperty("user.dir");
+    if (userDirProperty.endsWith("d8_r8/test")) {
+      return Paths.get(userDirProperty).getParent().getParent().toString() + "/";
     }
     return "";
   }
@@ -2273,7 +2273,7 @@
       throws IOException {
     boolean printCwd = builder.directory() != null;
     if (printCwd) {
-      out.println("(cd " + builder.directory().toString() + "; ");
+      out.println("(cd " + builder.directory() + "; ");
     }
     String command = String.join(" ", builder.command());
     out.println(command);
diff --git a/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTest.java b/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTest.java
index 7a10578..60ca0c6 100644
--- a/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTest.java
+++ b/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTest.java
@@ -80,8 +80,17 @@
     return PostStartupMockClass.class;
   }
 
+  public static Path getProjectRoot() {
+    String userDirProperty = System.getProperty("user.dir");
+    if (userDirProperty.endsWith("d8_r8/test")) {
+      return Paths.get(userDirProperty).getParent().getParent();
+    }
+    return Paths.get("");
+  }
+
   public Path getJava8RuntimeJar() {
-    return Paths.get("third_party", "openjdk", "openjdk-rt-1.8", "rt.jar");
+    return getProjectRoot()
+        .resolve(Paths.get("third_party", "openjdk", "openjdk-rt-1.8", "rt.jar"));
   }
 
   public List<String> getKeepMainRules(Class<?> clazz) {
diff --git a/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java b/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java
index f4ef14b..c2d4a30 100644
--- a/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java
+++ b/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java
@@ -13,7 +13,6 @@
 import com.android.tools.r8.compilerapi.assertionconfiguration.AssertionConfigurationTest;
 import com.android.tools.r8.compilerapi.classconflictresolver.ClassConflictResolverTest;
 import com.android.tools.r8.compilerapi.desugardependencies.DesugarDependenciesTest;
-import com.android.tools.r8.compilerapi.dexconsumers.PerClassSyntheticContextsTest;
 import com.android.tools.r8.compilerapi.diagnostics.ProguardKeepRuleDiagnosticsApiTest;
 import com.android.tools.r8.compilerapi.diagnostics.UnsupportedFeaturesDiagnosticApiTest;
 import com.android.tools.r8.compilerapi.globalsynthetics.GlobalSyntheticsTest;
@@ -24,6 +23,7 @@
 import com.android.tools.r8.compilerapi.mockdata.PostStartupMockClass;
 import com.android.tools.r8.compilerapi.sourcefile.CustomSourceFileTest;
 import com.android.tools.r8.compilerapi.startupprofile.StartupProfileApiTest;
+import com.android.tools.r8.compilerapi.syntheticscontexts.SyntheticContextsConsumerTest;
 import com.android.tools.r8.compilerapi.testsetup.ApiTestingSetUpTest;
 import com.android.tools.r8.compilerapi.wrappers.CommandLineParserTest;
 import com.android.tools.r8.compilerapi.wrappers.EnableMissingLibraryApiModelingTest;
@@ -60,7 +60,7 @@
           StartupProfileApiTest.ApiTest.class,
           ClassConflictResolverTest.ApiTest.class,
           ProguardKeepRuleDiagnosticsApiTest.ApiTest.class,
-          PerClassSyntheticContextsTest.ApiTest.class);
+          SyntheticContextsConsumerTest.ApiTest.class);
 
   private final TemporaryFolder temp;
 
diff --git a/src/test/java/com/android/tools/r8/compilerapi/dexconsumers/PerClassSyntheticContextsTest.java b/src/test/java/com/android/tools/r8/compilerapi/syntheticscontexts/SyntheticContextsConsumerTest.java
similarity index 66%
rename from src/test/java/com/android/tools/r8/compilerapi/dexconsumers/PerClassSyntheticContextsTest.java
rename to src/test/java/com/android/tools/r8/compilerapi/syntheticscontexts/SyntheticContextsConsumerTest.java
index 69f7b9b..f596594 100644
--- a/src/test/java/com/android/tools/r8/compilerapi/dexconsumers/PerClassSyntheticContextsTest.java
+++ b/src/test/java/com/android/tools/r8/compilerapi/syntheticscontexts/SyntheticContextsConsumerTest.java
@@ -1,17 +1,18 @@
 // Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.compilerapi.dexconsumers;
+package com.android.tools.r8.compilerapi.syntheticscontexts;
 
 import static org.junit.Assert.assertEquals;
 
+import com.android.tools.r8.ByteDataView;
 import com.android.tools.r8.ClassFileConsumer;
-import com.android.tools.r8.ClassFileConsumerData;
 import com.android.tools.r8.D8;
 import com.android.tools.r8.D8Command;
 import com.android.tools.r8.DexFilePerClassFileConsumer;
-import com.android.tools.r8.DexFilePerClassFileConsumerData;
 import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.SyntheticInfoConsumer;
+import com.android.tools.r8.SyntheticInfoConsumerData;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.compilerapi.CompilerApiTest;
 import com.android.tools.r8.compilerapi.CompilerApiTestRunner;
@@ -20,12 +21,14 @@
 import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Consumer;
 import org.junit.Test;
 
-public class PerClassSyntheticContextsTest extends CompilerApiTestRunner {
+public class SyntheticContextsConsumerTest extends CompilerApiTestRunner {
 
-  public PerClassSyntheticContextsTest(TestParameters parameters) {
+  public SyntheticContextsConsumerTest(TestParameters parameters) {
     super(parameters);
   }
 
@@ -47,8 +50,8 @@
             new ClassFileConsumer() {
 
               @Override
-              public void acceptClassFile(ClassFileConsumerData data) {
-                outputs.put(data.getClassDescriptor(), data.getByteDataCopy());
+              public void accept(ByteDataView data, String descriptor, DiagnosticsHandler handler) {
+                outputs.put(descriptor, data.copyByteData());
               }
 
               @Override
@@ -76,16 +79,36 @@
     }
 
     public void run(byte[] input, Consumer<String> syntheticContext) throws Exception {
+      Map<String, String> synthetic2context = new ConcurrentHashMap<>();
       D8.run(
           D8Command.builder()
               .addClassProgramData(input, Origin.unknown())
               .addLibraryFiles(getJava8RuntimeJar())
               .setMinApiLevel(1)
+              .setSyntheticInfoConsumer(
+                  new SyntheticInfoConsumer() {
+                    @Override
+                    public void acceptSyntheticInfo(SyntheticInfoConsumerData data) {
+                      synthetic2context.put(
+                          data.getSyntheticClass().getDescriptor(),
+                          data.getSynthesizingContextClass().getDescriptor());
+                    }
+
+                    @Override
+                    public void finished() {
+                      // nothing to finish up.
+                    }
+                  })
               .setProgramConsumer(
                   new DexFilePerClassFileConsumer() {
+
                     @Override
-                    public void acceptDexFile(DexFilePerClassFileConsumerData data) {
-                      syntheticContext.accept(data.getSynthesizingContextForPrimaryClass());
+                    public void accept(
+                        String primaryClassDescriptor,
+                        ByteDataView data,
+                        Set<String> descriptors,
+                        DiagnosticsHandler handler) {
+                      syntheticContext.accept(synthetic2context.get(primaryClassDescriptor));
                     }
 
                     @Override
diff --git a/src/test/java/com/android/tools/r8/d8/DexVersionTests.java b/src/test/java/com/android/tools/r8/d8/DexVersionTests.java
index 7c82964..f413f98 100644
--- a/src/test/java/com/android/tools/r8/d8/DexVersionTests.java
+++ b/src/test/java/com/android/tools/r8/d8/DexVersionTests.java
@@ -5,17 +5,18 @@
 
 import static org.junit.Assert.assertTrue;
 
+import com.android.tools.r8.ByteDataView;
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.D8;
 import com.android.tools.r8.D8Command;
 import com.android.tools.r8.DexIndexedConsumer;
-import com.android.tools.r8.DexIndexedConsumerData;
 import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.OutputMode;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.util.Set;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -76,7 +77,8 @@
     boolean hasOutput = false;
 
     @Override
-    public void acceptDexIndexedFile(DexIndexedConsumerData data) {
+    public void accept(
+        int fileIndex, ByteDataView data, Set<String> descriptors, DiagnosticsHandler handler) {
       hasOutput = true;
     }
 
diff --git a/src/test/java/com/android/tools/r8/debuginfo/NoLineInfoTest.java b/src/test/java/com/android/tools/r8/debuginfo/NoLineInfoTest.java
index 87820f1..f0d4c89 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/NoLineInfoTest.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/NoLineInfoTest.java
@@ -5,6 +5,7 @@
 
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assume.assumeFalse;
 
 import com.android.tools.r8.TestBase;
@@ -126,7 +127,10 @@
                 assertThat(
                     "Unexpected input-source stacktrace",
                     stacktrace,
-                    StackTrace.isSame(getUnexpectedRetracedStacktrace())));
+                    StackTrace.isSame(
+                        parameters.isCfRuntime()
+                            ? getExpectedInputStacktrace()
+                            : getUnexpectedRetracedStacktrace())));
   }
 
   private StackTraceLine line(String file, String method, int line) {
@@ -201,10 +205,9 @@
 
   // TODO(b/232212653): The retraced stack trace should be the same as `getExpectedInputStacktrace`.
   private StackTrace getUnexpectedRetracedStacktrace() {
+    assertFalse(parameters.isCfRuntime());
     StackTraceLine fooLine;
-    if (parameters.isCfRuntime()) {
-      fooLine = inputLine("foo", -1);
-    } else if (customSourceFile) {
+    if (customSourceFile) {
       // TODO(b/232212653): Should retrace convert out of "0" and represent it as <noline>/-1?
       fooLine = inputLine("foo", 0);
     } else if (isRuntimeWithPcAsLineNumberSupport()) {
@@ -213,17 +216,8 @@
     } else {
       fooLine = inputLine("foo", -1);
     }
-
-    // TODO(b/232212653): Normal line-opt will cause a single-line mapping. Retrace should not
-    //  optimize that to mean it represents a single possible line. (<noline> should not match 1:x).
-    StackTraceLine barLine =
-        parameters.isCfRuntime() ? inputLine("bar", 100) : inputLine("bar", getPcEncoding(0));
-
-    // TODO(b/232212653): The retracing in CF where the line table is preserved is incorrect.
-    //  same issue as for bar.
-    StackTraceLine bazLine =
-        parameters.isCfRuntime() ? inputLine("baz", 100) : inputLine("baz", getPcEncoding(0));
-
+    StackTraceLine barLine = inputLine("bar", getPcEncoding(0));
+    StackTraceLine bazLine = inputLine("baz", getPcEncoding(0));
     return StackTrace.builder()
         .add(fooLine)
         .add(barLine)
diff --git a/src/test/java/com/android/tools/r8/desugar/DesugarToClassFileDeprecatedAttribute.java b/src/test/java/com/android/tools/r8/desugar/DesugarToClassFileDeprecatedAttribute.java
index 2bc0090..efcc639 100644
--- a/src/test/java/com/android/tools/r8/desugar/DesugarToClassFileDeprecatedAttribute.java
+++ b/src/test/java/com/android/tools/r8/desugar/DesugarToClassFileDeprecatedAttribute.java
@@ -6,10 +6,11 @@
 
 import static org.junit.Assert.assertTrue;
 
+import com.android.tools.r8.ByteDataView;
 import com.android.tools.r8.ClassFileConsumer;
 import com.android.tools.r8.ClassFileConsumer.ForwardingConsumer;
-import com.android.tools.r8.ClassFileConsumerData;
 import com.android.tools.r8.D8TestCompileResult;
+import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.R8FullTestBuilder;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
@@ -90,8 +91,9 @@
             .setProgramConsumer(
                 new ClassFileConsumer.ForwardingConsumer(null) {
                   @Override
-                  public void acceptClassFile(ClassFileConsumerData data) {
-                    checkDeprecatedAttributes(data.getByteDataView().getBuffer());
+                  public void accept(
+                      ByteDataView data, String descriptor, DiagnosticsHandler handler) {
+                    checkDeprecatedAttributes(data.getBuffer());
                   }
                 })
             .compile();
@@ -126,8 +128,8 @@
       builder.setProgramConsumer(
           new ForwardingConsumer(null) {
             @Override
-            public void acceptClassFile(ClassFileConsumerData data) {
-              checkDeprecatedAttributes(data.getByteDataView().getBuffer());
+            public void accept(ByteDataView data, String descriptor, DiagnosticsHandler handler) {
+              checkDeprecatedAttributes(data.getBuffer());
             }
           });
     }
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/BackportDuplicationTest.java b/src/test/java/com/android/tools/r8/desugar/backports/BackportDuplicationTest.java
index 9be08a2..ed2dd4e 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/BackportDuplicationTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/BackportDuplicationTest.java
@@ -10,11 +10,11 @@
 import static org.junit.Assert.fail;
 import static org.junit.Assume.assumeTrue;
 
+import com.android.tools.r8.ByteDataView;
 import com.android.tools.r8.ClassFileConsumer;
-import com.android.tools.r8.ClassFileConsumerData;
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.DexFilePerClassFileConsumer;
-import com.android.tools.r8.DexFilePerClassFileConsumerData;
+import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.OutputMode;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
@@ -247,16 +247,21 @@
             firstRoundOutput.isCf()
                 ? new ClassFileConsumer.ForwardingConsumer(null) {
                   @Override
-                  public void acceptClassFile(ClassFileConsumerData data) {
-                    byte[] bytes = data.getByteDataCopy();
+                  public void accept(
+                      ByteDataView data, String descriptor, DiagnosticsHandler handler) {
+                    byte[] bytes = data.copyByteData();
                     assert bytes != null;
                     outputsRoundOne.add(bytes);
                   }
                 }
                 : new DexFilePerClassFileConsumer.ForwardingConsumer(null) {
                   @Override
-                  public void acceptDexFile(DexFilePerClassFileConsumerData data) {
-                    byte[] bytes = data.getByteDataCopy();
+                  public void accept(
+                      String primaryClassDescriptor,
+                      ByteDataView data,
+                      Set<String> descriptors,
+                      DiagnosticsHandler handler) {
+                    byte[] bytes = data.copyByteData();
                     assert bytes != null;
                     outputsRoundOne.add(bytes);
                   }
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/BackportMainDexTest.java b/src/test/java/com/android/tools/r8/desugar/backports/BackportMainDexTest.java
index 4a55fc2..5369238 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/BackportMainDexTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/BackportMainDexTest.java
@@ -9,8 +9,8 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 
+import com.android.tools.r8.ByteDataView;
 import com.android.tools.r8.DexIndexedConsumer;
-import com.android.tools.r8.DexIndexedConsumerData;
 import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.GenerateMainDexListRunResult;
 import com.android.tools.r8.OutputMode;
@@ -361,12 +361,13 @@
     Set<String> mainDexDescriptors;
 
     @Override
-    public void acceptDexIndexedFile(DexIndexedConsumerData data) {
-      if (data.getFileIndex() == 0) {
+    public void accept(
+        int fileIndex, ByteDataView data, Set<String> descriptors, DiagnosticsHandler handler) {
+      if (fileIndex == 0) {
         assertNull(mainDexBytes);
         assertNull(mainDexDescriptors);
-        mainDexBytes = data.getByteDataCopy();
-        mainDexDescriptors = data.getClassDescriptors();
+        mainDexBytes = data.copyByteData();
+        mainDexDescriptors = descriptors;
       }
     }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/EmptyDesugaredLibrary.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/EmptyDesugaredLibrary.java
index 2f65784..3fd5104 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/EmptyDesugaredLibrary.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/EmptyDesugaredLibrary.java
@@ -9,8 +9,9 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
+import com.android.tools.r8.ByteDataView;
 import com.android.tools.r8.DexIndexedConsumer;
-import com.android.tools.r8.DexIndexedConsumerData;
+import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.L8TestBuilder;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification;
@@ -21,6 +22,7 @@
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.List;
+import java.util.Set;
 import java.util.zip.ZipFile;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -65,7 +67,8 @@
     }
 
     @Override
-    public void acceptDexIndexedFile(DexIndexedConsumerData data) {
+    public void accept(
+        int fileIndex, ByteDataView data, Set<String> descriptors, DiagnosticsHandler handler) {
       count++;
     }
   }
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/SplitterTestBase.java b/src/test/java/com/android/tools/r8/dexsplitter/SplitterTestBase.java
index 3988efd..1d8821e 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/SplitterTestBase.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/SplitterTestBase.java
@@ -3,8 +3,10 @@
 import static junit.framework.TestCase.fail;
 import static org.junit.Assume.assumeTrue;
 
+import com.android.tools.r8.ByteDataView;
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.DexIndexedConsumer.ArchiveConsumer;
+import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.FeatureSplit;
 import com.android.tools.r8.R8FullTestBuilder;
 import com.android.tools.r8.R8TestCompileResult;
@@ -90,7 +92,17 @@
 
     builder
         .addProgramResourceProvider(ArchiveResourceProvider.fromArchive(featureJar, true))
-        .setProgramConsumer(new ArchiveConsumer(outputPath, true));
+        .setProgramConsumer(
+            new ArchiveConsumer(outputPath, true) {
+              @Override
+              public void accept(
+                  int fileIndex,
+                  ByteDataView data,
+                  Set<String> descriptors,
+                  DiagnosticsHandler handler) {
+                super.accept(fileIndex, data, descriptors, handler);
+              }
+            });
   }
 
   public static FeatureSplit splitWithNonJavaFile(
diff --git a/src/test/java/com/android/tools/r8/kotlin/reflection/ReflectiveConstructionWithInlineClassTest.java b/src/test/java/com/android/tools/r8/kotlin/reflection/ReflectiveConstructionWithInlineClassTest.java
index 32568f9..a3e00b4 100644
--- a/src/test/java/com/android/tools/r8/kotlin/reflection/ReflectiveConstructionWithInlineClassTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/reflection/ReflectiveConstructionWithInlineClassTest.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.R8FullTestBuilder;
 import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestShrinkerBuilder;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.kotlin.metadata.KotlinMetadataTestBase;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
@@ -96,6 +97,10 @@
         .addProgramFiles(kotlinc.getKotlinReflectJar())
         .addProgramFiles(kotlinc.getKotlinAnnotationJar())
         .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.LATEST))
+        .applyIf(
+            parameters.isCfRuntime()
+                && kotlinParameters.isNewerThan(KotlinCompilerVersion.KOTLINC_1_8_0),
+            TestShrinkerBuilder::addDontWarnJavaLangInvokeLambdaMetadataFactory)
         .setMinApi(parameters)
         .addKeepMainRule(MAIN_CLASS)
         .addKeepClassAndMembersRules(PKG + ".Data")
@@ -119,11 +124,7 @@
         .assertNoErrorMessages()
         .apply(KotlinMetadataTestBase::verifyExpectedWarningsFromKotlinReflectAndStdLib)
         .run(parameters.getRuntime(), MAIN_CLASS)
-        // TODO(b/269792580): Figure out why this is throwing an abstract method error.
-        .assertFailureWithErrorThatThrows(
-            kotlinParameters.isKotlinDev() && parameters.isCfRuntime()
-                ? AbstractMethodError.class
-                : IllegalArgumentException.class);
+        .assertFailureWithErrorThatThrows(IllegalArgumentException.class);
   }
 
   @Test
@@ -134,9 +135,6 @@
         .assertNoErrorMessages()
         .apply(KotlinMetadataTestBase::verifyExpectedWarningsFromKotlinReflectAndStdLib)
         .run(parameters.getRuntime(), MAIN_CLASS)
-        // TODO(b/269792580): Figure out why this is throwing an abstract method error.
-        .assertFailureWithErrorThatThrowsIf(
-            kotlinParameters.isKotlinDev() && parameters.isCfRuntime(), AbstractMethodError.class)
-        .assertSuccessWithOutputLinesIf(!kotlinParameters.isKotlinDev(), EXPECTED_OUTPUT);
+        .assertSuccessWithOutputLines(EXPECTED_OUTPUT);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/lightir/LirBasicCallbackTest.java b/src/test/java/com/android/tools/r8/lightir/LirBasicCallbackTest.java
index 062470c..abd980d 100644
--- a/src/test/java/com/android/tools/r8/lightir/LirBasicCallbackTest.java
+++ b/src/test/java/com/android/tools/r8/lightir/LirBasicCallbackTest.java
@@ -37,6 +37,11 @@
   private static class ThrowingStrategy extends LirEncodingStrategy<Value, Integer> {
 
     @Override
+    public boolean isPhiInlineInstruction() {
+      return false;
+    }
+
+    @Override
     public void defineBlock(BasicBlock block, int index) {
       throw new Unreachable();
     }
@@ -62,7 +67,7 @@
     }
 
     @Override
-    public LirSsaValueStrategy<Integer> getSsaValueStrategy() {
+    public LirStrategyInfo<Integer> getStrategyInfo() {
       return null;
     }
   }
diff --git a/src/test/java/com/android/tools/r8/naming/retraceproguard/CatchAllRangeWithNoLineNumberTest.java b/src/test/java/com/android/tools/r8/naming/retraceproguard/CatchAllRangeWithNoLineNumberTest.java
index 4372134..aac289a 100644
--- a/src/test/java/com/android/tools/r8/naming/retraceproguard/CatchAllRangeWithNoLineNumberTest.java
+++ b/src/test/java/com/android/tools/r8/naming/retraceproguard/CatchAllRangeWithNoLineNumberTest.java
@@ -26,7 +26,6 @@
 @RunWith(Parameterized.class)
 public class CatchAllRangeWithNoLineNumberTest extends TestBase {
 
-  private final TestParameters parameters;
   private final ProguardVersion proguardVersion;
 
   @Parameters(name = "{0}, {1}")
@@ -36,8 +35,8 @@
 
   public CatchAllRangeWithNoLineNumberTest(
       TestParameters parameters, ProguardVersion proguardVersion) {
-    this.parameters = parameters;
     this.proguardVersion = proguardVersion;
+    parameters.assertNoneRuntime();
   }
 
   private final String[] stackTrace =
@@ -85,7 +84,7 @@
   private final String retracedR8 =
       StringUtils.lines(
           "\tat foo.bar.Baz.foo(Baz.java:33)",
-          "\tat foo.bar.Qux.foo(Qux.java:33)",
+          "\tat foo.bar.Qux.foo(Qux.java)",
           "\tat foo.bar.Quux.foo(Quux.java:33)",
           "\tat foo.bar.Baz.foo(Baz.java:33)",
           "\tat foo.bar.Baz.foo(Baz.java:33)");
diff --git a/src/test/java/com/android/tools/r8/regress/Regress37740372.java b/src/test/java/com/android/tools/r8/regress/Regress37740372.java
index b46866c..8047716 100644
--- a/src/test/java/com/android/tools/r8/regress/Regress37740372.java
+++ b/src/test/java/com/android/tools/r8/regress/Regress37740372.java
@@ -8,11 +8,12 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
+import com.android.tools.r8.ByteDataView;
 import com.android.tools.r8.D8;
 import com.android.tools.r8.D8Command;
 import com.android.tools.r8.D8Command.Builder;
 import com.android.tools.r8.DexIndexedConsumer;
-import com.android.tools.r8.DexIndexedConsumerData;
+import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.origin.EmbeddedOrigin;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.smali.SmaliTestBase;
@@ -21,6 +22,7 @@
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import java.util.Base64;
+import java.util.Set;
 import org.junit.Test;
 
 public class Regress37740372 extends SmaliTestBase {
@@ -140,9 +142,10 @@
     }
 
     @Override
-    public void acceptDexIndexedFile(DexIndexedConsumerData data) {
-      assertEquals(0, data.getFileIndex());
-      this.data = data.getByteDataCopy();
+    public void accept(
+        int fileIndex, ByteDataView data, Set<String> descriptors, DiagnosticsHandler handler) {
+      assertEquals(0, fileIndex);
+      this.data = data.copyByteData();
     }
   }
 
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
index 36a00f1..c8ce59b 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
@@ -43,7 +43,6 @@
 import com.android.tools.r8.retrace.stacktraces.InlineNoLineNumberStackTrace;
 import com.android.tools.r8.retrace.stacktraces.InlineNoLineWithBaseEntryNumberAssumeNoInlineStackTrace;
 import com.android.tools.r8.retrace.stacktraces.InlinePreambleNoOriginalStackTrace;
-import com.android.tools.r8.retrace.stacktraces.InlinePreambleWithOriginalStackTrace;
 import com.android.tools.r8.retrace.stacktraces.InlineSourceFileContextStackTrace;
 import com.android.tools.r8.retrace.stacktraces.InlineSourceFileStackTrace;
 import com.android.tools.r8.retrace.stacktraces.InlineWithLineNumbersStackTrace;
@@ -248,11 +247,6 @@
   }
 
   @Test
-  public void testInlinePreambleWithOriginalStackTrace() throws Exception {
-    runRetraceTest(new InlinePreambleWithOriginalStackTrace());
-  }
-
-  @Test
   public void testInlinePreambleNoOriginalStackTrace() throws Exception {
     runRetraceTest(new InlinePreambleNoOriginalStackTrace());
   }
diff --git a/src/test/java/com/android/tools/r8/retrace/StackTraceRegularExpressionParserTests.java b/src/test/java/com/android/tools/r8/retrace/StackTraceRegularExpressionParserTests.java
index 80d580b..480f8bf 100644
--- a/src/test/java/com/android/tools/r8/retrace/StackTraceRegularExpressionParserTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/StackTraceRegularExpressionParserTests.java
@@ -485,12 +485,12 @@
 
           @Override
           public List<String> retracedStackTrace() {
-            return ImmutableList.of("com.android.tools.r8.R8.foo(7)");
+            return ImmutableList.of("com.android.tools.r8.R8.foo()");
           }
 
           @Override
           public List<String> retraceVerboseStackTrace() {
-            return ImmutableList.of("com.android.tools.r8.R8.boolean foo()(7)");
+            return ImmutableList.of("com.android.tools.r8.R8.boolean foo()()");
           }
 
           @Override
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/AmbiguousWithMultipleLineMappingsStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/AmbiguousWithMultipleLineMappingsStackTrace.java
index 51a9249..1041ace 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/AmbiguousWithMultipleLineMappingsStackTrace.java
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/AmbiguousWithMultipleLineMappingsStackTrace.java
@@ -40,9 +40,7 @@
     return Arrays.asList(
         "java.lang.IndexOutOfBoundsException",
         "\tat java.util.ArrayList.get(ArrayList.java:411)",
-        "\tat com.android.tools.r8.Internal.void foo(int)(Internal.java:10)",
-        "\t<OR> at com.android.tools.r8.Internal.void foo(int)(Internal.java:11)",
-        "\t<OR> at com.android.tools.r8.Internal.void foo(int)(Internal.java:12)");
+        "\tat com.android.tools.r8.Internal.void foo(int)(Internal.java)");
   }
 
   @Override
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/AmbiguousWithSignatureStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/AmbiguousWithSignatureStackTrace.java
index c9ed69c..1f0cd0c 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/AmbiguousWithSignatureStackTrace.java
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/AmbiguousWithSignatureStackTrace.java
@@ -41,10 +41,10 @@
     return Arrays.asList(
         "java.lang.IndexOutOfBoundsException",
         "\tat java.util.ArrayList.get(ArrayList.java:411)",
-        "\tat com.android.tools.r8.Internal.boolean foo(int,int)(Internal.java:13)",
-        "\t<OR> at com.android.tools.r8.Internal.void foo(int)(Internal.java:10)",
-        "\t<OR> at com.android.tools.r8.Internal.void foo(int,boolean)(Internal.java:12)",
-        "\t<OR> at com.android.tools.r8.Internal.void foo(int,int)(Internal.java:11)");
+        "\tat com.android.tools.r8.Internal.boolean foo(int,int)(Internal.java)",
+        "\t<OR> at com.android.tools.r8.Internal.void foo(int)(Internal.java)",
+        "\t<OR> at com.android.tools.r8.Internal.void foo(int,boolean)(Internal.java)",
+        "\t<OR> at com.android.tools.r8.Internal.void foo(int,int)(Internal.java)");
   }
 
   @Override
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineNoLineAssumeNoInlineAmbiguousStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineNoLineAssumeNoInlineAmbiguousStackTrace.java
index 515d6b0..7c63a27 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineNoLineAssumeNoInlineAmbiguousStackTrace.java
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineNoLineAssumeNoInlineAmbiguousStackTrace.java
@@ -21,20 +21,14 @@
   public List<String> retracedStackTrace() {
     return Arrays.asList(
         "Exception in thread \"main\" java.lang.NullPointerException",
-        // TODO(b/231622686): Should not emit inline frame.
-        "\tat retrace.Main.method1(Main.java)",
-        "\tat retrace.Main.main(Main.java)",
-        "\t<OR> at retrace.Main.otherMain(Main.java)");
+        "\tat retrace.Main.otherMain(Main.java)");
   }
 
   @Override
   public List<String> retraceVerboseStackTrace() {
     return Arrays.asList(
         "Exception in thread \"main\" java.lang.NullPointerException",
-        // TODO(b/231622686): Should not emit inline frame.
-        "\tat retrace.Main.void method1(java.lang.String)(Main.java:0)",
-        "\tat retrace.Main.void main(java.lang.String[])(Main.java:0)",
-        "\t<OR> at retrace.Main.void otherMain(java.lang.String[])(Main.java)");
+        "\tat retrace.Main.void otherMain(java.lang.String[])(Main.java)");
   }
 
   @Override
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineNoLineNumberAssumeNoInlineStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineNoLineNumberAssumeNoInlineStackTrace.java
index ffc5da2..9382f6e 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineNoLineNumberAssumeNoInlineStackTrace.java
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineNoLineNumberAssumeNoInlineStackTrace.java
@@ -19,12 +19,8 @@
 
   @Override
   public List<String> retracedStackTrace() {
-    // TODO(b/231622686): Should assume that no lines means no inline frames.
     return Arrays.asList(
         "Exception in thread \"main\" java.lang.NullPointerException",
-        "\tat retrace.Main.method1(Main.java)",
-        "\tat retrace.Main.main(Main.java)",
-        "\t<OR> at retrace.Main.method2(Main.java)",
         "\tat retrace.Main.main(Main.java)");
   }
 
@@ -33,10 +29,7 @@
     // TODO(b/231622686): Should assume that no lines means no inline frames.
     return Arrays.asList(
         "Exception in thread \"main\" java.lang.NullPointerException",
-        "\tat retrace.Main.void method1(java.lang.String)(Main.java:0)",
-        "\tat retrace.Main.void main(java.lang.String[])(Main.java:0)",
-        "\t<OR> at retrace.Main.void method2(int)(Main.java:0)",
-        "\tat retrace.Main.void main(java.lang.String[])(Main.java:0)");
+        "\tat retrace.Main.void main(java.lang.String[])(Main.java)");
   }
 
   @Override
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineNoLineWithBaseEntryNumberAssumeNoInlineStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineNoLineWithBaseEntryNumberAssumeNoInlineStackTrace.java
index 3e52ebd..3e6de74 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineNoLineWithBaseEntryNumberAssumeNoInlineStackTrace.java
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineNoLineWithBaseEntryNumberAssumeNoInlineStackTrace.java
@@ -19,22 +19,16 @@
 
   @Override
   public List<String> retracedStackTrace() {
-    // TODO(b/231622686): Should assume that no lines means no inline frames.
     return Arrays.asList(
         "Exception in thread \"main\" java.lang.NullPointerException",
-        "\tat retrace.Main.main(Main.java)",
-        "\t<OR> at retrace.Main.method1(Main.java)",
         "\tat retrace.Main.main(Main.java)");
   }
 
   @Override
   public List<String> retraceVerboseStackTrace() {
-    // TODO(b/231622686): Should assume that no lines means no inline frames.
     return Arrays.asList(
         "Exception in thread \"main\" java.lang.NullPointerException",
-        "\tat retrace.Main.void main(java.lang.String[])(Main.java)",
-        "\t<OR> at retrace.Main.void method1(java.lang.String)(Main.java:0)",
-        "\tat retrace.Main.void main(java.lang.String[])(Main.java:0)");
+        "\tat retrace.Main.void main(java.lang.String[])(Main.java)");
   }
 
   @Override
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/InlinePreambleNoOriginalStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/InlinePreambleNoOriginalStackTrace.java
index 47732b8..79a9172 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/InlinePreambleNoOriginalStackTrace.java
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/InlinePreambleNoOriginalStackTrace.java
@@ -20,21 +20,15 @@
   @Override
   public List<String> retracedStackTrace() {
     return Arrays.asList(
-        // TODO(b/231622686): Should only include preamble
         "Exception in thread \"main\" java.lang.NullPointerException",
-        "\tat retrace.Main.main(Main.java)",
-        "\t<OR> at retrace.Main.method1(Main.java)",
         "\tat retrace.Main.main(Main.java)");
   }
 
   @Override
   public List<String> retraceVerboseStackTrace() {
     return Arrays.asList(
-        // TODO(b/231622686): Should only include preamble
         "Exception in thread \"main\" java.lang.NullPointerException",
-        "\tat retrace.Main.void main(java.lang.String[])(Main.java)",
-        "\t<OR> at retrace.Main.void method1(java.lang.String)(Main.java:0)",
-        "\tat retrace.Main.void main(java.lang.String[])(Main.java:0)");
+        "\tat retrace.Main.void main(java.lang.String[])(Main.java)");
   }
 
   @Override
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/InlinePreambleWithOriginalStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/InlinePreambleWithOriginalStackTrace.java
deleted file mode 100644
index 72c2076..0000000
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/InlinePreambleWithOriginalStackTrace.java
+++ /dev/null
@@ -1,53 +0,0 @@
-// 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.retrace.stacktraces;
-
-import com.android.tools.r8.utils.StringUtils;
-import java.util.Arrays;
-import java.util.List;
-
-public class InlinePreambleWithOriginalStackTrace implements StackTraceForTest {
-
-  @Override
-  public List<String> obfuscatedStackTrace() {
-    return Arrays.asList(
-        "Exception in thread \"main\" java.lang.NullPointerException",
-        "\tat a.foo(Unknown Source)");
-  }
-
-  @Override
-  public List<String> retracedStackTrace() {
-    return Arrays.asList(
-        // TODO(b/231622686): Should only include preamble
-        "Exception in thread \"main\" java.lang.NullPointerException",
-        "\tat retrace.Main.main(Main.java)",
-        "\t<OR> at retrace.Main.method1(Main.java)",
-        "\tat retrace.Main.main(Main.java)");
-  }
-
-  @Override
-  public List<String> retraceVerboseStackTrace() {
-    return Arrays.asList(
-        // TODO(b/231622686): Should only include preamble
-        "Exception in thread \"main\" java.lang.NullPointerException",
-        "\tat retrace.Main.void main(java.lang.String[])(Main.java:0)",
-        "\t<OR> at retrace.Main.void method1(java.lang.String)(Main.java:0)",
-        "\tat retrace.Main.void main(java.lang.String[])(Main.java:0)");
-  }
-
-  @Override
-  public String mapping() {
-    return StringUtils.lines(
-        "retrace.Main -> a:",
-        "    0:1:void main(java.lang.String[]):0:0 -> foo",
-        "    2:2:void method1(java.lang.String):0:0 -> foo",
-        "    2:2:void main(java.lang.String[]):0 -> foo");
-  }
-
-  @Override
-  public int expectedWarnings() {
-    return 0;
-  }
-}
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/MultipleLinesNoLineNumberStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/MultipleLinesNoLineNumberStackTrace.java
index b1a31db..3f5746d 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/MultipleLinesNoLineNumberStackTrace.java
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/MultipleLinesNoLineNumberStackTrace.java
@@ -30,9 +30,8 @@
   public List<String> retracedStackTrace() {
     return Arrays.asList(
         "Exception in thread \"main\" java.lang.NullPointerException",
-        "\tat com.android.tools.r8.naming.retrace.Main.main(Main.java)",
-        "\t<OR> at com.android.tools.r8.naming.retrace.Main.method1(Main.java)",
-        "\tat com.android.tools.r8.naming.retrace.Main.main(Main.java)");
+        "\tat com.android.tools.r8.naming.retrace.Main.method1(Main.java:42)",
+        "\tat com.android.tools.r8.naming.retrace.Main.main(Main.java:28)");
   }
 
   @Override
@@ -40,8 +39,6 @@
     return Arrays.asList(
         "Exception in thread \"main\" java.lang.NullPointerException",
         "\tat com.android.tools.r8.naming.retrace.Main.void"
-            + " main(java.lang.String[])(Main.java:153)",
-        "\t<OR> at com.android.tools.r8.naming.retrace.Main.void"
             + " method1(java.lang.String)(Main.java:42)",
         "\tat com.android.tools.r8.naming.retrace.Main.void"
             + " main(java.lang.String[])(Main.java:28)");
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/OutsideLineRangeStackTraceTest.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/OutsideLineRangeStackTraceTest.java
index ea9ada4..2ebf79a 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/OutsideLineRangeStackTraceTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/OutsideLineRangeStackTraceTest.java
@@ -47,8 +47,7 @@
         "\tat some.other.Class.void method1()(Class.java:42)",
         "\tat some.other.Class.void method1()(Class.java:42)",
         "\tat some.Class.a(Class.java:27)",
-        "\tat some.Class.void method2()(Class.java)",
-        "\t<OR> at some.Class.void method2()(Class.java:10)");
+        "\tat some.Class.void method2()(Class.java)");
   }
 
   @Override
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/PreambleLineNumberStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/PreambleLineNumberStackTrace.java
index 316fdfc..98a6ed6 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/PreambleLineNumberStackTrace.java
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/PreambleLineNumberStackTrace.java
@@ -33,8 +33,7 @@
     return Arrays.asList(
         "Exception in thread \"main\" java.lang.NullPointerException",
         "  at kotlin.ResultKt.createFailure(Result.kt)",
-        // TODO(b/270593835): We should report kotlin.ResultKt.createFailure(Result.kt).
-        "  at kotlin.ResultKt.a(Result.kt:0)",
+        "  at kotlin.ResultKt.createFailure(Result.kt:0)",
         "  at kotlin.ResultKt.createFailure(Result.kt:122)",
         "  at kotlin.ResultKt.createFailure(Result.kt:124)");
   }
@@ -43,11 +42,8 @@
   public List<String> retraceVerboseStackTrace() {
     return Arrays.asList(
         "Exception in thread \"main\" java.lang.NullPointerException",
-        // TODO(b/270593835): We should not have an ambiguous frame reporting here.
-        "  at kotlin.ResultKt.void createFailure(java.lang.Throwable)(Result.kt:122)",
-        "  <OR> at kotlin.ResultKt.void createFailure(java.lang.Throwable)(Result.kt:124)",
-        // TODO(b/270593835): We should report kotlin.ResultKt.createFailure(Result.kt).
-        "  at kotlin.ResultKt.a(Result.kt:0)",
+        "  at kotlin.ResultKt.void createFailure(java.lang.Throwable)(Result.kt)",
+        "  at kotlin.ResultKt.void createFailure(java.lang.Throwable)(Result.kt:0)",
         "  at kotlin.ResultKt.void createFailure(java.lang.Throwable)(Result.kt:122)",
         "  at kotlin.ResultKt.void createFailure(java.lang.Throwable)(Result.kt:124)");
   }
diff --git a/src/test/java/com/android/tools/r8/synthesis/RepeatedCompilationNestedSyntheticsTest.java b/src/test/java/com/android/tools/r8/synthesis/RepeatedCompilationNestedSyntheticsTest.java
index e273552..b18935b 100644
--- a/src/test/java/com/android/tools/r8/synthesis/RepeatedCompilationNestedSyntheticsTest.java
+++ b/src/test/java/com/android/tools/r8/synthesis/RepeatedCompilationNestedSyntheticsTest.java
@@ -10,11 +10,10 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
+import com.android.tools.r8.ByteDataView;
 import com.android.tools.r8.ClassFileConsumer;
-import com.android.tools.r8.ClassFileConsumerData;
 import com.android.tools.r8.DesugarGraphConsumer;
 import com.android.tools.r8.DexFilePerClassFileConsumer;
-import com.android.tools.r8.DexFilePerClassFileConsumerData;
 import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
@@ -70,8 +69,8 @@
         .setProgramConsumer(
             new ClassFileConsumer() {
               @Override
-              public void acceptClassFile(ClassFileConsumerData data) {
-                firstCompilation.put(data.getClassDescriptor(), data.getByteDataCopy());
+              public void accept(ByteDataView data, String descriptor, DiagnosticsHandler handler) {
+                firstCompilation.put(descriptor, data.copyByteData());
               }
 
               @Override
@@ -122,9 +121,10 @@
                   b.setProgramConsumer(
                       new ClassFileConsumer() {
                         @Override
-                        public void acceptClassFile(ClassFileConsumerData data) {
-                          secondCompilation.put(data.getClassDescriptor(), data.getByteDataCopy());
-                          allDescriptors.add(data.getClassDescriptor());
+                        public void accept(
+                            ByteDataView data, String descriptor, DiagnosticsHandler handler) {
+                          secondCompilation.put(descriptor, data.copyByteData());
+                          allDescriptors.add(descriptor);
                         }
 
                         @Override
@@ -133,12 +133,15 @@
               b ->
                   b.setProgramConsumer(
                       new DexFilePerClassFileConsumer() {
+
                         @Override
-                        public synchronized void acceptDexFile(
-                            DexFilePerClassFileConsumerData data) {
-                          secondCompilation.put(
-                              data.getPrimaryClassDescriptor(), data.getByteDataCopy());
-                          allDescriptors.addAll(data.getClassDescriptors());
+                        public void accept(
+                            String primaryClassDescriptor,
+                            ByteDataView data,
+                            Set<String> descriptors,
+                            DiagnosticsHandler handler) {
+                          secondCompilation.put(primaryClassDescriptor, data.copyByteData());
+                          allDescriptors.addAll(descriptors);
                         }
 
                         @Override
diff --git a/src/test/java/com/android/tools/r8/synthesis/RepeatedCompilationSyntheticsTest.java b/src/test/java/com/android/tools/r8/synthesis/RepeatedCompilationSyntheticsTest.java
index 842c56e..e7e5c3c 100644
--- a/src/test/java/com/android/tools/r8/synthesis/RepeatedCompilationSyntheticsTest.java
+++ b/src/test/java/com/android/tools/r8/synthesis/RepeatedCompilationSyntheticsTest.java
@@ -10,7 +10,6 @@
 import com.android.tools.r8.ByteDataView;
 import com.android.tools.r8.ClassFileConsumer;
 import com.android.tools.r8.DexFilePerClassFileConsumer;
-import com.android.tools.r8.DexFilePerClassFileConsumerData;
 import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
@@ -23,6 +22,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.Set;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -113,9 +113,12 @@
                   b.setProgramConsumer(
                       new DexFilePerClassFileConsumer() {
                         @Override
-                        public synchronized void acceptDexFile(
-                            DexFilePerClassFileConsumerData data) {
-                          secondCompilation.addAll(data.getClassDescriptors());
+                        public void accept(
+                            String primaryClassDescriptor,
+                            ByteDataView data,
+                            Set<String> descriptors,
+                            DiagnosticsHandler handler) {
+                          secondCompilation.addAll(descriptors);
                         }
 
                         @Override
diff --git a/src/test/java/com/android/tools/r8/utils/Smali.java b/src/test/java/com/android/tools/r8/utils/Smali.java
index 4190da5..4321812 100644
--- a/src/test/java/com/android/tools/r8/utils/Smali.java
+++ b/src/test/java/com/android/tools/r8/utils/Smali.java
@@ -3,8 +3,8 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.utils;
 
+import com.android.tools.r8.ByteDataView;
 import com.android.tools.r8.DexIndexedConsumer;
-import com.android.tools.r8.DexIndexedConsumerData;
 import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.dex.ApplicationReader;
 import com.android.tools.r8.dex.ApplicationWriter;
@@ -19,6 +19,7 @@
 import java.io.StringReader;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import org.antlr.runtime.CommonTokenStream;
@@ -128,8 +129,9 @@
     byte[] contents;
 
     @Override
-    public void acceptDexIndexedFile(DexIndexedConsumerData data) {
-      contents = data.getByteDataCopy();
+    public void accept(
+        int fileIndex, ByteDataView data, Set<String> descriptors, DiagnosticsHandler handler) {
+      contents = data.copyByteData();
     }
 
     @Override
diff --git a/src/test/java/com/android/tools/r8/utils/StringUtilsTest.java b/src/test/java/com/android/tools/r8/utils/StringUtilsTest.java
index acadcc6..2237b07 100644
--- a/src/test/java/com/android/tools/r8/utils/StringUtilsTest.java
+++ b/src/test/java/com/android/tools/r8/utils/StringUtilsTest.java
@@ -3,7 +3,9 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.utils;
 
+import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertSame;
 
 import com.android.tools.r8.TestBase;
@@ -47,6 +49,18 @@
     assertListEquals(ImmutableList.of("\ra\r\rb\r"), StringUtils.splitLines("\ra\r\rb\r"));
   }
 
+  @Test
+  public void testSplit() {
+    assertListEquals(ImmutableList.of(" "), StringUtils.split(" ", '.'));
+    assertListEquals(ImmutableList.of("", ""), StringUtils.split(".", '.'));
+    assertListEquals(ImmutableList.of("", "", "", ""), StringUtils.split("...", '.'));
+    assertListEquals(ImmutableList.of("a", "b", "c", "d"), StringUtils.split("a.b.c.d", '.'));
+
+    assertArrayEquals(new String[] {"a", "b", "c"}, StringUtils.splitKnownSize("a.b.c", '.', 3));
+    assertNull(StringUtils.splitKnownSize("a.b.c", '.', 2));
+    assertNull(StringUtils.splitKnownSize("a.b.c", '.', 4));
+  }
+
   private void assertListEquals(List<String> xs, List<String> ys) {
     assertEquals(
         StringUtils.join(", ", xs, s -> '"' + StringUtils.toASCIIString(s) + '"', BraceType.SQUARE),
diff --git a/third_party/binary_compatibility_tests/compiler_api_tests.tar.gz.sha1 b/third_party/binary_compatibility_tests/compiler_api_tests.tar.gz.sha1
index e2f9b98..8a2887d 100644
--- a/third_party/binary_compatibility_tests/compiler_api_tests.tar.gz.sha1
+++ b/third_party/binary_compatibility_tests/compiler_api_tests.tar.gz.sha1
@@ -1 +1 @@
-0f355ce5bfc00d39fc533575bb119053b0d8cfc4
\ No newline at end of file
+42f88c3f809026d506e15c14804dd584bc0b9474
\ No newline at end of file