Merge commit '327a5d2d6d430c000dcde28f4acbf8764e90716b' into dev-release

Change-Id: If742c0288a29f4a6f5494e52e4d635867e3ef251
diff --git a/.gitignore b/.gitignore
index 4496e10..4008f3e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -96,8 +96,20 @@
 third_party/dependencies_new.tar.gz
 third_party/desugar/desugar_*.tar.gz
 third_party/desugar/desugar_*/
+third_party/examples
+third_party/examples.tar.gz
+third_party/examplesAndroidN
+third_party/examplesAndroidN.tar.gz
+third_party/examplesAndroidO
+third_party/examplesAndroidO.tar.gz
+third_party/examplesAndroidOGenerated
+third_party/examplesAndroidOGenerated.tar.gz
 third_party/examplesAndroidOLegacy
 third_party/examplesAndroidOLegacy.tar.gz
+third_party/examplesAndroidP
+third_party/examplesAndroidP.tar.gz
+third_party/examplesAndroidPGenerated
+third_party/examplesAndroidPGenerated.tar.gz
 third_party/framework
 third_party/framework.tar.gz
 third_party/gmail/*
@@ -155,6 +167,8 @@
 third_party/kotlin/kotlin-compiler-dev
 third_party/kotlinx-coroutines-1.3.6.tar.gz
 third_party/kotlinx-coroutines-1.3.6
+third_party/kotlinR8TestResources
+third_party/kotlinR8TestResources.tar.gz
 third_party/multidex
 third_party/multidex.tar.gz
 third_party/nest/*
diff --git a/build.gradle b/build.gradle
index 41d7c5c..9316f6d 100644
--- a/build.gradle
+++ b/build.gradle
@@ -105,12 +105,6 @@
         }
         output.resourcesDir = 'build/classes/debugTestResourcesJava8'
     }
-    examples {
-        java {
-            srcDirs = ['src/test/examples']
-        }
-        output.resourcesDir = 'build/classes/examples'
-    }
     examplesJava9 {
         java {
             srcDirs = ['src/test/examplesJava9']
@@ -136,30 +130,6 @@
             srcDirs = ['src/test/examplesJava20']
         }
     }
-    examplesAndroidN {
-        java {
-            srcDirs = ['src/test/examplesAndroidN']
-        }
-        output.resourcesDir = 'build/classes/examplesAndroidN'
-    }
-    examplesAndroidO {
-        java {
-            srcDirs = ['src/test/examplesAndroidO']
-        }
-        output.resourcesDir = 'build/classes/examplesAndroidO'
-    }
-    examplesAndroidP {
-        java {
-            srcDirs = ['src/test/examplesAndroidP']
-        }
-        output.resourcesDir = 'build/classes/examplesAndroidP'
-    }
-    kotlinR8TestResources {
-        java {
-            srcDirs = ['src/test/kotlinR8TestResources']
-        }
-        output.resourcesDir = 'build/classes/kotlinR8TestResources'
-    }
     keepanno {
         java {
             srcDirs = ['src/keepanno/java']
@@ -225,7 +195,6 @@
     main17Implementation group: 'org.ow2.asm', name: 'asm-analysis', version: asmVersion
     main17Implementation group: 'org.ow2.asm', name: 'asm-util', version: asmVersion
 
-    testCompile sourceSets.examples.output
     testCompile "junit:junit:$junitVersion"
     testCompile "com.google.guava:guava:$guavaVersion"
     testCompile "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
@@ -242,18 +211,8 @@
     testCompile group: 'it.unimi.dsi', name: 'fastutil', version: fastutilVersion
     testCompile group: 'org.javassist', name: 'javassist', version: javassistVersion
 
-    examplesAndroidOCompile group: 'org.ow2.asm', name: 'asm', version: asmVersion
-    examplesAndroidOCompile files("third_party/android_jar/lib-v26/android.jar")
-    examplesAndroidOCompile files("third_party/examplesAndroidOLegacy")
-
-    examplesAndroidPCompile group: 'org.ow2.asm', name: 'asm', version: asmVersion
-    // Import Guava for @Nullable annotation
-    examplesCompile "com.google.guava:guava:$guavaVersion"
-    examplesCompile "junit:junit:$junitVersion"
-    examplesCompile "org.mockito:mockito-core:$mockitoVersion"
     apiUsageSampleCompile sourceSets.main.output
     apiUsageSampleCompile "com.google.guava:guava:$guavaVersion"
-    kotlinR8TestResourcesCompileOnly "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
     errorprone("com.google.errorprone:error_prone_core:$errorproneVersion")
 
     keepannoCompile group: 'org.ow2.asm', name: 'asm', version: asmVersion
@@ -307,7 +266,13 @@
                 "dagger/2.41",
                 "dart-sdk",
                 "ddmlib",
+                "examples",
+                "examplesAndroidN",
+                "examplesAndroidO",
                 "examplesAndroidOLegacy",
+                "examplesAndroidOGenerated",
+                "examplesAndroidP",
+                "examplesAndroidPGenerated",
                 "gradle/gradle",
                 "google/google-java-format/1.14.0",
                 "google-java-format",
@@ -329,6 +294,7 @@
                 "kotlin/kotlin-compiler-1.7.0",
                 "kotlin/kotlin-compiler-1.8.0",
                 "kotlinx-coroutines-1.3.6",
+                "kotlinR8TestResources",
                 "multidex",
                 "openjdk/custom_conversion",
                 "openjdk/openjdk-rt-1.8",
@@ -947,7 +913,8 @@
     return baseD8CommandLine(allArgs)
 }
 
-def r8LibCreateTask(name, pgConfs = [], r8Task, output, libs = [], classpath = [], excldeps=false) {
+def r8LibCreateTask(
+    name, pgConfs = [], r8Task, output, libs = [], classpath = [], excldeps=false, debug=false) {
     return tasks.create("r8Lib${name}", Exec) {
         inputs.files ([pgConfs, r8WithRelocatedDeps.outputs, r8Task.outputs])
         outputs.file output
@@ -959,6 +926,7 @@
                 "--r8jar", r8Task.outputs.files[0],
                 "--output", output]
                 + (excldeps ? ['--excldeps-variant'] : [])
+                + (debug ? ['--debug-variant'] : [])
                 + (pgConfs.collectMany { ["--pg-conf", it] })
                 + (libs.collectMany { ["--lib", it] })
                 + (classpath.collectMany { ["--classpath", it] }))
@@ -1071,6 +1039,10 @@
             ["src/main/keep_retrace.txt"],
             R8Lib,
             r8RetracePath,
+            [],
+            [],
+            false,
+            true
     ).dependsOn(R8Lib)
     outputs.file r8RetracePath
 }
@@ -1085,6 +1057,7 @@
             [],
             repackageDeps.outputs.files,
             true,
+            true,
     ).dependsOn(R8LibNoDeps)
     outputs.file r8RetraceExludeDepsPath
 }
@@ -1133,211 +1106,6 @@
     dependsOn buildR8ApiUsageSample
 }
 
-// Proto lite generated code yields warnings when compiling with javac.
-// We change the options passed to javac to ignore it.
-compileExamplesJava.options.compilerArgs = ["-Xlint:none"]
-
-
-task buildExampleJars {
-    def examplesDir = file("src/test/examples")
-    task extractExamplesRuntime(type: Sync) {
-        dependsOn configurations.examplesRuntime
-        from { configurations.examplesRuntime.collect { zipTree(it) } }
-        include "**/*.class"
-        includeEmptyDirs false
-        into "$buildDir/runtime/examples/"
-    }
-
-    task "copy_examples_resources"(type: org.gradle.api.tasks.Copy) {
-        from examplesDir
-        exclude "**/*.java"
-        exclude "**/keep-rules*.txt"
-        into file("build/test/examples/classes")
-    }
-
-    task "compile_examples"(type: JavaCompile) {
-        dependsOn "copy_examples_resources"
-        source examplesDir
-        include "**/*.java"
-        destinationDir = file("build/test/examples/classes")
-        classpath = sourceSets.examples.compileClasspath
-        sourceCompatibility = JavaVersion.VERSION_1_7
-        targetCompatibility = JavaVersion.VERSION_1_7
-        options.compilerArgs = ["-g:source,lines", "-Xlint:none"]
-    }
-    task "compile_examples_debuginfo_all"(type: JavaCompile) {
-        source examplesDir
-        include "**/*.java"
-        destinationDir = file("build/test/examples/classes_debuginfo_all")
-        classpath = sourceSets.examples.compileClasspath
-        sourceCompatibility = JavaVersion.VERSION_1_7
-        targetCompatibility = JavaVersion.VERSION_1_7
-        options.compilerArgs = ["-g", "-Xlint:none"]
-    }
-    task "compile_examples_debuginfo_none"(type: JavaCompile) {
-        source examplesDir
-        include "**/*.java"
-        destinationDir = file("build/test/examples/classes_debuginfo_none")
-        classpath = sourceSets.examples.compileClasspath
-        sourceCompatibility = JavaVersion.VERSION_1_7
-        targetCompatibility = JavaVersion.VERSION_1_7
-        options.compilerArgs = ["-g:none", "-Xlint:none"]
-    }
-    examplesDir.eachDir { dir ->
-        def name = dir.getName();
-        def exampleOutputDir = file("build/test/examples");
-        def jarName = "${name}.jar"
-        dependsOn "jar_example_${name}"
-        dependsOn "jar_example_${name}_debuginfo_all"
-        dependsOn "jar_example_${name}_debuginfo_none"
-        dependsOn "extractExamplesRuntime"
-        def runtimeDependencies = copySpec { }
-        task "jar_example_${name}"(type: Jar, dependsOn: "compile_examples") {
-            archiveName = "${name}.jar"
-            destinationDir = exampleOutputDir
-            from "build/test/examples/classes"
-            include name + "/**/*"
-            with runtimeDependencies
-            includeEmptyDirs true
-        }
-        task "jar_example_${name}_debuginfo_all"(type: Jar, dependsOn: "compile_examples_debuginfo_all") {
-            archiveName = "${name}_debuginfo_all.jar"
-            destinationDir = exampleOutputDir
-            from "build/test/examples/classes_debuginfo_all"
-            include name + "/**/*.class"
-            with runtimeDependencies
-            includeEmptyDirs false
-        }
-        task "jar_example_${name}_debuginfo_none"(type: Jar, dependsOn: "compile_examples_debuginfo_none") {
-            archiveName = "${name}_debuginfo_none.jar"
-            destinationDir = exampleOutputDir
-            from "build/test/examples/classes_debuginfo_none"
-            include name + "/**/*.class"
-            with runtimeDependencies
-            includeEmptyDirs false
-        }
-    }
-}
-
-task buildExampleAndroidNJars {
-    dependsOn downloadDeps
-    def examplesDir = file("src/test/examplesAndroidN")
-    task "compile_examplesAndroidN"(type: JavaCompile) {
-        source = fileTree(dir: examplesDir, include: '**/*.java')
-        destinationDir = file("build/test/examplesAndroidN/classes")
-        classpath = sourceSets.main.compileClasspath
-        sourceCompatibility = JavaVersion.VERSION_1_8
-        targetCompatibility = JavaVersion.VERSION_1_8
-        options.compilerArgs += ["-Xlint:-options"]
-    }
-    examplesDir.eachDir { dir ->
-        def name = dir.getName();
-        def exampleOutputDir = file("build/test/examplesAndroidN");
-        def jarName = "${name}.jar"
-        dependsOn "jar_examplesAndroidN_${name}"
-        task "jar_examplesAndroidN_${name}"(type: Jar, dependsOn: "compile_examplesAndroidN") {
-            archiveName = jarName
-            destinationDir = exampleOutputDir
-            from "build/test/examplesAndroidN/classes"
-            include "**/" + name + "/**/*.class"
-        }
-    }
-}
-
-
-task buildExampleAndroidOJars {
-    dependsOn downloadDeps
-    def examplesDir = file("src/test/examplesAndroidO")
-    // Compiling the rest of the files as Java 1.8 code.
-    task "compile_examplesAndroidO"(type: JavaCompile) {
-        source = fileTree(dir: examplesDir, include: '**/*.java', exclude: '**/legacy/**/*.java')
-        destinationDir = file("build/test/examplesAndroidO/classes")
-        classpath = sourceSets.main.compileClasspath
-        classpath += files("build/test/examplesAndroidOLegacy/classes")
-        classpath += files("third_party/android_jar/lib-v26/android.jar")
-        classpath += files("third_party/examplesAndroidOLegacy")
-        sourceCompatibility = JavaVersion.VERSION_1_8
-        targetCompatibility = JavaVersion.VERSION_1_8
-        options.compilerArgs += ["-Xlint:-options", "-parameters"]
-    }
-    examplesDir.eachDir { dir ->
-        def name = dir.getName();
-        def destinationDir = file("build/test/examplesAndroidO/classes");
-        if (file("src/test/examplesAndroidO/" + name + "/TestGenerator.java").isFile()) {
-            task "generate_examplesAndroidO_${name}"(type: JavaExec,
-                    dependsOn: "compile_examplesAndroidO") {
-                main = name + ".TestGenerator"
-                classpath = files(destinationDir, sourceSets.main.compileClasspath)
-                args destinationDir, file("build/test/examplesAndroidO/classes/${name}").toString()
-            }
-        } else {
-            task "generate_examplesAndroidO_${name}" () {}
-        }
-    }
-    examplesDir.eachDir { dir ->
-        def name = dir.getName();
-        def exampleOutputDir = file("build/test/examplesAndroidO");
-        def jarName = "${name}.jar"
-        dependsOn "jar_examplesAndroidO_${name}"
-        task "jar_examplesAndroidO_${name}"(type: Jar, dependsOn: ["compile_examplesAndroidO",
-                "generate_examplesAndroidO_${name}"]) {
-            archiveName = jarName
-            destinationDir = exampleOutputDir
-            from "build/test/examplesAndroidO/classes"        // Java 1.8 classes
-            from "build/test/examplesAndroidOLegacy/classes"  // Java 1.6 classes
-            include "**/" + name + "/**/*.class"
-            // Do not include generator into the test runtime jar, it is not useful.
-            // Otherwise, shrinking will need ASM jars.
-            exclude "**/TestGenerator*"
-        }
-    }
-}
-
-task buildExampleAndroidPJars {
-    dependsOn downloadDeps
-    def examplesDir = file("src/test/examplesAndroidP")
-
-    task "compile_examplesAndroidP"(type: JavaCompile) {
-        source = fileTree(dir: examplesDir, include: '**/*.java')
-        destinationDir = file("build/test/examplesAndroidP/classes")
-        classpath = sourceSets.main.compileClasspath
-        sourceCompatibility = JavaVersion.VERSION_1_8
-        targetCompatibility = JavaVersion.VERSION_1_8
-        options.compilerArgs += ["-Xlint:-options"]
-    }
-    examplesDir.eachDir { dir ->
-        def name = dir.getName();
-        def destinationDir = file("build/test/examplesAndroidP/classes");
-        if (file("src/test/examplesAndroidP/" + name + "/TestGenerator.java").isFile()) {
-            task "generate_examplesAndroidP_${name}"(type: JavaExec,
-                    dependsOn: "compile_examplesAndroidP") {
-                main = name + ".TestGenerator"
-                classpath = files(destinationDir.toString(), sourceSets.main.compileClasspath)
-                args destinationDir, file("build/test/examplesAndroidP/classes/${name}").toString()
-            }
-        } else {
-            task "generate_examplesAndroidP_${name}" () {}
-        }
-    }
-    examplesDir.eachDir { dir ->
-        def name = dir.getName();
-        def exampleOutputDir = file("build/test/examplesAndroidP");
-        def jarName = "${name}.jar"
-        dependsOn "jar_examplesAndroidP_${name}"
-        task "jar_examplesAndroidP_${name}"(type: Jar,
-                dependsOn: ["compile_examplesAndroidP",
-                            "generate_examplesAndroidP_${name}"]) {
-            archiveName = jarName
-            destinationDir = exampleOutputDir
-            from "build/test/examplesAndroidP/classes"  // Java 1.8 classes
-            include "**/" + name + "/**/*.class"
-            // Do not include generator into the test runtime jar, it is not useful.
-            // Otherwise, shrinking will need ASM jars.
-            exclude "**/TestGenerator*"
-        }
-    }
-}
-
 def buildExampleJarsCreateTask(javaVersion, sourceSet) {
     return tasks.create("buildExample${javaVersion}Jars") {
         def examplesDir = file("src/test/examples${javaVersion}")
@@ -1362,46 +1130,6 @@
 buildExampleJarsCreateTask("Java17", sourceSets.examplesJava17)
 buildExampleJarsCreateTask("Java20", sourceSets.examplesJava20)
 
-task provideArtFrameworksDependencies {
-    cloudDependencies.tools.forEach({ art ->
-        if (art.contains("art")) {
-            def taskName = art.replace('/','_')
-            dependsOn "patch_${taskName}"
-            task "patch_${taskName}"(type: org.gradle.api.tasks.Copy){
-                from "tools/${art}/framework"
-                include "**.jar"
-                into file("tools/${art}/out/host/linux-x86/framework")
-            }
-        }
-    })
-}
-
-task buildKotlinR8TestResources {
-    def examplesDir = file("src/test/kotlinR8TestResources")
-    examplesDir.eachDir { dir ->
-        def name = dir.getName()
-        def taskName = "jar_kotlinR8TestResources_${name}"
-        def javaOutput = "build/test/kotlinR8TestResources/${name}/java"
-        def javaOutputJarName = "${name}.jar"
-        def javaOutputJarDir = "build/test/kotlinR8TestResources"
-        task "${taskName}Java"(type: JavaCompile) {
-            source = fileTree(dir: file("${examplesDir}/${name}"), include: '**/*.java')
-            destinationDir = file(javaOutput)
-            classpath = sourceSets.main.compileClasspath
-            sourceCompatibility = JavaVersion.VERSION_1_8
-            targetCompatibility = JavaVersion.VERSION_1_8
-            options.compilerArgs += ["-g", "-Xlint:-options"]
-        }
-        task "${taskName}JavaJar"(type: Jar, dependsOn: "${taskName}Java") {
-            archiveName = javaOutputJarName
-            destinationDir = file(javaOutputJarDir)
-            from javaOutput
-            include "**/*.class"
-        }
-        dependsOn "${taskName}JavaJar"
-    }
-}
-
 task buildExamples {
     if (OperatingSystem.current().isMacOsX() || OperatingSystem.current().isWindows()) {
         logger.lifecycle("WARNING: Testing (including building examples) is only partially supported on your " +
@@ -1411,22 +1139,11 @@
           "It is fully supported on Linux and partially supported on Mac OS and Windows")
       return;
     }
-    dependsOn buildExampleJars
-    dependsOn buildExampleAndroidNJars
-    dependsOn buildExampleAndroidOJars
-    dependsOn buildExampleAndroidPJars
     dependsOn buildExampleJava9Jars
     dependsOn buildExampleJava10Jars
     dependsOn buildExampleJava11Jars
     dependsOn buildExampleJava17Jars
     dependsOn buildExampleJava20Jars
-    def examplesDir = file("src/test/examples")
-    examplesDir.eachDir { dir ->
-        def exampleOutputDir = file("build/test/examples/" + dir.getName());
-        if (!exampleOutputDir.exists()) {
-          exampleOutputDir.mkdirs()
-        }
-    }
 }
 
 tasks.withType(Test) {
@@ -1512,8 +1229,10 @@
     delete r8LibTestPath
     from zipTree(buildR8LibCfTestDeps.outputs.files[0])
     def examplesDir = file("build/test")
-    examplesDir.eachDir { dir ->
-        from ("${buildDir}/test/${dir.getName()}/classes")
+    if (examplesDir.exists()) {
+        examplesDir.eachDir { dir ->
+            from ("${buildDir}/test/${dir.getName()}/classes")
+        }
     }
     from ("${buildDir}/runtime/examples")
     into r8LibTestPath
@@ -2023,9 +1742,7 @@
         }
         dependsOn downloadDeps
         dependsOn buildExamples
-        dependsOn buildKotlinR8TestResources
         dependsOn buildPreNJdwpTestsJar
-        dependsOn provideArtFrameworksDependencies
     } else {
         logger.lifecycle("WARNING: Testing in not supported on your platform. Testing is only fully supported on " +
             "Linux and partially supported on Mac OS and Windows. Art does not run on other platforms.")
diff --git a/d8_r8/commonBuildSrc/src/main/kotlin/DependenciesPlugin.kt b/d8_r8/commonBuildSrc/src/main/kotlin/DependenciesPlugin.kt
index e396985..2214408 100644
--- a/d8_r8/commonBuildSrc/src/main/kotlin/DependenciesPlugin.kt
+++ b/d8_r8/commonBuildSrc/src/main/kotlin/DependenciesPlugin.kt
@@ -6,6 +6,7 @@
 import java.io.File
 import java.net.URI
 import java.nio.file.Paths
+import kotlin.reflect.full.declaredMemberProperties
 import org.gradle.api.JavaVersion
 import org.gradle.api.Plugin
 import org.gradle.api.Project
@@ -86,24 +87,6 @@
   return "****** ${title} ******"
 }
 
-fun Project.ensureThirdPartyDependencies(name : String, deps : List<ThirdPartyDependency>) : Task {
-  val outputFiles : MutableList<File> = mutableListOf()
-  val root = getRoot()
-  val depsTasks = deps.map { tpd ->
-    val projectAndTaskName = "${project.name}-$name"
-    val downloadTaskName = "download-third-party-$projectAndTaskName-${tpd.packageName}"
-    val downloadTask = tasks.register<DownloadDependencyTask>(downloadTaskName) {
-      setDependency(root.resolve(tpd.sha1File), root.resolve(tpd.path), tpd.type, root)
-    }.get()
-    outputFiles.add(tpd.path)
-    downloadTask
-  }
-  return tasks.register("ensure-third-party-$name") {
-    dependsOn(*depsTasks.toTypedArray())
-    outputs.files(outputFiles)
-  }.get()
-}
-
 /**
  * Builds a jar for each sub folder in a test source set.
  *
@@ -274,23 +257,34 @@
   output: File,
   pgConf: List<File>,
   excludingDepsVariant: Boolean,
+  debugVariant: Boolean,
   lib: List<File> = listOf(),
-  classpath: List<File> = listOf()
+  classpath: List<File> = listOf(),
+  pgInputMap: File? = null
 ) : List<String> {
-  val pgList = pgConf.flatMap({ listOf("--pg-conf", "$it") })
-  val libList = lib.flatMap({ listOf("--lib", "$it") })
-  val cpList = classpath.flatMap({ listOf("--classpath", "$it") })
-  val exclList = if (excludingDepsVariant) listOf("--excldeps-variant") else listOf()
-  return listOf(
-    "python3",
-    "${getRoot().resolve("tools").resolve("create_r8lib.py")}",
-    "--r8compiler",
-    "${r8Compiler}",
-    "--r8jar",
-    "${input}",
-    "--output",
-    "${output}",
-  ) + exclList + pgList + libList + cpList
+  return buildList {
+    add("python3")
+    add("${getRoot().resolve("tools").resolve("create_r8lib.py")}")
+    add("--r8compiler")
+    add("${r8Compiler}")
+    add("--r8jar")
+    add("${input}")
+    add("--output")
+    add("${output}")
+    pgConf.forEach { add("--pg-conf"); add("$it") }
+    lib.forEach { add("--lib"); add("$it") }
+    classpath.forEach { add("--classpath"); add("$it") }
+    if (excludingDepsVariant) {
+      add("--excldeps-variant")
+    }
+    if (debugVariant) {
+      add("--debug-variant")
+    }
+    if (pgInputMap != null) {
+      add("--pg-map")
+      add("$pgInputMap")
+    }
+  }
 }
 
 object JvmCompatibility {
@@ -379,10 +373,34 @@
     "ddmlib",
     Paths.get("third_party", "ddmlib").toFile(),
     Paths.get("third_party", "ddmlib.tar.gz.sha1").toFile())
+  val examples = ThirdPartyDependency(
+    "examples",
+    Paths.get("third_party", "examples").toFile(),
+    Paths.get("third_party", "examples.tar.gz.sha1").toFile())
+  val examplesAndroidN = ThirdPartyDependency(
+    "examplesAndroidN",
+    Paths.get("third_party", "examplesAndroidN").toFile(),
+    Paths.get("third_party", "examplesAndroidN.tar.gz.sha1").toFile())
+  val examplesAndroidO = ThirdPartyDependency(
+    "examplesAndroidO",
+    Paths.get("third_party", "examplesAndroidO").toFile(),
+    Paths.get("third_party", "examplesAndroidO.tar.gz.sha1").toFile())
+  val examplesAndroidOGenerated = ThirdPartyDependency(
+    "examplesAndroidOGenerated",
+    Paths.get("third_party", "examplesAndroidOGenerated").toFile(),
+    Paths.get("third_party", "examplesAndroidOGenerated.tar.gz.sha1").toFile())
   val examplesAndroidOLegacy = ThirdPartyDependency(
     "examplesAndroidOLegacy",
     Paths.get("third_party", "examplesAndroidOLegacy").toFile(),
     Paths.get("third_party", "examplesAndroidOLegacy.tar.gz.sha1").toFile())
+  val examplesAndroidP = ThirdPartyDependency(
+    "examplesAndroidP",
+    Paths.get("third_party", "examplesAndroidP").toFile(),
+    Paths.get("third_party", "examplesAndroidP.tar.gz.sha1").toFile())
+  val examplesAndroidPGenerated = ThirdPartyDependency(
+    "examplesAndroidPGenerated",
+    Paths.get("third_party", "examplesAndroidPGenerated").toFile(),
+    Paths.get("third_party", "examplesAndroidPGenerated.tar.gz.sha1").toFile())
   val desugarJdkLibs = ThirdPartyDependency(
     "desugar-jdk-libs",
     Paths.get("third_party", "openjdk", "desugar_jdk_libs").toFile(),
@@ -402,6 +420,10 @@
     "gson",
     Paths.get("third_party", "gson", "gson-2.10.1").toFile(),
     Paths.get("third_party", "gson", "gson-2.10.1.tar.gz.sha1").toFile())
+  val guavaJre = ThirdPartyDependency(
+    "guava-jre",
+    Paths.get("third_party", "guava", "guava-32.1.2-jre").toFile(),
+    Paths.get("third_party", "guava", "guava-32.1.2-jre.tar.gz.sha1").toFile())
   val desugarJdkLibs11 = ThirdPartyDependency(
     "desugar-jdk-libs-11",
     Paths.get("third_party", "openjdk", "desugar_jdk_libs_11").toFile(),
@@ -434,6 +456,10 @@
     Paths.get("third_party", "jdwp-tests").toFile(),
     Paths.get("third_party", "jdwp-tests.tar.gz.sha1").toFile())
   val kotlinCompilers = getThirdPartyKotlinCompilers()
+  val kotlinR8TestResources = ThirdPartyDependency(
+    "kotlinR8TestResources",
+    Paths.get("third_party", "kotlinR8TestResources").toFile(),
+    Paths.get("third_party", "kotlinR8TestResources.tar.gz.sha1").toFile())
   val multidex = ThirdPartyDependency(
     "multidex",
     Paths.get("third_party", "multidex").toFile(),
@@ -629,47 +655,23 @@
       DependencyType.X20)}
 }
 
-val testRuntimeDependencies = (listOf(
-  ThirdPartyDeps.aapt2,
-  ThirdPartyDeps.artTests,
-  ThirdPartyDeps.artTestsLegacy,
-  ThirdPartyDeps.compilerApi,
-  ThirdPartyDeps.coreLambdaStubs,
-  ThirdPartyDeps.customConversion,
-  ThirdPartyDeps.dagger,
-  ThirdPartyDeps.desugarJdkLibs,
-  ThirdPartyDeps.desugarJdkLibsLegacy,
-  ThirdPartyDeps.desugarJdkLibs11,
-  ThirdPartyDeps.examplesAndroidOLegacy,
-  ThirdPartyDeps.gson,
-  ThirdPartyDeps.jacoco,
-  ThirdPartyDeps.java8Runtime,
-  ThirdPartyDeps.jdk11Test,
-  ThirdPartyDeps.jsr223,
-  ThirdPartyDeps.multidex,
-  ThirdPartyDeps.r8,
-  ThirdPartyDeps.r8Mappings,
-  ThirdPartyDeps.r8v2_0_74,
-  ThirdPartyDeps.r8v3_2_54,
-  ThirdPartyDeps.retraceBenchmark,
-  ThirdPartyDeps.retraceBinaryCompatibility,
-  ThirdPartyDeps.rhino,
-  ThirdPartyDeps.rhinoAndroid,
-  ThirdPartyDeps.smali,
-  ThirdPartyDeps.tivi)
-    + ThirdPartyDeps.androidJars
-    + ThirdPartyDeps.androidVMs
-    + ThirdPartyDeps.desugarLibraryReleases
-    + ThirdPartyDeps.jdks
-    + ThirdPartyDeps.kotlinCompilers
-    + ThirdPartyDeps.proguards)
+private fun Project.allDependencies() : List<ThirdPartyDependency> {
+  val allDeps = mutableListOf<ThirdPartyDependency>()
+  ThirdPartyDeps::class.declaredMemberProperties.forEach {
+    val value = it.get(ThirdPartyDeps)
+    if (value is List<*>) {
+      allDeps.addAll(value as List<ThirdPartyDependency>)
+    } else {
+      allDeps.add(value as ThirdPartyDependency)
+    }
+  }
+  return allDeps
+}
 
-val testRuntimeInternalDependencies = (listOf(
-  ThirdPartyDeps.clank,
-  ThirdPartyDeps.framework,
-  ThirdPartyDeps.nest,
-  ThirdPartyDeps.proto,
-  ThirdPartyDeps.protobufLite,
-  ThirdPartyDeps.retraceInternal)
-  + ThirdPartyDeps.internalIssues
-  + ThirdPartyDeps.gmscoreVersions)
\ No newline at end of file
+fun Project.allPublicDependencies() : List<ThirdPartyDependency> {
+  return allDependencies().filter { x -> x.type == DependencyType.GOOGLE_STORAGE }
+}
+
+fun Project.allInternalDependencies() : List<ThirdPartyDependency> {
+  return allDependencies().filter { x -> x.type == DependencyType.X20 }
+}
diff --git a/d8_r8/commonBuildSrc/src/main/kotlin/DownloadAllDependenciesTask.kt b/d8_r8/commonBuildSrc/src/main/kotlin/DownloadAllDependenciesTask.kt
new file mode 100644
index 0000000..92c1af2
--- /dev/null
+++ b/d8_r8/commonBuildSrc/src/main/kotlin/DownloadAllDependenciesTask.kt
@@ -0,0 +1,158 @@
+// 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.
+
+import java.io.BufferedReader
+import java.io.File
+import java.io.IOException
+import java.io.InputStreamReader
+import java.nio.charset.StandardCharsets
+import java.util.Arrays
+import java.util.stream.Collectors
+import javax.inject.Inject
+import org.gradle.api.DefaultTask
+import org.gradle.api.file.RegularFileProperty
+import org.gradle.api.provider.Property
+import org.gradle.api.tasks.InputFiles
+import org.gradle.api.tasks.OutputDirectories
+import org.gradle.api.tasks.TaskAction
+import org.gradle.internal.os.OperatingSystem
+import org.gradle.workers.WorkAction
+import org.gradle.workers.WorkParameters
+import org.gradle.workers.WorkerExecutor
+
+abstract class DownloadAllDependenciesTask : DefaultTask() {
+
+  private var _root: File? = null
+  private var _thirdPartyDeps: List<ThirdPartyDependency>? = null;
+
+  @InputFiles
+  fun getInputFile(): List<File> {
+    return _thirdPartyDeps!!.map { _root!!.resolve(it.sha1File) }
+  }
+
+  @OutputDirectories
+  fun getOutputDir(): List<File> {
+    return _thirdPartyDeps!!.map { _root!!.resolve(it.path) }
+  }
+
+  @Inject
+  protected abstract fun getWorkerExecutor(): WorkerExecutor?
+
+  fun setDependencies(root: File, thirdPartyDeps: List<ThirdPartyDependency>) {
+    this._root = root
+    this._thirdPartyDeps = thirdPartyDeps;
+  }
+
+  @TaskAction
+  fun execute() {
+    val noIsolation = getWorkerExecutor()!!.noIsolation()
+    _thirdPartyDeps?.forEach {
+      val root = _root!!
+      val sha1File = root.resolve(it.sha1File)
+      val tarGzFile = sha1File.resolveSibling(sha1File.name.replace(".sha1", ""))
+      val outputDir = root.resolve(it.path)
+      if (!sha1File.exists()) {
+        throw RuntimeException("Missing sha1 file: $sha1File")
+      }
+      if (shouldExecute(outputDir, tarGzFile, sha1File)) {
+        println("Downloading ${it}")
+        noIsolation.submit(RunDownload::class.java) {
+          type.set(it.type)
+          this.sha1File.set(sha1File)
+          this.outputDir.set(outputDir)
+          this.tarGzFile.set(tarGzFile)
+          this.root.set(root)
+        }
+      }
+    }
+  }
+
+  interface RunDownloadParameters : WorkParameters {
+    val type : Property<DependencyType>
+    val sha1File : RegularFileProperty
+    val outputDir : RegularFileProperty
+    val tarGzFile : RegularFileProperty
+    val root : RegularFileProperty
+  }
+
+  abstract class RunDownload : WorkAction<RunDownloadParameters> {
+    override fun execute() {
+      val sha1File = parameters.sha1File.asFile.get()
+      val outputDir = parameters.outputDir.asFile.get()
+      val tarGzFile = parameters.tarGzFile.asFile.get()
+      if (!shouldExecute(outputDir, tarGzFile, sha1File)) {
+        return;
+      }
+      if (outputDir.exists() && outputDir.isDirectory) {
+        outputDir.delete()
+      }
+      when (parameters.type.get()) {
+        DependencyType.GOOGLE_STORAGE -> {
+          downloadFromGoogleStorage(parameters, sha1File)
+        }
+        DependencyType.X20 -> {
+          downloadFromX20(parameters, sha1File)
+        }
+      }
+    }
+
+    @Throws(IOException::class, InterruptedException::class)
+    private fun downloadFromGoogleStorage(parameters: RunDownloadParameters, sha1File: File) {
+      val args = Arrays.asList("-n", "-b", "r8-deps", "-s", "-u", sha1File.toString())
+      if (OperatingSystem.current().isWindows) {
+        val command: MutableList<String> = ArrayList()
+        command.add("download_from_google_storage.bat")
+        command.addAll(args)
+        runProcess(parameters, ProcessBuilder().command(command))
+      } else {
+        runProcess(
+          parameters,
+          ProcessBuilder()
+            .command("bash",
+                     "-c",
+                     "download_from_google_storage " + java.lang.String.join(" ", args)))
+      }
+    }
+
+    @Throws(IOException::class, InterruptedException::class)
+    private fun downloadFromX20(parameters: RunDownloadParameters, sha1File: File) {
+      if (OperatingSystem.current().isWindows) {
+        throw RuntimeException("Downloading from x20 unsupported on windows")
+      }
+      runProcess(parameters,
+        ProcessBuilder()
+          .command("bash", "-c", "tools/download_from_x20.py $sha1File"))
+    }
+
+    @Throws(IOException::class, InterruptedException::class)
+    private fun runProcess(parameters: RunDownloadParameters, builder: ProcessBuilder) {
+      builder.directory(parameters.root.asFile.get())
+      val command = java.lang.String.join(" ", builder.command())
+      val p = builder.start()
+      val exit = p.waitFor()
+      if (exit != 0) {
+        throw IOException("Process failed for $command\n"
+            + BufferedReader(
+            InputStreamReader(p.errorStream, StandardCharsets.UTF_8))
+            .lines()
+            .collect(Collectors.joining("\n")))
+      }
+    }
+  }
+
+  companion object {
+    fun shouldExecute(outputDir: File, tarGzFile: File, sha1File: File) : Boolean {
+      // First run will write the tar.gz file, causing the second run to still be out-of-date.
+      // Check if the modification time of the tar is newer than the sha in which case we are done.
+      if (outputDir.exists()
+        && outputDir.isDirectory
+        && outputDir.list()!!.isNotEmpty()
+        && tarGzFile.exists()
+        && sha1File.lastModified() <= tarGzFile.lastModified()) {
+        return false
+      }
+      return true
+    }
+  }
+}
\ No newline at end of file
diff --git a/d8_r8/commonBuildSrc/src/main/kotlin/DownloadDependencyTask.kt b/d8_r8/commonBuildSrc/src/main/kotlin/DownloadDependencyTask.kt
deleted file mode 100644
index 370bcad..0000000
--- a/d8_r8/commonBuildSrc/src/main/kotlin/DownloadDependencyTask.kt
+++ /dev/null
@@ -1,201 +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.
-
-import DependenciesPlugin.Companion.computeRoot
-import java.io.BufferedReader
-import java.io.File
-import java.io.IOException
-import java.io.InputStreamReader
-import java.io.RandomAccessFile
-import java.lang.Thread.sleep
-import java.nio.channels.FileChannel
-import java.nio.channels.FileLock
-import java.nio.channels.OverlappingFileLockException
-import java.nio.charset.StandardCharsets
-import java.util.Arrays
-import java.util.stream.Collectors
-import javax.inject.Inject
-import org.gradle.api.DefaultTask
-import org.gradle.api.file.RegularFileProperty
-import org.gradle.api.provider.Property
-import org.gradle.api.tasks.InputFile
-import org.gradle.api.tasks.OutputDirectory
-import org.gradle.api.tasks.TaskAction
-import org.gradle.api.tasks.options.Option
-import org.gradle.internal.os.OperatingSystem
-import org.gradle.workers.WorkAction
-import org.gradle.workers.WorkParameters
-import org.gradle.workers.WorkerExecutor
-
-abstract class DownloadDependencyTask : DefaultTask() {
-
-  private var dependencyType: DependencyType = DependencyType.GOOGLE_STORAGE
-  private var _outputDir: File? = null
-  private var _tarGzFile: File? = null
-  private var _sha1File: File? = null
-  private var _root: File? = null
-
-  @InputFile
-  fun getInputFile(): File? {
-    return _sha1File
-  }
-
-  @OutputDirectory
-  fun getOutputDir(): File? {
-    return _outputDir
-  }
-
-  @Inject
-  protected abstract fun getWorkerExecutor(): WorkerExecutor?
-
-  @Option(
-    option = "dependency",
-    description = "Sets the dependency information for a cloud stored file")
-  fun setDependency(sha1File: File, outputDir: File, dependencyType: DependencyType, root: File) {
-    _outputDir = outputDir
-    _sha1File = sha1File
-    _tarGzFile = sha1File.resolveSibling(sha1File.name.replace(".sha1", ""))
-    _root = root
-    this.dependencyType = dependencyType
-  }
-
-  @TaskAction
-  fun execute() {
-    val sha1File = _sha1File!!
-    val outputDir = _outputDir!!
-    val tarGzFile = _tarGzFile!!
-    if (!sha1File.exists()) {
-      throw RuntimeException("Missing sha1 file: $sha1File")
-    }
-    if (!shouldExecute(outputDir, tarGzFile, sha1File)) {
-      return
-    }
-    // Create a lock to ensure sequential a single downloader per third party dependency.
-    val lockFile = sha1File.parentFile.resolve(sha1File.name + ".download_deps_lock")
-    if (!lockFile.exists()) {
-      lockFile.createNewFile()
-    }
-    getWorkerExecutor()!!
-      .noIsolation()
-      .submit(RunDownload::class.java) {
-        type.set(dependencyType)
-        this.sha1File.set(sha1File)
-        this.outputDir.set(outputDir)
-        this.tarGzFile.set(tarGzFile)
-        this.lockFile.set(lockFile)
-        this.root.set(_root!!)
-      }
-  }
-
-  interface RunDownloadParameters : WorkParameters {
-    val type : Property<DependencyType>
-    val sha1File : RegularFileProperty
-    val outputDir : RegularFileProperty
-    val tarGzFile : RegularFileProperty
-    val lockFile : RegularFileProperty
-    val root : RegularFileProperty
-  }
-
-  abstract class RunDownload : WorkAction<RunDownloadParameters> {
-    override fun execute() {
-      var lock : FileLock? = null
-      try {
-        val sha1File = parameters.sha1File.asFile.get()
-        val outputDir = parameters.outputDir.asFile.get()
-        val tarGzFile = parameters.tarGzFile.asFile.get()
-        if (!shouldExecute(outputDir, tarGzFile, sha1File)) {
-          return;
-        }
-        val lockFile = parameters.lockFile.asFile.get()
-        val channel: FileChannel = RandomAccessFile(lockFile, "rw").getChannel()
-        // Block until we have the lock.
-        var couldTakeLock = false
-        while (!couldTakeLock) {
-          try {
-            lock = channel.lock()
-            couldTakeLock = true;
-          } catch (ignored: OverlappingFileLockException) {
-            sleep(50);
-          }
-        }
-        if (!shouldExecute(outputDir, tarGzFile, sha1File)) {
-          return;
-        }
-        if (outputDir.exists() && outputDir.isDirectory) {
-          outputDir.delete()
-        }
-        when (parameters.type.get()) {
-          DependencyType.GOOGLE_STORAGE -> {
-            downloadFromGoogleStorage(parameters, sha1File)
-          }
-          DependencyType.X20 -> {
-            downloadFromX20(parameters, sha1File)
-          }
-        }
-      } catch (e: Exception) {
-        throw RuntimeException(e)
-      } finally {
-        lock?.release()
-      }
-    }
-
-    @Throws(IOException::class, InterruptedException::class)
-    private fun downloadFromGoogleStorage(parameters: RunDownloadParameters, sha1File: File) {
-      val args = Arrays.asList("-n", "-b", "r8-deps", "-s", "-u", sha1File.toString())
-      if (OperatingSystem.current().isWindows) {
-        val command: MutableList<String> = ArrayList()
-        command.add("download_from_google_storage.bat")
-        command.addAll(args)
-        runProcess(parameters, ProcessBuilder().command(command))
-      } else {
-        runProcess(
-          parameters,
-          ProcessBuilder()
-            .command("bash",
-                     "-c",
-                     "download_from_google_storage " + java.lang.String.join(" ", args)))
-      }
-    }
-
-    @Throws(IOException::class, InterruptedException::class)
-    private fun downloadFromX20(parameters: RunDownloadParameters, sha1File: File) {
-      if (OperatingSystem.current().isWindows) {
-        throw RuntimeException("Downloading from x20 unsupported on windows")
-      }
-      runProcess(parameters,
-        ProcessBuilder()
-          .command("bash", "-c", "tools/download_from_x20.py $sha1File"))
-    }
-
-    @Throws(IOException::class, InterruptedException::class)
-    private fun runProcess(parameters: RunDownloadParameters, builder: ProcessBuilder) {
-      builder.directory(parameters.root.asFile.get())
-      val command = java.lang.String.join(" ", builder.command())
-      val p = builder.start()
-      val exit = p.waitFor()
-      if (exit != 0) {
-        throw IOException("Process failed for $command\n"
-            + BufferedReader(
-            InputStreamReader(p.errorStream, StandardCharsets.UTF_8))
-            .lines()
-            .collect(Collectors.joining("\n")))
-      }
-    }
-  }
-
-  companion object {
-    fun shouldExecute(outputDir: File, tarGzFile: File, sha1File: File) : Boolean {
-      // First run will write the tar.gz file, causing the second run to still be out-of-date.
-      // Check if the modification time of the tar is newer than the sha in which case we are done.
-      if (outputDir.exists()
-        && outputDir.isDirectory
-        && outputDir.list().isNotEmpty()
-        && tarGzFile.exists()
-        && sha1File.lastModified() <= tarGzFile.lastModified()) {
-        return false
-      }
-      return true
-    }
-  }
-}
\ No newline at end of file
diff --git a/d8_r8/commonBuildSrc/src/main/kotlin/TestConfigurationHelper.kt b/d8_r8/commonBuildSrc/src/main/kotlin/TestConfigurationHelper.kt
index 766bcf8..c63476c 100644
--- a/d8_r8/commonBuildSrc/src/main/kotlin/TestConfigurationHelper.kt
+++ b/d8_r8/commonBuildSrc/src/main/kotlin/TestConfigurationHelper.kt
@@ -48,7 +48,11 @@
 
     fun setupTestTask(test: Test, isR8Lib: Boolean = false, r8Jar: File? = null) {
       val project = test.project
-      test.environment("USE_NEW_GRADLE_SETUP", "true")
+      test.systemProperty("USE_NEW_GRADLE_SETUP", "true")
+      if (project.hasProperty("testfilter")) {
+        val testFilter = project.property("testfilter").toString()
+        test.filter.setIncludePatterns(*(testFilter.split("|").toTypedArray()))
+      }
       if (project.hasProperty("kotlin_compiler_dev")) {
         test.systemProperty("com.android.tools.r8.kotlincompilerdev", "1")
       }
diff --git a/d8_r8/commonBuildSrc/src/main/kotlin/TestingState.kt b/d8_r8/commonBuildSrc/src/main/kotlin/TestingState.kt
index 2ef455f..1b89075 100644
--- a/d8_r8/commonBuildSrc/src/main/kotlin/TestingState.kt
+++ b/d8_r8/commonBuildSrc/src/main/kotlin/TestingState.kt
@@ -8,8 +8,9 @@
 import java.io.PrintStream
 import java.net.URLEncoder
 import java.nio.file.Path
-import java.util.concurrent.TimeUnit
 import org.gradle.api.Project
+import org.gradle.api.logging.Logger
+import org.gradle.api.tasks.Exec
 import org.gradle.api.tasks.testing.Test
 import org.gradle.api.tasks.testing.TestDescriptor
 import org.gradle.api.tasks.testing.TestListener
@@ -17,6 +18,7 @@
 import org.gradle.api.tasks.testing.TestOutputListener
 import org.gradle.api.tasks.testing.TestResult
 import org.gradle.api.tasks.testing.TestResult.ResultType
+import org.gradle.kotlin.dsl.register
 
 // Utility to install tracking of test results in status files.
 class TestingState {
@@ -29,7 +31,7 @@
     enum class Mode { ALL, OUTSTANDING, FAILING, PAST_FAILING }
 
     // These are the files that are allowed for tracking test status.
-    enum class StatusFile { SUCCESS, FAILURE, PAST_FAILURE }
+    enum class StatusType { SUCCESS, FAILURE, PAST_FAILURE }
 
     fun getRerunMode(project: Project) : Mode? {
       val prop = project.findProperty(MODE_PROPERTY) ?: return null
@@ -54,30 +56,57 @@
       val index = indexDir.resolve("index.html")
       val resuming = reportDir.exists()
       if (resuming) {
-        applyTestFilters(testingStateMode, task, reportDir)
+        applyTestFilters(testingStateMode, task, reportDir, indexDir, projectName)
       }
       addTestHandler(task, projectName, index, reportDir)
     }
 
-    private fun applyTestFilters(mode: Mode, task: Test, reportDir: File) {
+    private fun applyTestFilters(
+      mode: Mode,
+      task: Test,
+      reportDir: File,
+      indexDir: File,
+      projectName: String,
+    ) {
       if (mode == Mode.ALL) {
         // Running without filters will (re)run all tests.
         return
       }
-      if (mode == Mode.OUTSTANDING) {
-        task.logger.lifecycle(
-          "Note: the building of an exclude list often times out."
-            + "You may need to simply rerun all tests.")
-        forEachTestReportStatusMatching(task, reportDir, StatusFile.SUCCESS, { clazz, name ->
-          task.filter.excludeTestsMatching("$clazz.$name")
-        })
-        return
+      val statusType = getStatusTypeForMode(mode)
+      val statusOutputFile = indexDir.resolve("${projectName}.${statusType.name}.txt")
+      val findStatusTask = task.project.tasks.register<Exec>("${projectName}-find-status-files")
+      {
+        inputs.dir(reportDir)
+        outputs.file(statusOutputFile)
+        workingDir(reportDir)
+        commandLine(
+          "find", ".", "-name", statusType.name
+        )
+        doFirst {
+          standardOutput = statusOutputFile.outputStream()
+        }
       }
-      assert(mode == Mode.FAILING || mode == Mode.PAST_FAILING)
-      val result = if (mode == Mode.FAILING) StatusFile.FAILURE else StatusFile.PAST_FAILURE
-      forEachTestReportStatusMatching(task, reportDir, result, { clazz, name ->
-        task.filter.includeTestsMatching("$clazz.$name")
-      })
+      task.dependsOn(findStatusTask)
+      task.doFirst {
+        if (mode == Mode.OUTSTANDING) {
+          forEachTestReportStatusMatching(
+            statusType,
+            findStatusTask.get().outputs.files.singleFile,
+            task.logger,
+            { clazz, name -> task.filter.excludeTestsMatching("${clazz}.${name}") })
+        } else {
+          val hasMatch = forEachTestReportStatusMatching(
+            statusType,
+            findStatusTask.get().outputs.files.singleFile,
+            task.logger,
+            { clazz, name -> task.filter.includeTestsMatching("${clazz}.${name}") })
+          if (!hasMatch) {
+            // Add a filter that does not match to ensure the test run is not "without filters"
+            // which would run all tests.
+            task.filter.includeTestsMatching("NON_MATCHING_TEST_FILTER")
+          }
+        }
+      }
     }
 
     private fun addTestHandler(
@@ -181,11 +210,20 @@
       })
     }
 
-    private fun getStatusFile(result: ResultType) : StatusFile {
+    private fun getStatusTypeForMode(mode: Mode) : StatusType {
+      return when (mode) {
+        Mode.OUTSTANDING -> StatusType.SUCCESS
+        Mode.FAILING -> StatusType.FAILURE
+        Mode.PAST_FAILING -> StatusType.PAST_FAILURE
+        Mode.ALL -> throw RuntimeException("Unexpected mode 'all' in status determination")
+      }
+    }
+
+    private fun getStatusTypeForResult(result: ResultType) : StatusType {
       return when (result) {
-        ResultType.FAILURE -> StatusFile.FAILURE
-        ResultType.SUCCESS -> StatusFile.SUCCESS
-        ResultType.SKIPPED -> StatusFile.SUCCESS
+        ResultType.FAILURE -> StatusType.FAILURE
+        ResultType.SUCCESS -> StatusType.SUCCESS
+        ResultType.SKIPPED -> StatusType.SUCCESS
       }
     }
 
@@ -287,18 +325,18 @@
       reportDir: File,
       desc: TestDescriptor,
       result: ResultType) {
-      val statusFile = getStatusFile(result)
+      val statusFile = getStatusTypeForResult(result)
       withTestResultEntryWriter(reportDir, desc, statusFile.name, false, {
         it.append(statusFile.name)
       })
-      if (statusFile == StatusFile.FAILURE) {
-        getTestResultEntryOutputFile(reportDir, desc, StatusFile.SUCCESS.name).delete()
-        val pastFailure = StatusFile.PAST_FAILURE.name
+      if (statusFile == StatusType.FAILURE) {
+        getTestResultEntryOutputFile(reportDir, desc, StatusType.SUCCESS.name).delete()
+        val pastFailure = StatusType.PAST_FAILURE.name
         withTestResultEntryWriter(reportDir, desc, pastFailure, false, {
           it.append(pastFailure)
         })
       } else {
-        getTestResultEntryOutputFile(reportDir, desc, StatusFile.FAILURE.name).delete()
+        getTestResultEntryOutputFile(reportDir, desc, StatusType.FAILURE.name).delete()
       }
     }
 
@@ -313,25 +351,13 @@
       FileWriter(file, append).use(fn)
     }
 
-    fun forEachTestReportStatusMatching(
-      test: Test,
-      reportDir: File,
-      statusFile: StatusFile,
-      onTest: (String, String) -> Unit
-    ): Boolean {
-      val fileName = statusFile.name
-      val logger = test.logger
-      val proc = ProcessBuilder("find", ".", "-name", fileName)
-        .directory(reportDir)
-        .redirectOutput(ProcessBuilder.Redirect.PIPE)
-        .start()
-      val result = proc.waitFor(10, TimeUnit.SECONDS)
-      if (!result) {
-        throw RuntimeException("Unexpected failure to find reports within time limit")
-      }
-      var hadMatch = false
-      for (rawLine in proc.inputStream.bufferedReader().lineSequence()) {
-        // Lines are of the form: ./<class>/<name>/FAILURE
+    private fun forEachTestReportStatusMatching(
+      type: StatusType, file: File, logger: Logger, onTest: (String, String) -> Unit
+    ) : Boolean {
+      val fileName = type.name
+      var hasMatch = false
+      for (rawLine in file.bufferedReader().lineSequence()) {
+        // Lines are of the form: ./<class>/<name>/<mode>
         try {
           val trimmed = rawLine.trim()
           val line = trimmed.substring(2)
@@ -339,12 +365,12 @@
           val clazz = line.substring(0, sep)
           val name = line.substring(sep + 1, line.length - fileName.length - 1)
           onTest(clazz, name)
-          hadMatch = true
+          hasMatch = true
         } catch (e: Exception) {
           logger.lifecycle("WARNING: failed attempt to read test description from: '${rawLine}'")
         }
       }
-      return hadMatch
+      return hasMatch
     }
   }
 }
\ No newline at end of file
diff --git a/d8_r8/keepanno/build.gradle.kts b/d8_r8/keepanno/build.gradle.kts
index d04408d..56d1ea0 100644
--- a/d8_r8/keepanno/build.gradle.kts
+++ b/d8_r8/keepanno/build.gradle.kts
@@ -13,6 +13,7 @@
   }
   sourceCompatibility = JvmCompatibility.sourceCompatibility
   targetCompatibility = JvmCompatibility.targetCompatibility
+  withSourcesJar()
 }
 
 dependencies {
@@ -20,12 +21,11 @@
   compileOnly(Deps.guava)
 }
 
-val thirdPartyCompileDependenciesTask = ensureThirdPartyDependencies(
-  "compileDeps",
-  listOf(Jdk.JDK_11.getThirdPartyDependency()))
-
 tasks {
-  withType<JavaCompile> {
-    dependsOn(thirdPartyCompileDependenciesTask)
+  val keepAnnoJar by registering(Jar::class) {
+    dependsOn(gradle.includedBuild("shared").task(":downloadDeps"))
+    from(sourceSets.main.get().output)
+    destinationDirectory.set(getRoot().resolveAll("build", "libs"))
+    archiveFileName.set("keepanno-annotations.jar")
   }
-}
\ No newline at end of file
+}
diff --git a/d8_r8/keepanno/settings.gradle.kts b/d8_r8/keepanno/settings.gradle.kts
index 3238e39..8159946 100644
--- a/d8_r8/keepanno/settings.gradle.kts
+++ b/d8_r8/keepanno/settings.gradle.kts
@@ -25,3 +25,5 @@
 }
 
 rootProject.name = "keepanno"
+val root = rootProject.projectDir.parentFile
+includeBuild(root.resolve("shared"))
diff --git a/d8_r8/library_desugar/build.gradle.kts b/d8_r8/library_desugar/build.gradle.kts
index d7f32c6..4094e64 100644
--- a/d8_r8/library_desugar/build.gradle.kts
+++ b/d8_r8/library_desugar/build.gradle.kts
@@ -10,7 +10,7 @@
 java {
   sourceSets.main.configure {
     java.srcDir(getRoot().resolveAll("src", "library_desugar", "java"))
-    output.resourcesDir = getRoot().resolveAll("build", "classes", "library_desugar_conversions")
+    output.setResourcesDir(getRoot().resolveAll("build", "classes", "library_desugar_conversions"))
   }
   sourceCompatibility = JavaVersion.VERSION_1_8
   targetCompatibility = JavaVersion.VERSION_1_8
@@ -18,13 +18,3 @@
 
 dependencies {
 }
-
-val thirdPartyCompileDependenciesTask = ensureThirdPartyDependencies(
-  "compileDeps",
-  listOf(Jdk.JDK_11.getThirdPartyDependency()))
-
-tasks {
-  withType<JavaCompile> {
-    dependsOn(thirdPartyCompileDependenciesTask)
-  }
-}
diff --git a/d8_r8/main/build.gradle.kts b/d8_r8/main/build.gradle.kts
index bec7b28..ba2c1a9 100644
--- a/d8_r8/main/build.gradle.kts
+++ b/d8_r8/main/build.gradle.kts
@@ -19,6 +19,7 @@
   }
   sourceCompatibility = JvmCompatibility.sourceCompatibility
   targetCompatibility = JvmCompatibility.targetCompatibility
+  withSourcesJar()
 }
 
 dependencies {
@@ -34,14 +35,6 @@
   errorprone(Deps.errorprone)
 }
 
-val thirdPartyCompileDependenciesTask = ensureThirdPartyDependencies(
-  "compileDeps",
-  listOf(Jdk.JDK_11.getThirdPartyDependency()))
-
-val thirdPartyResourceDependenciesTask = ensureThirdPartyDependencies(
-  "resourceDeps",
-  listOf(ThirdPartyDeps.apiDatabase))
-
 val keepAnnoJarTask = projectTask("keepanno", "jar")
 val resourceShrinkerJarTask = projectTask("resourceshrinker", "jar")
 val resourceShrinkerDepsTask = projectTask("resourceshrinker", "depsJar")
@@ -65,10 +58,11 @@
   }
 
   withType<ProcessResources> {
-    dependsOn(thirdPartyResourceDependenciesTask)
+    dependsOn(gradle.includedBuild("shared").task(":downloadDeps"))
   }
 
   val consolidatedLicense by registering {
+    dependsOn(gradle.includedBuild("shared").task(":downloadDeps"))
     val root = getRoot()
     val r8License = root.resolve("LICENSE")
     val libraryLicense = root.resolve("LIBRARY-LICENSE")
@@ -78,7 +72,7 @@
       libraryLicenseFiles,
       mainJarDependencies().map(::zipTree))
 
-    val license = rootProject.layout.buildDirectory.file("generatedLicense/LICENSE").get().asFile
+    val license = getRoot().resolveAll("build", "generatedLicense", "LICENSE")
     outputs.files(license)
 
     doLast {
@@ -123,23 +117,26 @@
   }
 
   val swissArmyKnife by registering(Jar::class) {
+    dependsOn(keepAnnoJarTask)
+    dependsOn(resourceShrinkerJarTask)
+    dependsOn(gradle.includedBuild("shared").task(":downloadDeps"))
     from(sourceSets.main.get().output)
+    from(keepAnnoJarTask.outputs.files.map(::zipTree))
+    from(resourceShrinkerJarTask.outputs.files.map(::zipTree))
     from(consolidatedLicense)
     manifest {
       attributes["Main-Class"] = "com.android.tools.r8.SwissArmyKnife"
     }
     exclude("META-INF/*.kotlin_module")
     exclude("**/*.kotlin_metadata")
-    archiveFileName.set("r8-swissarmyknife.jar")
+    destinationDirectory.set(getRoot().resolveAll("build", "libs"))
+    archiveFileName.set("r8-full-exclude-deps.jar")
   }
 
   val depsJar by registering(Jar::class) {
-    dependsOn(keepAnnoJarTask)
-    dependsOn(resourceShrinkerJarTask)
+    dependsOn(gradle.includedBuild("shared").task(":downloadDeps"))
     dependsOn(resourceShrinkerDepsTask)
     from(mainJarDependencies().map(::zipTree))
-    from(keepAnnoJarTask.outputs.files.map(::zipTree))
-    from(resourceShrinkerJarTask.outputs.files.map(::zipTree))
     from(resourceShrinkerDepsTask.outputs.files.map(::zipTree))
     exclude("**/module-info.class")
     exclude("**/*.kotlin_metadata")
@@ -168,7 +165,7 @@
     val swissArmy = swissArmyKnife.get().outputs.getFiles().getSingleFile()
     val deps = depsJar.get().outputs.files.getSingleFile()
     inputs.files(listOf(swissArmy, deps))
-    val output = getRoot().resolveAll("build", "libs", "r8-with-relocated-deps.jar")
+    val output = getRoot().resolveAll("build", "libs", "r8.jar")
     outputs.file(output)
     commandLine = baseCompilerCommandLine(
       swissArmy,
@@ -223,7 +220,7 @@
 }
 
 tasks.withType<JavaCompile> {
-  dependsOn(thirdPartyCompileDependenciesTask)
+  dependsOn(gradle.includedBuild("shared").task(":downloadDeps"))
   println("NOTE: Running with JDK: " + org.gradle.internal.jvm.Jvm.current().javaHome)
 
   // Enable error prone for D8/R8 main sources.
diff --git a/d8_r8/main/settings.gradle.kts b/d8_r8/main/settings.gradle.kts
index 34ab9ed..4e1d975 100644
--- a/d8_r8/main/settings.gradle.kts
+++ b/d8_r8/main/settings.gradle.kts
@@ -27,5 +27,6 @@
 rootProject.name = "r8"
 
 val root = rootProject.projectDir.parentFile
+includeBuild(root.resolve("shared"))
 includeBuild(root.resolve("keepanno"))
 includeBuild(root.resolve("resourceshrinker"))
diff --git a/d8_r8/resourceshrinker/build.gradle.kts b/d8_r8/resourceshrinker/build.gradle.kts
index 60a037b..750f076 100644
--- a/d8_r8/resourceshrinker/build.gradle.kts
+++ b/d8_r8/resourceshrinker/build.gradle.kts
@@ -16,6 +16,7 @@
   }
   sourceCompatibility = JvmCompatibility.sourceCompatibility
   targetCompatibility = JvmCompatibility.targetCompatibility
+  withSourcesJar()
 }
 
 fun jarDependencies() : FileCollection {
@@ -29,14 +30,10 @@
     })
 }
 
-val thirdPartyCompileDependenciesTask = ensureThirdPartyDependencies(
-  "compileDeps",
-  listOf(ThirdPartyDeps.r8))
-
 dependencies {
   compileOnly(Deps.asm)
   compileOnly(Deps.guava)
-  compileOnly(files(getRoot().resolve(ThirdPartyDeps.r8.path).resolve("r8lib_8.2.20-dev.jar")))
+  compileOnly(files(resolve(ThirdPartyDeps.r8, "r8lib_8.2.20-dev.jar")))
   implementation("com.android.tools.build:aapt2-proto:8.2.0-alpha10-10154469")
   implementation("com.google.protobuf:protobuf-java:3.19.3")
   implementation("com.android.tools.layoutlib:layoutlib-api:31.2.0-alpha10")
@@ -46,7 +43,7 @@
 
 tasks {
   withType<KotlinCompile> {
-    dependsOn(thirdPartyCompileDependenciesTask)
+    dependsOn(gradle.includedBuild("shared").task(":downloadDeps"))
     kotlinOptions {
       // We cannot use languageVersion.set(JavaLanguageVersion.of(8)) because gradle cannot figure
       // out that the jdk is 1_8 and will try to download it.
diff --git a/d8_r8/resourceshrinker/settings.gradle.kts b/d8_r8/resourceshrinker/settings.gradle.kts
index 604d9cb..d879107 100644
--- a/d8_r8/resourceshrinker/settings.gradle.kts
+++ b/d8_r8/resourceshrinker/settings.gradle.kts
@@ -25,3 +25,6 @@
 }
 
 rootProject.name = "resourceshrinker"
+
+val root = rootProject.projectDir.parentFile
+includeBuild(root.resolve("shared"))
diff --git a/d8_r8/settings.gradle.kts b/d8_r8/settings.gradle.kts
index c3c86b7..f4ff5e0 100644
--- a/d8_r8/settings.gradle.kts
+++ b/d8_r8/settings.gradle.kts
@@ -4,6 +4,8 @@
 
 // TODO(b/270105162): Move this file out the repository root when old gradle is removed.
 
+import org.gradle.internal.os.OperatingSystem
+
 rootProject.name = "d8-r8"
 
 // Bootstrap building by downloading dependencies.
@@ -29,8 +31,12 @@
       return
   }
 
+  var downloadScript = "download_from_google_storage.py"
+  if (OperatingSystem.current().isWindows()) {
+    downloadScript = "download_from_google_storage.bat"
+  }
   val cmd = listOf(
-    "download_from_google_storage.py",
+    downloadScript,
     "--extract",
     "--bucket",
     dependencies_bucket,
@@ -78,8 +84,7 @@
   }
 }
 
-// This project is temporarily located in d8_r8. When moved to root, the parent
-// folder should just be removed.
+includeBuild(root.resolve("shared"))
 includeBuild(root.resolve("keepanno"))
 includeBuild(root.resolve("resourceshrinker"))
 
diff --git a/d8_r8/shared/build.gradle.kts b/d8_r8/shared/build.gradle.kts
new file mode 100644
index 0000000..fa66c14
--- /dev/null
+++ b/d8_r8/shared/build.gradle.kts
@@ -0,0 +1,19 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+plugins {
+  `kotlin-dsl`
+  id("dependencies-plugin")
+}
+
+tasks {
+
+  val downloadDeps by registering(DownloadAllDependenciesTask::class) {
+    this.setDependencies(getRoot(), allPublicDependencies())
+  }
+
+  val downloadDepsInternal by registering(DownloadAllDependenciesTask::class) {
+    this.setDependencies(getRoot(), allInternalDependencies())
+  }
+}
\ No newline at end of file
diff --git a/d8_r8/test_modules/tests_java_examples/gradle.properties b/d8_r8/shared/gradle.properties
similarity index 100%
rename from d8_r8/test_modules/tests_java_examples/gradle.properties
rename to d8_r8/shared/gradle.properties
diff --git a/d8_r8/shared/settings.gradle.kts b/d8_r8/shared/settings.gradle.kts
new file mode 100644
index 0000000..0de348f
--- /dev/null
+++ b/d8_r8/shared/settings.gradle.kts
@@ -0,0 +1,27 @@
+// 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.
+
+pluginManagement {
+  repositories {
+    maven {
+      url = uri("file:../../third_party/dependencies")
+    }
+    maven {
+      url = uri("file:../../third_party/dependencies_new")
+    }
+  }
+}
+
+dependencyResolutionManagement {
+  repositories {
+    maven {
+      url = uri("file:../../third_party/dependencies")
+    }
+    maven {
+      url = uri("file:../../third_party/dependencies_new")
+    }
+  }
+}
+
+rootProject.name = "shared"
diff --git a/d8_r8/test/build.gradle.kts b/d8_r8/test/build.gradle.kts
index 6cc61e5..b7fd7b5 100644
--- a/d8_r8/test/build.gradle.kts
+++ b/d8_r8/test/build.gradle.kts
@@ -21,21 +21,16 @@
 dependencies { }
 
 val keepAnnoCompileTask = projectTask("keepanno", "compileJava")
+val keepAnnoSourcesTask = projectTask("keepanno", "sourcesJar")
 val mainDepsJarTask = projectTask("main", "depsJar")
-val r8Jar = projectTask("main", "jar")
+val swissArmyKnifeTask = projectTask("main", "swissArmyKnife")
 val r8WithRelocatedDepsTask = projectTask("main", "r8WithRelocatedDeps")
+val mainSourcesTask = projectTask("main", "sourcesJar")
+val resourceShrinkerSourcesTask = projectTask("resourceshrinker", "sourcesJar")
 val java8TestJarTask = projectTask("tests_java_8", "testJar")
 val java8TestsDepsJarTask = projectTask("tests_java_8", "depsJar")
 val bootstrapTestsDepsJarTask = projectTask("tests_bootstrap", "depsJar")
 
-val thirdPartyRuntimeDependenciesTask = ensureThirdPartyDependencies(
-  "runtimeDeps",
-  testRuntimeDependencies)
-
-val thirdPartyRuntimeInternalDependenciesTask = ensureThirdPartyDependencies(
-  "runtimeInternalDeps",
-  testRuntimeInternalDependencies)
-
 tasks {
   withType<Exec> {
     doFirst {
@@ -91,10 +86,10 @@
     dependsOn(mainDepsJarTask)
     dependsOn(r8WithRelocatedDepsTask)
     val r8Compiler = r8WithRelocatedDepsTask.outputs.files.getSingleFile()
-    val r8Jar = r8Jar.outputs.files.getSingleFile()
+    val r8Jar = swissArmyKnifeTask.outputs.files.getSingleFile()
     val deps = mainDepsJarTask.outputs.files.getSingleFile()
     inputs.files(listOf(r8Compiler, r8Jar, deps))
-    val output = file(Paths.get("build", "libs", "r8lib-exclude-deps.jar"))
+    val output = getRoot().resolveAll("build", "libs", "r8lib-exclude-deps.jar")
     outputs.file(output)
     commandLine = createR8LibCommandLine(
       r8Compiler,
@@ -102,9 +97,31 @@
       output,
       listOf(getRoot().resolveAll("src", "main", "keep.txt")),
       true,
+      false,
       listOf(deps))
   }
 
+  val retraceNoDeps by registering(Exec::class) {
+    dependsOn(r8LibNoDeps)
+    val r8Compiler = r8WithRelocatedDepsTask.outputs.files.getSingleFile()
+    val r8Jar = r8LibNoDeps.get().outputs.files.getSingleFile()
+    val deps = mainDepsJarTask.outputs.files.getSingleFile()
+    inputs.files(listOf(r8Compiler, r8Jar, deps))
+    val inputMap = file(r8Jar.toString() + ".map")
+    val output = getRoot().resolveAll("build", "libs", "r8retrace-exclude-deps.jar")
+    outputs.file(output)
+    commandLine = createR8LibCommandLine(
+      r8Compiler,
+      r8Jar,
+      output,
+      listOf(getRoot().resolveAll("src", "main", "keep_retrace.txt")),
+      true,
+      true,
+      listOf(deps),
+      listOf(),
+      inputMap)
+  }
+
   val generateKeepRules by registering(Exec::class) {
     dependsOn(r8WithRelocatedDepsTask)
     dependsOn(mainDepsJarTask)
@@ -153,9 +170,31 @@
       r8,
       output,
       listOf(keepTxt, generatedKeepRules, keepResourceShrinkerTxt),
+      false,
       false)
   }
 
+  val retraceWithRelocatedDeps by registering(Exec::class) {
+    dependsOn(r8LibWithRelocatedDeps)
+    val r8Compiler = r8WithRelocatedDepsTask.outputs.files.getSingleFile()
+    val r8Jar = r8LibWithRelocatedDeps.get().outputs.files.getSingleFile()
+    val deps = mainDepsJarTask.outputs.files.getSingleFile()
+    inputs.files(listOf(r8Compiler, r8Jar, deps))
+    val inputMap = file(r8Jar.toString() + ".map")
+    val output = getRoot().resolveAll("build", "libs", "r8retrace.jar")
+    outputs.file(output)
+    commandLine = createR8LibCommandLine(
+      r8Compiler,
+      r8Jar,
+      output,
+      listOf(getRoot().resolveAll("src", "main", "keep_retrace.txt")),
+      false,
+      true,
+      listOf(),
+      listOf(),
+      inputMap)
+  }
+
   val resourceshrinkercli by registering(Exec::class) {
     dependsOn(r8WithRelocatedDepsTask)
     val r8 = r8WithRelocatedDepsTask.outputs.files.getSingleFile()
@@ -169,6 +208,7 @@
       r8,
       output,
       listOf(keepTxt, cliKeep),
+      false,
       false)
   }
 
@@ -193,7 +233,6 @@
     dependsOn(allTestsJarRelocated)
     dependsOn(r8WithRelocatedDepsTask)
     dependsOn(allTestWithApplyMappingProguardConfiguration)
-    dependsOn(thirdPartyRuntimeDependenciesTask)
     val r8 = r8WithRelocatedDepsTask.outputs.files.singleFile
     val allTests = allTestsJarRelocated.get().outputs.files.singleFile
     val pgConf = allTestWithApplyMappingProguardConfiguration.get().outputs.files.singleFile
@@ -222,30 +261,6 @@
         "$allTests"))
   }
 
-  test {
-    println("NOTE: Number of processors " + Runtime.getRuntime().availableProcessors())
-    println("NOTE: Max parallel forks " + maxParallelForks)
-    val os = DefaultNativePlatform.getCurrentOperatingSystem()
-    if (os.isMacOsX) {
-      logger.lifecycle(
-        "WARNING: Testing in only partially supported on Mac OS. \n" +
-          "Art only runs on Linux and tests requiring Art runs in a Docker container, which must " +
-          "be present. See tools/docker/README.md for details.")
-    } else if (os.isWindows) {
-      logger.lifecycle(
-        "WARNING: Testing in only partially supported on Windows. Art only runs on Linux and " +
-          "tests requiring Art will be skipped")
-    } else if (!os.isLinux) {
-      logger.log(
-        ERROR,
-        "Testing in not supported on your platform. Testing is only fully supported on " +
-          "Linux and partially supported on Mac OS and Windows. Art does not run on other " +
-          "platforms.")
-    }
-    dependsOn(gradle.includedBuild("tests_java_8").task(":test"))
-    dependsOn(gradle.includedBuild("tests_bootstrap").task(":test"))
-  }
-
   val unzipTests by registering(Copy::class) {
     dependsOn(allTestsJar)
     val outputDir = file("${buildDir}/unpacked/test")
@@ -267,9 +282,9 @@
     dependsOn(r8LibWithRelocatedDeps)
     dependsOn(unzipTests)
     dependsOn(unzipRewrittenTests)
-    dependsOn(thirdPartyRuntimeDependenciesTask)
+    dependsOn(gradle.includedBuild("shared").task(":downloadDeps"))
     if (!project.hasProperty("no_internal")) {
-      dependsOn(thirdPartyRuntimeInternalDependenciesTask)
+      dependsOn(gradle.includedBuild("shared").task(":downloadDepsInternal"))
     }
     val r8LibJar = r8LibWithRelocatedDeps.get().outputs.files.singleFile
     this.configure(isR8Lib = true, r8Jar = r8LibJar)
@@ -281,8 +296,7 @@
       r8LibJar,
       unzipRewrittenTests.get().outputs.files)
     testClassesDirs = unzipRewrittenTests.get().outputs.files
-
-    environment.put("TEST_CLASSES_LOCATIONS", unzipTests.get().outputs.files.singleFile)
+    systemProperty("TEST_DATA_LOCATION", unzipTests.get().outputs.files.singleFile)
     systemProperty("KEEP_ANNO_JAVAC_BUILD_DIR", keepAnnoCompileTask.outputs.files.getAsPath())
     systemProperty("EXAMPLES_JAVA_11_JAVAC_BUILD_DIR",
                    getRoot().resolveAll("build", "test", "examplesJava11", "classes"))
@@ -290,5 +304,27 @@
     // TODO(b/270105162): This should change if running with retrace lib/r8lib.
     systemProperty("RETRACE_RUNTIME_PATH", r8LibJar)
     systemProperty("R8_DEPS", mainDepsJarTask.outputs.files.singleFile)
- }
-}
\ No newline at end of file
+    systemProperty("com.android.tools.r8.artprofilerewritingcompletenesscheck", "true")
+  }
+
+  val sourcesJar by registering(Jar::class) {
+    dependsOn(mainSourcesTask)
+    dependsOn(resourceShrinkerSourcesTask)
+    dependsOn(keepAnnoSourcesTask)
+    from(mainSourcesTask.outputs.files.map(::zipTree))
+    from(resourceShrinkerSourcesTask.outputs.files.map(::zipTree))
+    from(keepAnnoSourcesTask.outputs.files.map(::zipTree))
+    archiveClassifier.set("sources")
+    archiveFileName.set("r8-src.jar")
+    destinationDirectory.set(getRoot().resolveAll("build", "libs"))
+  }
+
+  test {
+    if (project.hasProperty("r8lib")) {
+      dependsOn(r8LibTest)
+    } else {
+      dependsOn(gradle.includedBuild("tests_java_8").task(":test"))
+      dependsOn(gradle.includedBuild("tests_bootstrap").task(":test"))
+    }
+  }
+}
diff --git a/d8_r8/test/settings.gradle.kts b/d8_r8/test/settings.gradle.kts
index 5df668c..375809a 100644
--- a/d8_r8/test/settings.gradle.kts
+++ b/d8_r8/test/settings.gradle.kts
@@ -27,7 +27,9 @@
 rootProject.name = "r8-tests"
 
 val root = rootProject.projectDir.parentFile
+includeBuild(root.resolve("shared"))
 includeBuild(root.resolve("keepanno"))
 includeBuild(root.resolve("main"))
+includeBuild(root.resolve("resourceshrinker"))
 includeBuild(root.resolve("test_modules").resolve("tests_java_8"))
 includeBuild(root.resolve("test_modules").resolve("tests_bootstrap"))
diff --git a/d8_r8/test_modules/tests_bootstrap/build.gradle.kts b/d8_r8/test_modules/tests_bootstrap/build.gradle.kts
index 1a8ff99..bf64d96 100644
--- a/d8_r8/test_modules/tests_bootstrap/build.gradle.kts
+++ b/d8_r8/test_modules/tests_bootstrap/build.gradle.kts
@@ -67,15 +67,11 @@
 
   withType<Test> {
     TestingState.setUpTestingState(this)
-
-    environment.put("USE_NEW_GRADLE_SETUP", "true")
-    environment.put("TEST_CLASSES_LOCATIONS", "$buildDir/classes/java/test")
     dependsOn(mainR8RelocatedTask)
-    systemProperty("R8_WITH_RELOCATED_DEPS", mainR8RelocatedTask.outputs.files.getSingleFile())
-    systemProperty("R8_RUNTIME_PATH", mainR8RelocatedTask.outputs.files.getSingleFile())
-
-    // TODO(b/291198792): Remove this exclusion when desugared library runs correctly.
-    exclude("com/android/tools/r8/bootstrap/HelloWorldCompiledOnArtTest**")
+    systemProperty("TEST_DATA_LOCATION",
+                   layout.buildDirectory.dir("classes/java/test").get().toString())
+    systemProperty("R8_WITH_RELOCATED_DEPS", mainR8RelocatedTask.outputs.files.singleFile)
+    systemProperty("R8_RUNTIME_PATH", mainR8RelocatedTask.outputs.files.singleFile)
   }
 
   val testJar by registering(Jar::class) {
@@ -85,6 +81,10 @@
   }
 
   val depsJar by registering(Jar::class) {
+    dependsOn(gradle.includedBuild("shared").task(":downloadDeps"))
+    if (!project.hasProperty("no_internal")) {
+      dependsOn(gradle.includedBuild("shared").task(":downloadDepsInternal"))
+    }
     from(testDependencies().map(::zipTree))
     duplicatesStrategy = DuplicatesStrategy.EXCLUDE
     archiveFileName.set("deps.jar")
diff --git a/d8_r8/test_modules/tests_bootstrap/settings.gradle.kts b/d8_r8/test_modules/tests_bootstrap/settings.gradle.kts
index fba8852..cb05e4f 100644
--- a/d8_r8/test_modules/tests_bootstrap/settings.gradle.kts
+++ b/d8_r8/test_modules/tests_bootstrap/settings.gradle.kts
@@ -27,8 +27,9 @@
 rootProject.name = "tests_bootstrap"
 
 val root = rootProject.projectDir.parentFile.parentFile
-//
+
 // We need to include src/main as a composite-build otherwise our test-modules
 // will compete with the test to compile the source files.
+includeBuild(root.resolve("shared"))
 includeBuild(root.resolve("main"))
 includeBuild(root.resolve("test_modules").resolve("tests_java_8"))
diff --git a/d8_r8/test_modules/tests_java_10/build.gradle.kts b/d8_r8/test_modules/tests_java_10/build.gradle.kts
index 7d653b1..fa6f761 100644
--- a/d8_r8/test_modules/tests_java_10/build.gradle.kts
+++ b/d8_r8/test_modules/tests_java_10/build.gradle.kts
@@ -26,13 +26,9 @@
 // We just need to register the examples jars for it to be referenced by other modules.
 val buildExampleJars = buildExampleJars("examplesJava10")
 
-val thirdPartyCompileDependenciesTask = ensureThirdPartyDependencies(
-  "compileDeps",
-  listOf(Jdk.JDK_11.getThirdPartyDependency()))
-
 tasks {
   withType<JavaCompile> {
-    dependsOn(thirdPartyCompileDependenciesTask)
+    dependsOn(gradle.includedBuild("shared").task(":downloadDeps"))
     options.setFork(true)
     options.forkOptions.memoryMaximumSize = "3g"
     options.forkOptions.executable = getCompilerPath(Jdk.JDK_11)
diff --git a/d8_r8/test_modules/tests_java_10/settings.gradle.kts b/d8_r8/test_modules/tests_java_10/settings.gradle.kts
index 53e69d0..d7b2f9a 100644
--- a/d8_r8/test_modules/tests_java_10/settings.gradle.kts
+++ b/d8_r8/test_modules/tests_java_10/settings.gradle.kts
@@ -25,3 +25,5 @@
 }
 
 rootProject.name = "tests_java_10"
+val root = rootProject.projectDir.parentFile.parentFile
+includeBuild(root.resolve("shared"))
diff --git a/d8_r8/test_modules/tests_java_11/build.gradle.kts b/d8_r8/test_modules/tests_java_11/build.gradle.kts
index f056220..d8f620f 100644
--- a/d8_r8/test_modules/tests_java_11/build.gradle.kts
+++ b/d8_r8/test_modules/tests_java_11/build.gradle.kts
@@ -26,13 +26,9 @@
 // We just need to register the examples jars for it to be referenced by other modules.
 val buildExampleJars = buildExampleJars("examplesJava11")
 
-val thirdPartyCompileDependenciesTask = ensureThirdPartyDependencies(
-  "compileDeps",
-  listOf(Jdk.JDK_11.getThirdPartyDependency()))
-
 tasks {
   withType<JavaCompile> {
-    dependsOn(thirdPartyCompileDependenciesTask)
+    dependsOn(gradle.includedBuild("shared").task(":downloadDeps"))
     options.setFork(true)
     options.forkOptions.memoryMaximumSize = "3g"
     options.forkOptions.executable = getCompilerPath(Jdk.JDK_11)
diff --git a/d8_r8/test_modules/tests_java_11/settings.gradle.kts b/d8_r8/test_modules/tests_java_11/settings.gradle.kts
index 4dc457a..90fa823 100644
--- a/d8_r8/test_modules/tests_java_11/settings.gradle.kts
+++ b/d8_r8/test_modules/tests_java_11/settings.gradle.kts
@@ -25,3 +25,5 @@
 }
 
 rootProject.name = "tests_java_11"
+val root = rootProject.projectDir.parentFile.parentFile
+includeBuild(root.resolve("shared"))
diff --git a/d8_r8/test_modules/tests_java_17/build.gradle.kts b/d8_r8/test_modules/tests_java_17/build.gradle.kts
index 005cb87..92f1dcd 100644
--- a/d8_r8/test_modules/tests_java_17/build.gradle.kts
+++ b/d8_r8/test_modules/tests_java_17/build.gradle.kts
@@ -26,13 +26,9 @@
 // We just need to register the examples jars for it to be referenced by other modules.
 val buildExampleJars = buildExampleJars("examplesJava17")
 
-val thirdPartyCompileDependenciesTask = ensureThirdPartyDependencies(
-  "compileDeps",
-  listOf(Jdk.JDK_17.getThirdPartyDependency()))
-
 tasks {
   withType<JavaCompile> {
-    dependsOn(thirdPartyCompileDependenciesTask)
+    dependsOn(gradle.includedBuild("shared").task(":downloadDeps"))
     options.setFork(true)
     options.forkOptions.memoryMaximumSize = "3g"
     options.forkOptions.executable = getCompilerPath(Jdk.JDK_17)
diff --git a/d8_r8/test_modules/tests_java_17/settings.gradle.kts b/d8_r8/test_modules/tests_java_17/settings.gradle.kts
index 3312a37..fc53d30 100644
--- a/d8_r8/test_modules/tests_java_17/settings.gradle.kts
+++ b/d8_r8/test_modules/tests_java_17/settings.gradle.kts
@@ -25,3 +25,5 @@
 }
 
 rootProject.name = "tests_java_17"
+val root = rootProject.projectDir.parentFile.parentFile
+includeBuild(root.resolve("shared"))
diff --git a/d8_r8/test_modules/tests_java_20/build.gradle.kts b/d8_r8/test_modules/tests_java_20/build.gradle.kts
index f2d2e31..43037f0 100644
--- a/d8_r8/test_modules/tests_java_20/build.gradle.kts
+++ b/d8_r8/test_modules/tests_java_20/build.gradle.kts
@@ -26,13 +26,9 @@
 // We just need to register the examples jars for it to be referenced by other modules.
 val buildExampleJars = buildExampleJars("examplesJava20")
 
-val thirdPartyCompileDependenciesTask = ensureThirdPartyDependencies(
-  "compileDeps",
-  listOf(Jdk.JDK_20.getThirdPartyDependency()))
-
 tasks {
   withType<JavaCompile> {
-    dependsOn(thirdPartyCompileDependenciesTask)
+    dependsOn(gradle.includedBuild("shared").task(":downloadDeps"))
     options.setFork(true)
     options.forkOptions.memoryMaximumSize = "3g"
     options.forkOptions.executable = getCompilerPath(Jdk.JDK_20)
diff --git a/d8_r8/test_modules/tests_java_20/settings.gradle.kts b/d8_r8/test_modules/tests_java_20/settings.gradle.kts
index 0fc5857..85d94d0 100644
--- a/d8_r8/test_modules/tests_java_20/settings.gradle.kts
+++ b/d8_r8/test_modules/tests_java_20/settings.gradle.kts
@@ -25,3 +25,5 @@
 }
 
 rootProject.name = "tests_java_20"
+val root = rootProject.projectDir.parentFile.parentFile
+includeBuild(root.resolve("shared"))
diff --git a/d8_r8/test_modules/tests_java_8/build.gradle.kts b/d8_r8/test_modules/tests_java_8/build.gradle.kts
index f87553a..881089b 100644
--- a/d8_r8/test_modules/tests_java_8/build.gradle.kts
+++ b/d8_r8/test_modules/tests_java_8/build.gradle.kts
@@ -58,33 +58,12 @@
   implementation(Deps.smaliUtil)
 }
 
-val thirdPartyCompileDependenciesTask = ensureThirdPartyDependencies(
-  "compileDeps",
-  listOf(
-    ThirdPartyDeps.apiDatabase,
-    ThirdPartyDeps.ddmLib,
-    ThirdPartyDeps.jasmin,
-    ThirdPartyDeps.jdwpTests))
-
-val thirdPartyRuntimeDependenciesTask = ensureThirdPartyDependencies(
-  "runtimeDeps",
-  testRuntimeDependencies)
-
-val thirdPartyRuntimeInternalDependenciesTask = ensureThirdPartyDependencies(
-  "runtimeInternalDeps",
-  testRuntimeInternalDependencies)
-
 val sourceSetDependenciesTasks = arrayOf(
-  projectTask("tests_java_examples", getExampleJarsTaskName("examples")),
   projectTask("tests_java_9", getExampleJarsTaskName("examplesJava9")),
   projectTask("tests_java_10", getExampleJarsTaskName("examplesJava10")),
   projectTask("tests_java_11", getExampleJarsTaskName("examplesJava11")),
   projectTask("tests_java_17", getExampleJarsTaskName("examplesJava17")),
   projectTask("tests_java_20", getExampleJarsTaskName("examplesJava20")),
-  projectTask("tests_java_examplesAndroidN", getExampleJarsTaskName("examplesAndroidN")),
-  projectTask("tests_java_examplesAndroidO", getExampleJarsTaskName("examplesAndroidO")),
-  projectTask("tests_java_examplesAndroidP", getExampleJarsTaskName("examplesAndroidP")),
-  projectTask("tests_java_kotlinR8TestResources", getExampleJarsTaskName("kotlinR8TestResources")),
 )
 
 fun testDependencies() : FileCollection {
@@ -101,14 +80,14 @@
 
 tasks {
   "compileTestJava" {
-    dependsOn(thirdPartyCompileDependenciesTask)
+    dependsOn(gradle.includedBuild("shared").task(":downloadDeps"))
   }
   withType<JavaCompile> {
     dependsOn(gradle.includedBuild("keepanno").task(":jar"))
     dependsOn(gradle.includedBuild("resourceshrinker").task(":jar"))
     dependsOn(gradle.includedBuild("main").task(":compileJava"))
     dependsOn(gradle.includedBuild("main").task(":processResources"))
-    dependsOn(thirdPartyCompileDependenciesTask)
+    dependsOn(gradle.includedBuild("shared").task(":downloadDeps"))
   }
 
   withType<KotlinCompile> {
@@ -117,14 +96,14 @@
 
   withType<Test> {
     TestingState.setUpTestingState(this)
-    environment.put("USE_NEW_GRADLE_SETUP", "true")
-    environment.put("TEST_CLASSES_LOCATIONS", "$buildDir/classes/java/test")
     dependsOn(mainDepsJarTask)
-    dependsOn(thirdPartyRuntimeDependenciesTask)
+    dependsOn(gradle.includedBuild("shared").task(":downloadDeps"))
     if (!project.hasProperty("no_internal")) {
-      dependsOn(thirdPartyRuntimeInternalDependenciesTask)
+      dependsOn(gradle.includedBuild("shared").task(":downloadDepsInternal"))
     }
     dependsOn(*sourceSetDependenciesTasks)
+    systemProperty("TEST_DATA_LOCATION",
+                   layout.buildDirectory.dir("classes/java/test").get().toString())
     systemProperty("KEEP_ANNO_JAVAC_BUILD_DIR", keepAnnoCompileTask.outputs.files.getAsPath())
     // This path is set when compiling examples jar task in DependenciesPlugin.
     systemProperty("EXAMPLES_JAVA_11_JAVAC_BUILD_DIR",
@@ -138,6 +117,7 @@
       mainCompileTask.outputs.files.getAsPath().split(File.pathSeparator)[0] +
         File.pathSeparator + mainDepsJarTask.outputs.files.singleFile)
     systemProperty("R8_DEPS", mainDepsJarTask.outputs.files.singleFile)
+    systemProperty("com.android.tools.r8.artprofilerewritingcompletenesscheck", "true")
   }
 
   val testJar by registering(Jar::class) {
@@ -149,9 +129,9 @@
   }
 
   val depsJar by registering(Jar::class) {
+    dependsOn(gradle.includedBuild("shared").task(":downloadDeps"))
     dependsOn(gradle.includedBuild("keepanno").task(":jar"))
     dependsOn(gradle.includedBuild("resourceshrinker").task(":jar"))
-    dependsOn(thirdPartyCompileDependenciesTask)
     from(testDependencies().map(::zipTree))
     duplicatesStrategy = DuplicatesStrategy.EXCLUDE
     archiveFileName.set("deps.jar")
diff --git a/d8_r8/test_modules/tests_java_8/settings.gradle.kts b/d8_r8/test_modules/tests_java_8/settings.gradle.kts
index 627a65a..a0cc31c 100644
--- a/d8_r8/test_modules/tests_java_8/settings.gradle.kts
+++ b/d8_r8/test_modules/tests_java_8/settings.gradle.kts
@@ -27,21 +27,15 @@
 rootProject.name = "tests_java_8"
 
 val root = rootProject.projectDir.parentFile.parentFile
+includeBuild(root.resolve("shared"))
 includeBuild(root.resolve("keepanno"))
 includeBuild(root.resolve("resourceshrinker"))
 
 // We need to include src/main as a composite-build otherwise our test-modules
 // will compete with the test to compile the source files.
 includeBuild(root.resolve("main"))
-
 includeBuild(root.resolve("test_modules").resolve("tests_java_9"))
 includeBuild(root.resolve("test_modules").resolve("tests_java_10"))
 includeBuild(root.resolve("test_modules").resolve("tests_java_11"))
 includeBuild(root.resolve("test_modules").resolve("tests_java_17"))
 includeBuild(root.resolve("test_modules").resolve("tests_java_20"))
-includeBuild(root.resolve("test_modules").resolve("tests_java_examples"))
-includeBuild(root.resolve("test_modules").resolve("tests_java_examplesAndroidN"))
-includeBuild(root.resolve("test_modules").resolve("tests_java_examplesAndroidO"))
-includeBuild(root.resolve("test_modules").resolve("tests_java_examplesAndroidP"))
-includeBuild(root.resolve("test_modules").resolve("tests_java_kotlinR8TestResources"))
-
diff --git a/d8_r8/test_modules/tests_java_9/build.gradle.kts b/d8_r8/test_modules/tests_java_9/build.gradle.kts
index bda4b77..136a506 100644
--- a/d8_r8/test_modules/tests_java_9/build.gradle.kts
+++ b/d8_r8/test_modules/tests_java_9/build.gradle.kts
@@ -26,13 +26,9 @@
 // We just need to register the examples jars for it to be referenced by other modules.
 val buildExampleJars = buildExampleJars("examplesJava9")
 
-val thirdPartyCompileDependenciesTask = ensureThirdPartyDependencies(
-  "compileDeps",
-  listOf(Jdk.JDK_9.getThirdPartyDependency()))
-
 tasks {
   withType<JavaCompile> {
-    dependsOn(thirdPartyCompileDependenciesTask)
+    dependsOn(gradle.includedBuild("shared").task(":downloadDeps"))
     options.setFork(true)
     options.forkOptions.memoryMaximumSize = "3g"
     options.forkOptions.executable = getCompilerPath(Jdk.JDK_9)
diff --git a/d8_r8/test_modules/tests_java_9/settings.gradle.kts b/d8_r8/test_modules/tests_java_9/settings.gradle.kts
index 2639b0f..46bc3a8 100644
--- a/d8_r8/test_modules/tests_java_9/settings.gradle.kts
+++ b/d8_r8/test_modules/tests_java_9/settings.gradle.kts
@@ -25,3 +25,5 @@
 }
 
 rootProject.name = "tests_java_9"
+val root = rootProject.projectDir.parentFile.parentFile
+includeBuild(root.resolve("shared"))
diff --git a/d8_r8/test_modules/tests_java_examples/build.gradle.kts b/d8_r8/test_modules/tests_java_examples/build.gradle.kts
deleted file mode 100644
index 95a9105..0000000
--- a/d8_r8/test_modules/tests_java_examples/build.gradle.kts
+++ /dev/null
@@ -1,57 +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.
-
-import org.gradle.api.JavaVersion
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
-
-plugins {
-  `kotlin-dsl`
-  `java-library`
-  id("dependencies-plugin")
-}
-
-val root = getRoot()
-
-java {
-  sourceSets.test.configure {
-    java.srcDirs.clear()
-    java.srcDir(root.resolveAll("src", "test", "examples"))
-  }
-  sourceCompatibility = JavaVersion.VERSION_1_8
-  targetCompatibility = JavaVersion.VERSION_1_8
-}
-
-dependencies {
-  testCompileOnly(Deps.mockito)
-}
-
-val thirdPartyCompileDependenciesTask = ensureThirdPartyDependencies(
-  "compileDeps",
-  listOf(Jdk.JDK_11.getThirdPartyDependency()))
-
-tasks {
-  compileTestJava {
-    options.compilerArgs = listOf("-g:source,lines")
-  }
-
-  register<JavaCompile>("debuginfo-all") {
-    source = sourceSets.test.get().allSource
-    include("**/*.java")
-    classpath = sourceSets.test.get().compileClasspath
-    destinationDirectory.set(getRoot().resolveAll("build","test","examples","classes_debuginfo_all"))
-    options.compilerArgs = listOf("-g")
-  }
-
-  register<JavaCompile>("debuginfo-none") {
-    source = sourceSets.test.get().allSource
-    include("**/*.java")
-    classpath = sourceSets.test.get().compileClasspath
-    destinationDirectory.set(getRoot().resolveAll("build","test","examples","classes_debuginfo_none"))
-    options.compilerArgs = listOf("-g:none")
-  }
-}
-
-// We just need to register the examples jars for it to be referenced by other modules. This should
-// be placed after all task registrations.
-val buildExampleJars = buildExampleJars("examples")
diff --git a/d8_r8/test_modules/tests_java_examples/settings.gradle.kts b/d8_r8/test_modules/tests_java_examples/settings.gradle.kts
deleted file mode 100644
index 77e513f..0000000
--- a/d8_r8/test_modules/tests_java_examples/settings.gradle.kts
+++ /dev/null
@@ -1,27 +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.
-
-pluginManagement {
-  repositories {
-    maven {
-      url = uri("file:../../../third_party/dependencies")
-    }
-    maven {
-      url = uri("file:../../../third_party/dependencies_new")
-    }
-  }
-}
-
-dependencyResolutionManagement {
-  repositories {
-    maven {
-      url = uri("file:../../../third_party/dependencies")
-    }
-    maven {
-      url = uri("file:../../../third_party/dependencies_new")
-    }
-  }
-}
-
-rootProject.name = "tests_java_examples"
diff --git a/d8_r8/test_modules/tests_java_examplesAndroidN/build.gradle.kts b/d8_r8/test_modules/tests_java_examplesAndroidN/build.gradle.kts
deleted file mode 100644
index 57b641f..0000000
--- a/d8_r8/test_modules/tests_java_examplesAndroidN/build.gradle.kts
+++ /dev/null
@@ -1,40 +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.
-
-import org.gradle.api.JavaVersion
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
-
-plugins {
-  `kotlin-dsl`
-  `java-library`
-  id("dependencies-plugin")
-}
-
-val root = getRoot()
-
-java {
-  sourceSets.test.configure {
-    java.srcDirs.clear()
-    java.srcDir(root.resolveAll("src", "test", "examplesAndroidN"))
-  }
-  sourceCompatibility = JavaVersion.VERSION_1_8
-  targetCompatibility = JavaVersion.VERSION_1_8
-}
-
-dependencies {
-  testCompileOnly(Deps.asm)
-}
-
-// We just need to register the examples jars for it to be referenced by other modules.
-val buildExampleJars = buildExampleJars("examplesAndroidN")
-
-val thirdPartyCompileDependenciesTask = ensureThirdPartyDependencies(
-  "compileDeps",
-  listOf(Jdk.JDK_11.getThirdPartyDependency()))
-
-tasks {
-  withType<JavaCompile> {
-    dependsOn(thirdPartyCompileDependenciesTask)
-  }
-}
diff --git a/d8_r8/test_modules/tests_java_examplesAndroidN/gradle.properties b/d8_r8/test_modules/tests_java_examplesAndroidN/gradle.properties
deleted file mode 100644
index a82d85e..0000000
--- a/d8_r8/test_modules/tests_java_examplesAndroidN/gradle.properties
+++ /dev/null
@@ -1,18 +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.
-
-org.gradle.jvmargs=-Xmx2048M -Dfile.encoding=UTF-8
-kotlin.daemon.jvmargs=-Xmx3g -Dkotlin.js.compiler.legacy.force_enabled=true
-systemProp.file.encoding=UTF-8
-
-# Enable new incremental compilation
-kotlin.incremental.useClasspathSnapshot=true
-
-org.gradle.parallel=true
-org.gradle.caching=false
-org.gradle.configuration-cache=true
-
-# Do not download any jdks or detect them. We provide them.
-org.gradle.java.installations.auto-detect=false
-org.gradle.java.installations.auto-download=false
diff --git a/d8_r8/test_modules/tests_java_examplesAndroidN/settings.gradle.kts b/d8_r8/test_modules/tests_java_examplesAndroidN/settings.gradle.kts
deleted file mode 100644
index 000e19b..0000000
--- a/d8_r8/test_modules/tests_java_examplesAndroidN/settings.gradle.kts
+++ /dev/null
@@ -1,27 +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.
-
-pluginManagement {
-  repositories {
-    maven {
-      url = uri("file:../../../third_party/dependencies")
-    }
-    maven {
-      url = uri("file:../../../third_party/dependencies_new")
-    }
-  }
-}
-
-dependencyResolutionManagement {
-  repositories {
-    maven {
-      url = uri("file:../../../third_party/dependencies")
-    }
-    maven {
-      url = uri("file:../../../third_party/dependencies_new")
-    }
-  }
-}
-
-rootProject.name = "tests_java_examplesAndroidN"
diff --git a/d8_r8/test_modules/tests_java_examplesAndroidO/build.gradle.kts b/d8_r8/test_modules/tests_java_examplesAndroidO/build.gradle.kts
deleted file mode 100644
index 062128c..0000000
--- a/d8_r8/test_modules/tests_java_examplesAndroidO/build.gradle.kts
+++ /dev/null
@@ -1,52 +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.
-
-import org.gradle.api.JavaVersion
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
-
-plugins {
-  `kotlin-dsl`
-  `java-library`
-  id("dependencies-plugin")
-}
-
-val root = getRoot()
-
-java {
-  sourceSets.test.configure {
-    java.srcDirs.clear()
-    java.srcDir(root.resolveAll("src", "test", "examplesAndroidO"))
-  }
-  sourceCompatibility = JavaVersion.VERSION_1_8
-  targetCompatibility = JavaVersion.VERSION_1_8
-}
-
-// NOTE: we want to enable a scenario when test needs to reference some classes generated by legacy
-// (1.6) Java compiler to test some specific behaviour. To do so we compile all the java files
-// located in a sub-directory called 'legacy' with Java 1.6, then compile the rest of the files with
-// Java 1.8 and a reference to previously generated 1.6 classes.
-
-dependencies {
-  testCompileOnly(Deps.asm)
-  testCompileOnly(resolve(ThirdPartyDeps.examplesAndroidOLegacy))
-  testCompileOnly(resolve(getThirdPartyAndroidJar("lib-v26"),"android.jar"))
-}
-
-// We just need to register the examples jars for it to be referenced by other modules.
-val buildExampleJars = buildExampleJars("examplesAndroidO")
-
-val thirdPartyCompileDependenciesTask = ensureThirdPartyDependencies(
-  "compileDeps",
-  listOf(
-    ThirdPartyDeps.examplesAndroidOLegacy,
-    Jdk.JDK_11.getThirdPartyDependency(),
-    getThirdPartyAndroidJar("lib-v26")))
-
-tasks {
-  withType<JavaCompile> {
-    dependsOn(thirdPartyCompileDependenciesTask)
-    options.compilerArgs.add("-Xlint:-options")
-    options.compilerArgs.add("-parameters")
-  }
-}
diff --git a/d8_r8/test_modules/tests_java_examplesAndroidO/gradle.properties b/d8_r8/test_modules/tests_java_examplesAndroidO/gradle.properties
deleted file mode 100644
index a82d85e..0000000
--- a/d8_r8/test_modules/tests_java_examplesAndroidO/gradle.properties
+++ /dev/null
@@ -1,18 +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.
-
-org.gradle.jvmargs=-Xmx2048M -Dfile.encoding=UTF-8
-kotlin.daemon.jvmargs=-Xmx3g -Dkotlin.js.compiler.legacy.force_enabled=true
-systemProp.file.encoding=UTF-8
-
-# Enable new incremental compilation
-kotlin.incremental.useClasspathSnapshot=true
-
-org.gradle.parallel=true
-org.gradle.caching=false
-org.gradle.configuration-cache=true
-
-# Do not download any jdks or detect them. We provide them.
-org.gradle.java.installations.auto-detect=false
-org.gradle.java.installations.auto-download=false
diff --git a/d8_r8/test_modules/tests_java_examplesAndroidO/settings.gradle.kts b/d8_r8/test_modules/tests_java_examplesAndroidO/settings.gradle.kts
deleted file mode 100644
index d508d51..0000000
--- a/d8_r8/test_modules/tests_java_examplesAndroidO/settings.gradle.kts
+++ /dev/null
@@ -1,27 +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.
-
-pluginManagement {
-  repositories {
-    maven {
-      url = uri("file:../../../third_party/dependencies")
-    }
-    maven {
-      url = uri("file:../../../third_party/dependencies_new")
-    }
-  }
-}
-
-dependencyResolutionManagement {
-  repositories {
-    maven {
-      url = uri("file:../../../third_party/dependencies")
-    }
-    maven {
-      url = uri("file:../../../third_party/dependencies_new")
-    }
-  }
-}
-
-rootProject.name = "tests_java_examplesAndroidO"
diff --git a/d8_r8/test_modules/tests_java_examplesAndroidP/build.gradle.kts b/d8_r8/test_modules/tests_java_examplesAndroidP/build.gradle.kts
deleted file mode 100644
index efed289..0000000
--- a/d8_r8/test_modules/tests_java_examplesAndroidP/build.gradle.kts
+++ /dev/null
@@ -1,40 +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.
-
-import org.gradle.api.JavaVersion
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
-
-plugins {
-  `kotlin-dsl`
-  `java-library`
-  id("dependencies-plugin")
-}
-
-val root = getRoot()
-
-java {
-  sourceSets.test.configure {
-    java.srcDirs.clear()
-    java.srcDir(root.resolveAll("src", "test", "examplesAndroidP"))
-  }
-  sourceCompatibility = JavaVersion.VERSION_1_8
-  targetCompatibility = JavaVersion.VERSION_1_8
-}
-
-dependencies {
-  testCompileOnly(Deps.asm)
-}
-
-// We just need to register the examples jars for it to be referenced by other modules.
-val buildExampleJars = buildExampleJars("examplesAndroidP")
-
-val thirdPartyCompileDependenciesTask = ensureThirdPartyDependencies(
-  "compileDeps",
-  listOf(Jdk.JDK_11.getThirdPartyDependency()))
-
-tasks {
-  withType<JavaCompile> {
-    dependsOn(thirdPartyCompileDependenciesTask)
-  }
-}
diff --git a/d8_r8/test_modules/tests_java_examplesAndroidP/gradle.properties b/d8_r8/test_modules/tests_java_examplesAndroidP/gradle.properties
deleted file mode 100644
index a82d85e..0000000
--- a/d8_r8/test_modules/tests_java_examplesAndroidP/gradle.properties
+++ /dev/null
@@ -1,18 +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.
-
-org.gradle.jvmargs=-Xmx2048M -Dfile.encoding=UTF-8
-kotlin.daemon.jvmargs=-Xmx3g -Dkotlin.js.compiler.legacy.force_enabled=true
-systemProp.file.encoding=UTF-8
-
-# Enable new incremental compilation
-kotlin.incremental.useClasspathSnapshot=true
-
-org.gradle.parallel=true
-org.gradle.caching=false
-org.gradle.configuration-cache=true
-
-# Do not download any jdks or detect them. We provide them.
-org.gradle.java.installations.auto-detect=false
-org.gradle.java.installations.auto-download=false
diff --git a/d8_r8/test_modules/tests_java_examplesAndroidP/settings.gradle.kts b/d8_r8/test_modules/tests_java_examplesAndroidP/settings.gradle.kts
deleted file mode 100644
index 1e0be82..0000000
--- a/d8_r8/test_modules/tests_java_examplesAndroidP/settings.gradle.kts
+++ /dev/null
@@ -1,27 +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.
-
-pluginManagement {
-  repositories {
-    maven {
-      url = uri("file:../../../third_party/dependencies")
-    }
-    maven {
-      url = uri("file:../../../third_party/dependencies_new")
-    }
-  }
-}
-
-dependencyResolutionManagement {
-  repositories {
-    maven {
-      url = uri("file:../../../third_party/dependencies")
-    }
-    maven {
-      url = uri("file:../../../third_party/dependencies_new")
-    }
-  }
-}
-
-rootProject.name = "tests_java_examplesAndroidP"
diff --git a/d8_r8/test_modules/tests_java_kotlinR8TestResources/build.gradle.kts b/d8_r8/test_modules/tests_java_kotlinR8TestResources/build.gradle.kts
deleted file mode 100644
index 06d9518..0000000
--- a/d8_r8/test_modules/tests_java_kotlinR8TestResources/build.gradle.kts
+++ /dev/null
@@ -1,45 +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.
-
-import org.gradle.api.JavaVersion
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
-
-plugins {
-  `kotlin-dsl`
-  `java-library`
-  id("dependencies-plugin")
-}
-
-val root = getRoot()
-
-java {
-  sourceSets.test.configure {
-    java.srcDirs.clear()
-    java.srcDir(root.resolveAll("src", "test", "kotlinR8TestResources"))
-  }
-  sourceCompatibility = JavaVersion.VERSION_1_8
-  targetCompatibility = JavaVersion.VERSION_1_8
-}
-
-dependencies {
-  testCompileOnly(Deps.asm)
-}
-
-// We just need to register the examples jars for it to be referenced by other modules.
-val buildExampleJars = buildExampleJars("kotlinR8TestResources")
-
-val thirdPartyCompileDependenciesTask = ensureThirdPartyDependencies(
-  "compileDeps",
-  listOf(Jdk.JDK_11.getThirdPartyDependency()))
-
-tasks {
-  withType<JavaCompile> {
-    dependsOn(thirdPartyCompileDependenciesTask)
-  }
-  withType<KotlinCompile> {
-    kotlinOptions {
-      jvmTarget = "1.8"
-    }
-  }
-}
diff --git a/d8_r8/test_modules/tests_java_kotlinR8TestResources/gradle.properties b/d8_r8/test_modules/tests_java_kotlinR8TestResources/gradle.properties
deleted file mode 100644
index a82d85e..0000000
--- a/d8_r8/test_modules/tests_java_kotlinR8TestResources/gradle.properties
+++ /dev/null
@@ -1,18 +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.
-
-org.gradle.jvmargs=-Xmx2048M -Dfile.encoding=UTF-8
-kotlin.daemon.jvmargs=-Xmx3g -Dkotlin.js.compiler.legacy.force_enabled=true
-systemProp.file.encoding=UTF-8
-
-# Enable new incremental compilation
-kotlin.incremental.useClasspathSnapshot=true
-
-org.gradle.parallel=true
-org.gradle.caching=false
-org.gradle.configuration-cache=true
-
-# Do not download any jdks or detect them. We provide them.
-org.gradle.java.installations.auto-detect=false
-org.gradle.java.installations.auto-download=false
diff --git a/d8_r8/test_modules/tests_java_kotlinR8TestResources/settings.gradle.kts b/d8_r8/test_modules/tests_java_kotlinR8TestResources/settings.gradle.kts
deleted file mode 100644
index 7eb50aa..0000000
--- a/d8_r8/test_modules/tests_java_kotlinR8TestResources/settings.gradle.kts
+++ /dev/null
@@ -1,27 +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.
-
-pluginManagement {
-  repositories {
-    maven {
-      url = uri("file:../../../third_party/dependencies")
-    }
-    maven {
-      url = uri("file:../../../third_party/dependencies_new")
-    }
-  }
-}
-
-dependencyResolutionManagement {
-  repositories {
-    maven {
-      url = uri("file:../../../third_party/dependencies")
-    }
-    maven {
-      url = uri("file:../../../third_party/dependencies_new")
-    }
-  }
-}
-
-rootProject.name = "tests_java_kotlinR8TestResources"
diff --git a/infra/config/global/generated/cr-buildbucket.cfg b/infra/config/global/generated/cr-buildbucket.cfg
index d929383..1c1f973 100644
--- a/infra/config/global/generated/cr-buildbucket.cfg
+++ b/infra/config/global/generated/cr-buildbucket.cfg
@@ -37,7 +37,7 @@
         '  "test_wrapper": "tools/archive.py"'
         '}'
       priority: 25
-      execution_timeout_secs: 1800
+      execution_timeout_secs: 3600
       expiration_secs: 126000
       build_numbers: YES
       service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
@@ -65,7 +65,7 @@
         '  "test_wrapper": "tools/archive.py"'
         '}'
       priority: 25
-      execution_timeout_secs: 1800
+      execution_timeout_secs: 3600
       expiration_secs: 126000
       build_numbers: YES
       service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
diff --git a/infra/config/global/generated/project.cfg b/infra/config/global/generated/project.cfg
index 3f70a4d..755c550 100644
--- a/infra/config/global/generated/project.cfg
+++ b/infra/config/global/generated/project.cfg
@@ -7,7 +7,7 @@
 name: "r8"
 access: "group:all"
 lucicfg {
-  version: "1.39.14"
+  version: "1.39.15"
   package_dir: ".."
   config_dir: "generated"
   entry_point: "main.star"
diff --git a/infra/config/global/main.star b/infra/config/global/main.star
index 885d58e..aca08c1 100755
--- a/infra/config/global/main.star
+++ b/infra/config/global/main.star
@@ -282,7 +282,7 @@
         priority = 25,
         trigger = not desugar,
         properties = properties,
-        execution_timeout = time.hour * 1 if desugar else time.minute * 30 ,
+        execution_timeout = time.hour * 1,
         expiration_timeout = time.hour * 35,
     )
 archivers()
diff --git a/scripts/update-host-art.sh b/scripts/update-host-art.sh
index 44e5a49..ef7670b 100755
--- a/scripts/update-host-art.sh
+++ b/scripts/update-host-art.sh
@@ -135,7 +135,9 @@
 
 # Required framework files.
 mkdir -p $DEST/framework
+mkdir -p $DEST/out/host/linux-x86/framework
 cp -R $ANDROID_HOST_BUILD/framework/* $DEST/framework
+cp $ANDROID_HOST_BUILD/framework/*.jar $DEST/out/host/linux-x86/framework
 
 # Required library files.
 mkdir -p $DEST/lib64
@@ -202,4 +204,4 @@
 
 echo "Now run"
 echo "(cd $DEST_ROOT; upload_to_google_storage.py -a --bucket r8-deps $ART_DIR)"
-echo "NOTE; If $ART_DIR has several directory elements adjust accordingly."
\ No newline at end of file
+echo "NOTE; If $ART_DIR has several directory elements adjust accordingly."
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index bd0152e..c210df5 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -147,12 +147,22 @@
       return super.addClasspathResourceProvider(provider);
     }
 
-    /** Set input proguard map used for distribution of classes in multi-dex. */
+    /**
+     * Set input proguard map used for distribution of classes in multi-dex. Use {@link
+     * #setProguardMapInputFile}
+     */
+    @Deprecated()
     public Builder setProguardInputMapFile(Path proguardInputMap) {
       getAppBuilder().setProguardMapInputData(proguardInputMap);
       return self();
     }
 
+    /** Set input proguard map used for distribution of classes in multi-dex. */
+    public Builder setProguardMapInputFile(Path proguardInputMap) {
+      getAppBuilder().setProguardMapInputData(proguardInputMap);
+      return self();
+    }
+
     /**
      * Set a consumer for receiving the proguard-map content.
      *
diff --git a/src/main/java/com/android/tools/r8/D8CommandParser.java b/src/main/java/com/android/tools/r8/D8CommandParser.java
index 8bf79ed..49360fd 100644
--- a/src/main/java/com/android/tools/r8/D8CommandParser.java
+++ b/src/main/java/com/android/tools/r8/D8CommandParser.java
@@ -265,7 +265,7 @@
       } else if (arg.equals("--classfile")) {
         outputMode = OutputMode.ClassFile;
       } else if (arg.equals("--pg-map")) {
-        builder.setProguardInputMapFile(Paths.get(nextArg));
+        builder.setProguardMapInputFile(Paths.get(nextArg));
       } else if (arg.equals("--pg-map-output")) {
         builder.setProguardMapOutputPath(Paths.get(nextArg));
       } else if (arg.equals("--partition-map-output")) {
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index fc1122a..1decde2 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -46,6 +46,7 @@
 import com.android.tools.r8.utils.InternalOptions.DesugarState;
 import com.android.tools.r8.utils.InternalOptions.HorizontalClassMergerOptions;
 import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization;
+import com.android.tools.r8.utils.InternalOptions.MappingComposeOptions;
 import com.android.tools.r8.utils.ProgramClassCollection;
 import com.android.tools.r8.utils.Reporter;
 import com.android.tools.r8.utils.SemanticVersion;
@@ -289,6 +290,12 @@
       return super.setProguardMapOutputPath(proguardMapOutput);
     }
 
+    /** Set input proguard map used for distribution of classes in multi-dex. */
+    public Builder setProguardMapInputFile(Path proguardInputMap) {
+      getAppBuilder().setProguardMapInputData(proguardInputMap);
+      return self();
+    }
+
     /**
      * Set a consumer for receiving the proguard-map content.
      *
@@ -1091,6 +1098,8 @@
         (internal.isOptimizing() || internal.isMinifying())
             ? LineNumberOptimization.ON
             : LineNumberOptimization.OFF;
+    MappingComposeOptions mappingComposeOptions = internal.mappingComposeOptions();
+    mappingComposeOptions.enableExperimentalMappingComposition = true;
 
     HorizontalClassMergerOptions horizontalClassMergerOptions =
         internal.horizontalClassMergerOptions();
diff --git a/src/main/java/com/android/tools/r8/R8CommandParser.java b/src/main/java/com/android/tools/r8/R8CommandParser.java
index f5f22bf..d88314d 100644
--- a/src/main/java/com/android/tools/r8/R8CommandParser.java
+++ b/src/main/java/com/android/tools/r8/R8CommandParser.java
@@ -42,6 +42,7 @@
           "--main-dex-list-output",
           "--pg-conf",
           "--pg-conf-output",
+          "--pg-map",
           "--pg-map-output",
           "--partition-map-output",
           "--desugared-lib",
@@ -71,6 +72,12 @@
         .add(flag0("--pg-compat", "Compile with R8 in Proguard compatibility mode."))
         .add(ParseFlagInfoImpl.getPgConf())
         .add(flag1("--pg-conf-output", "<file>", "Output the collective configuration to <file>."))
+        .add(
+            ParseFlagInfoImpl.flag1(
+                "--pg-map",
+                "<file>",
+                "Use <file> as a mapping file for distribution "
+                    + "and composition with output mapping file."))
         .add(ParseFlagInfoImpl.getPgMapOutput())
         .add(ParseFlagInfoImpl.getPartitionMapOutput())
         .add(ParseFlagInfoImpl.getDesugaredLib())
@@ -113,6 +120,7 @@
                 "  %MAP_HASH: compiler generated mapping hash."))
         .add(ParseFlagInfoImpl.getAndroidPlatformBuild())
         .add(ParseFlagInfoImpl.getArtProfile())
+        .add(ParseFlagInfoImpl.getStartupProfile())
         .add(ParseFlagInfoImpl.getVersion("r8"))
         .add(ParseFlagInfoImpl.getHelp())
         .build();
@@ -294,6 +302,8 @@
       } else if (arg.equals("--pg-conf-output")) {
         FileConsumer consumer = new FileConsumer(Paths.get(nextArg));
         builder.setProguardConfigurationConsumer(consumer);
+      } else if (arg.equals("--pg-map")) {
+        builder.setProguardMapInputFile(Paths.get(nextArg));
       } else if (arg.equals("--pg-map-output")) {
         builder.setProguardMapOutputPath(Paths.get(nextArg));
       } else if (arg.equals("--partition-map-output")) {
diff --git a/src/main/java/com/android/tools/r8/cf/CfVersion.java b/src/main/java/com/android/tools/r8/cf/CfVersion.java
index f69e532..c85f50f 100644
--- a/src/main/java/com/android/tools/r8/cf/CfVersion.java
+++ b/src/main/java/com/android/tools/r8/cf/CfVersion.java
@@ -46,6 +46,8 @@
   public static final CfVersion V20_PREVIEW = new CfVersion(Opcodes.V20 | Opcodes.V_PREVIEW);
   public static final CfVersion V21 = new CfVersion(Opcodes.V21);
   public static final CfVersion V21_PREVIEW = new CfVersion(Opcodes.V21 | Opcodes.V_PREVIEW);
+  public static final CfVersion V22 = new CfVersion(66);
+  public static final CfVersion V22_PREVIEW = new CfVersion(66 | Opcodes.V_PREVIEW);
 
   private final int version;
 
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index 30bbe20..4a8b427 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -31,6 +31,7 @@
 import com.android.tools.r8.ir.analysis.value.AbstractValueJoiner.AbstractValueParameterJoiner;
 import com.android.tools.r8.ir.desugar.TypeRewriter;
 import com.android.tools.r8.ir.optimize.enums.EnumDataMap;
+import com.android.tools.r8.ir.optimize.info.MethodResolutionOptimizationInfoCollection;
 import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoFactory;
 import com.android.tools.r8.ir.optimize.library.LibraryMemberOptimizer;
 import com.android.tools.r8.ir.optimize.library.LibraryMethodSideEffectModelCollection;
@@ -95,6 +96,8 @@
   private NamingLens namingLens = NamingLens.getIdentityLens();
   private ProguardCompatibilityActions proguardCompatibilityActions;
   private RootSet rootSet;
+  private MethodResolutionOptimizationInfoCollection methodResolutionOptimizationInfoCollection =
+      MethodResolutionOptimizationInfoCollection.empty();
   private MainDexRootSet mainDexRootSet = null;
   private StartupProfile startupProfile;
 
@@ -340,6 +343,16 @@
     return abstractValueParameterJoiner;
   }
 
+  public MethodResolutionOptimizationInfoCollection
+      getMethodResolutionOptimizationInfoCollection() {
+    return methodResolutionOptimizationInfoCollection;
+  }
+
+  public void setMethodResolutionOptimizationInfoCollection(
+      MethodResolutionOptimizationInfoCollection getMethodResolutionOptimizationInfoCollection) {
+    this.methodResolutionOptimizationInfoCollection = getMethodResolutionOptimizationInfoCollection;
+  }
+
   public InstanceFieldInitializationInfoFactory instanceFieldInitializationInfoFactory() {
     return instanceFieldInitializationInfoFactory;
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java b/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
index c4eace2..c154471 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
@@ -57,6 +57,7 @@
   private int emittedPc = NO_PC_INFO;
   private Position emittedPosition = Position.none();
   private Int2ReferenceMap<DebugLocalInfo> emittedLocals;
+  private final boolean isComposing;
 
   // Emitted events.
   private final List<DexDebugEvent> events = new ArrayList<>();
@@ -66,9 +67,12 @@
 
   public DexDebugEventBuilder(AppView<?> appView, IRCode code) {
     this.appView = appView;
-    this.method = code.method();
-    this.factory = appView.dexItemFactory();
-    this.options = appView.options();
+    method = code.context().getDefinition();
+    factory = appView.dexItemFactory();
+    options = appView.options();
+    isComposing =
+        options.mappingComposeOptions().enableExperimentalMappingComposition
+            && appView.appInfo().app().getProguardMap() != null;
   }
 
   /** Add events at pc for instruction. */
@@ -94,7 +98,9 @@
       updateLocals(instruction.asDebugLocalsChange());
     } else if (pcAdvancing) {
       if (!position.isNone() && !position.equals(emittedPosition)) {
-        if (options.debug || instruction.instructionInstanceCanThrow(appView, context)) {
+        if (options.debug
+            || instruction.instructionInstanceCanThrow(appView, context)
+            || isComposing) {
           emitDebugPosition(pc, position);
         }
       }
diff --git a/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java b/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java
index d7362d6..428c57d 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java
@@ -8,7 +8,11 @@
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.LookupResult.LookupResultSuccess;
 import com.android.tools.r8.graph.LookupResult.LookupResultSuccess.LookupResultCollectionState;
+import com.android.tools.r8.ir.code.InvokeMethod;
 import com.android.tools.r8.ir.desugar.LambdaDescriptor;
+import com.android.tools.r8.ir.optimize.info.DefaultMethodOptimizationInfo;
+import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo;
+import com.android.tools.r8.ir.optimize.info.MethodResolutionOptimizationInfoCollection;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.InstantiatedObject;
 import com.android.tools.r8.utils.BooleanBox;
@@ -283,6 +287,19 @@
     public abstract SingleResolutionResult<T> withInitialResolutionHolder(
         DexClass newInitialResolutionHolder);
 
+    public MethodOptimizationInfo getOptimizationInfo(
+        AppView<?> appView, InvokeMethod invoke, DexClassAndMethod singleTarget) {
+      if (singleTarget != null) {
+        return singleTarget.getOptimizationInfo();
+      }
+      if (invoke.isInvokeMethodWithDynamicDispatch() && resolvedMethod.belongsToVirtualPool()) {
+        MethodResolutionOptimizationInfoCollection methodResolutionOptimizationInfoCollection =
+            appView.getMethodResolutionOptimizationInfoCollection();
+        return methodResolutionOptimizationInfoCollection.get(resolvedMethod, resolvedHolder);
+      }
+      return DefaultMethodOptimizationInfo.getInstance();
+    }
+
     @Override
     public DexClass getInitialResolutionHolder() {
       return initialResolutionHolder;
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 ade2770..0341252 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
@@ -801,7 +801,7 @@
     }
 
     // TODO(mkroghj) Test if shorten live ranges is worth it.
-    if (!options.isGeneratingClassFiles()) {
+    if (options.isGeneratingDex()) {
       timing.begin("Canonicalize constants");
       ConstantCanonicalizer constantCanonicalizer =
           new ConstantCanonicalizer(appView, context, code);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java
index bc66527..cc12b6e 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java
@@ -18,6 +18,7 @@
 import com.android.tools.r8.ir.analysis.fieldaccess.TrivialFieldAccessReprocessor;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.conversion.passes.FilledNewArrayRewriter;
+import com.android.tools.r8.ir.optimize.ConstantCanonicalizer;
 import com.android.tools.r8.ir.optimize.DeadCodeRemover;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackDelayed;
 import com.android.tools.r8.lightir.LirCode;
@@ -336,7 +337,13 @@
       method.setCode(rewrittenLirCode, appView);
     }
     IRCode irCode = method.buildIR(appView, MethodConversionOptions.forPostLirPhase(appView));
-    new FilledNewArrayRewriter(appView).run(irCode, onThreadTiming);
+    FilledNewArrayRewriter filledNewArrayRewriter = new FilledNewArrayRewriter(appView);
+    boolean changed = filledNewArrayRewriter.run(irCode, onThreadTiming).hasChanged().toBoolean();
+    if (appView.options().isGeneratingDex() && changed) {
+      ConstantCanonicalizer constantCanonicalizer =
+          new ConstantCanonicalizer(appView, method, irCode);
+      constantCanonicalizer.canonicalize();
+    }
     // Processing is done and no further uses of the meta-data should arise.
     BytecodeMetadataProvider noMetadata = BytecodeMetadataProvider.empty();
     // During processing optimization info may cause previously live code to become dead.
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/passes/FilledNewArrayRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/passes/FilledNewArrayRewriter.java
index 13e590b..e269dc1 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/passes/FilledNewArrayRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/passes/FilledNewArrayRewriter.java
@@ -4,10 +4,10 @@
 
 package com.android.tools.r8.ir.conversion.passes;
 
-import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexItem;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
@@ -28,6 +28,7 @@
 import com.android.tools.r8.ir.code.NewArrayFilled;
 import com.android.tools.r8.ir.code.NewArrayFilledData;
 import com.android.tools.r8.ir.code.Position;
+import com.android.tools.r8.ir.code.StaticGet;
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.ir.conversion.passes.result.CodeRewriterResult;
 import com.android.tools.r8.utils.InternalOptions.RewriteArrayOptions;
@@ -303,13 +304,15 @@
 
   private static class ConstantMaterializingInstructionCache {
 
-    // All DEX aput instructions takes an 8-bit wide register value for the source.
-    private final int MAX_MATERIALIZING_CONSTANTS = Constants.U8BIT_MAX - 16;
+    private final RewriteArrayOptions rewriteArrayOptions;
+
     // Track constants as DexItems, DexString for string constants and DexType for class constants.
     private final Map<DexItem, Integer> constantOccurrences = new IdentityHashMap<>();
     private final Map<DexItem, Value> constantValue = new IdentityHashMap<>();
 
-    private ConstantMaterializingInstructionCache(NewArrayFilled newArrayFilled) {
+    private ConstantMaterializingInstructionCache(
+        RewriteArrayOptions rewriteArrayOptions, NewArrayFilled newArrayFilled) {
+      this.rewriteArrayOptions = rewriteArrayOptions;
       for (Value elementValue : newArrayFilled.inValues()) {
         if (elementValue.hasAnyUsers()) {
           continue;
@@ -318,6 +321,8 @@
           addOccurrence(elementValue.getDefinition().asConstString().getValue());
         } else if (elementValue.isConstClass()) {
           addOccurrence(elementValue.getDefinition().asConstClass().getValue());
+        } else if (elementValue.isDefinedByInstructionSatisfying(Instruction::isStaticGet)) {
+          addOccurrence(elementValue.getDefinition().asStaticGet().getField());
         }
         // Don't canonicalize numbers, as on DEX FilledNewArray is supported for primitives
         // on all versions.
@@ -339,25 +344,45 @@
           seenOcourence(type);
           return value;
         }
+      } else if (elementValue.isDefinedByInstructionSatisfying(Instruction::isStaticGet)) {
+        DexField field = elementValue.getDefinition().asStaticGet().getField();
+        Value value = constantValue.get(field);
+        if (value != null) {
+          seenOcourence(field);
+          return value;
+        }
       }
       return null;
     }
 
+    // Order: String > field > class.
     private DexItem smallestConstant(DexItem c1, DexItem c2) {
       if (c1 instanceof DexString) {
         if (c2 instanceof DexString) {
           return ((DexString) c1).compareTo((DexString) c2) < 0 ? c1 : c2;
         } else {
-          assert c2 instanceof DexType;
-          return c2; // String larger than class.
+          assert c2 instanceof DexField || c2 instanceof DexType;
+          return c2; // String larger than field and class.
+        }
+      } else if (c1 instanceof DexField) {
+        if (c2 instanceof DexField) {
+          return ((DexField) c1).compareTo((DexField) c2) < 0 ? c1 : c2;
+        } else {
+          // Field less than string, larger than class
+          if (c2 instanceof DexString) {
+            return c1;
+          } else {
+            assert c2 instanceof DexType;
+            return c2;
+          }
         }
       } else {
         assert c1 instanceof DexType;
         if (c2 instanceof DexType) {
           return ((DexType) c1).compareTo((DexType) c2) < 0 ? c1 : c2;
         } else {
-          assert c2 instanceof DexString;
-          return c1; // String larger than class.
+          assert c2 instanceof DexString || c2 instanceof DexField;
+          return c1; // Class less than string and field.
         }
       }
     }
@@ -366,6 +391,8 @@
       Instruction instruction = value.getDefinition();
       if (instruction.isConstString()) {
         return instruction.asConstString().getValue();
+      } else if (instruction.isStaticGet()) {
+        return instruction.asStaticGet().getField();
       } else {
         assert instruction.isConstClass();
         return instruction.asConstClass().getValue();
@@ -376,10 +403,10 @@
       DexItem constant = getConstant(value);
       assert constantOccurrences.containsKey(constant);
       assert !constantValue.containsKey(constant);
-      if (constantValue.size() < MAX_MATERIALIZING_CONSTANTS) {
+      if (constantValue.size() < rewriteArrayOptions.maxMaterializingConstants) {
         constantValue.put(constant, value);
       } else {
-        assert constantValue.size() == MAX_MATERIALIZING_CONSTANTS;
+        assert constantValue.size() == rewriteArrayOptions.maxMaterializingConstants;
         // Find the least valuable active constant.
         int leastOccurrences = Integer.MAX_VALUE;
         DexItem valueWithLeastOccurrences = null;
@@ -403,7 +430,7 @@
           constantValue.remove(valueWithLeastOccurrences);
           constantValue.put(constant, value);
         }
-        assert constantValue.size() == MAX_MATERIALIZING_CONSTANTS;
+        assert constantValue.size() == rewriteArrayOptions.maxMaterializingConstants;
       }
       seenOcourence(constant);
     }
@@ -437,7 +464,7 @@
     NewArrayEmpty newArrayEmpty = rewriteToNewArrayEmpty(code, instructionIterator, newArrayFilled);
 
     ConstantMaterializingInstructionCache constantMaterializingInstructionCache =
-        new ConstantMaterializingInstructionCache(newArrayFilled);
+        new ConstantMaterializingInstructionCache(rewriteArrayOptions, newArrayFilled);
 
     int index = 0;
     for (Value elementValue : newArrayFilled.inValues()) {
@@ -484,25 +511,29 @@
     if (elementValue.hasAnyUsers()
         || !(elementValue.isConstString()
             || elementValue.isConstNumber()
-            || elementValue.isConstClass())) {
+            || elementValue.isConstClass()
+            || elementValue.isDefinedByInstructionSatisfying(Instruction::isStaticGet))) {
       return elementValue;
     }
 
     Value existingValue = constantMaterializingInstructionCache.getValue(elementValue);
     if (existingValue != null) {
-      addToRemove(elementValue.definition);
+      addToRemove(elementValue.getDefinition());
       return existingValue;
     }
 
     Instruction copy;
     if (elementValue.isConstNumber()) {
-      copy = ConstNumber.copyOf(code, elementValue.definition.asConstNumber());
+      copy = ConstNumber.copyOf(code, elementValue.getDefinition().asConstNumber());
     } else if (elementValue.isConstString()) {
-      copy = ConstString.copyOf(code, elementValue.definition.asConstString());
+      copy = ConstString.copyOf(code, elementValue.getDefinition().asConstString());
       constantMaterializingInstructionCache.putNewValue(copy.asConstString().outValue());
     } else if (elementValue.isConstClass()) {
-      copy = ConstClass.copyOf(code, elementValue.definition.asConstClass());
+      copy = ConstClass.copyOf(code, elementValue.getDefinition().asConstClass());
       constantMaterializingInstructionCache.putNewValue(copy.asConstClass().outValue());
+    } else if (elementValue.isDefinedByInstructionSatisfying(Instruction::isStaticGet)) {
+      copy = StaticGet.copyOf(code, elementValue.getDefinition().asStaticGet());
+      constantMaterializingInstructionCache.putNewValue(copy.asStaticGet().outValue());
     } else {
       assert false;
       return elementValue;
@@ -510,7 +541,7 @@
     copy.setBlock(instructionIterator.getBlock());
     copy.setPosition(newArrayEmpty.getPosition());
     instructionIterator.add(copy);
-    addToRemove(elementValue.definition);
+    addToRemove(elementValue.getDefinition());
     return copy.outValue();
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/AssumeInserter.java b/src/main/java/com/android/tools/r8/ir/optimize/AssumeInserter.java
index 0680915..21abec1 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/AssumeInserter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/AssumeInserter.java
@@ -250,12 +250,13 @@
       }
     }
 
-    if (singleTarget == null) {
+    MethodOptimizationInfo optimizationInfo =
+        resolutionResult.getOptimizationInfo(appView, invoke, singleTarget);
+    if (optimizationInfo.isDefault()) {
       return false;
     }
 
     boolean needsAssumeInstruction = false;
-    MethodOptimizationInfo optimizationInfo = singleTarget.getDefinition().getOptimizationInfo();
 
     // Case (2), invocations that are guaranteed to return a non-null value.
     if (invoke.hasUsedOutValue()) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java b/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java
index 6405fb1..cdd49d1 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java
@@ -74,6 +74,7 @@
   private Set<InstanceGet> ineligibleInstanceGetInstructions;
 
   public ConstantCanonicalizer(AppView<?> appView, ProgramMethod context, IRCode code) {
+    assert appView.options().isGeneratingDex();
     this.appView = appView;
     this.branchSimplifier = new BranchSimplifier(appView);
     this.context = context;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
index f596d2a..ea5f65d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
@@ -33,7 +33,6 @@
   static final int UNKNOWN_RETURNED_ARGUMENT = -1;
   static final boolean UNKNOWN_NEVER_RETURNS_NORMALLY = false;
   static final AbstractValue UNKNOWN_ABSTRACT_RETURN_VALUE = UnknownValue.getInstance();
-  static final boolean UNKNOWN_TRIGGERS_CLASS_INIT_BEFORE_ANY_SIDE_EFFECT = false;
   static final boolean UNKNOWN_INITIALIZER_ENABLING_JAVA_ASSERTIONS = false;
   static final boolean UNKNOWN_MAY_HAVE_SIDE_EFFECTS = true;
   static final boolean UNKNOWN_RETURN_VALUE_ONLY_DEPENDS_ON_ARGUMENTS = false;
@@ -47,6 +46,11 @@
   }
 
   @Override
+  public boolean isDefault() {
+    return true;
+  }
+
+  @Override
   public boolean cannotBeKept() {
     return false;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
index 31775ae..6a2bc86 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
@@ -28,6 +28,10 @@
     Default
   }
 
+  public boolean isDefault() {
+    return false;
+  }
+
   public abstract boolean cannotBeKept();
 
   public abstract boolean classInitializerMayBePostponed();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodResolutionOptimizationInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodResolutionOptimizationInfoCollection.java
new file mode 100644
index 0000000..6c2c13b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodResolutionOptimizationInfoCollection.java
@@ -0,0 +1,43 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.info;
+
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexReference;
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * Collection of optimization info for virtual methods with dynamic dispatch.
+ *
+ * <p>When a call to a virtual method does not have a single target, we cannot use the optimization
+ * info we compute for each method. Given the resolved method, this collection returns a piece of
+ * optimization info that is true for all possible dispatch targets (i.e., the join of the
+ * optimization of all possible dispatch targets).
+ */
+public class MethodResolutionOptimizationInfoCollection {
+
+  private static final MethodResolutionOptimizationInfoCollection EMPTY =
+      new MethodResolutionOptimizationInfoCollection(Collections.emptyMap());
+
+  private final Map<DexReference, MethodOptimizationInfo> backing;
+
+  MethodResolutionOptimizationInfoCollection(Map<DexReference, MethodOptimizationInfo> backing) {
+    this.backing = backing;
+  }
+
+  public static MethodResolutionOptimizationInfoCollection empty() {
+    return EMPTY;
+  }
+
+  public MethodOptimizationInfo get(DexEncodedMethod method, DexClass holder) {
+    MethodOptimizationInfo defaultValue = DefaultMethodOptimizationInfo.getInstance();
+    if (!holder.isProgramClass()) {
+      return defaultValue;
+    }
+    return backing.getOrDefault(method.getReference(), defaultValue);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/StackTraceRegularExpressionParser.java b/src/main/java/com/android/tools/r8/retrace/internal/StackTraceRegularExpressionParser.java
index 720562d..d9935ac 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/StackTraceRegularExpressionParser.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/StackTraceRegularExpressionParser.java
@@ -158,7 +158,10 @@
     }
   }
 
-  private static final String identifierSegment = "[^\\s\\[;(<]+";
+  private static final String notAllowedCharacters = "\\s\\[;:(<";
+  private static final String identifierPrefix = "[^\\d" + notAllowedCharacters + "]";
+  private static final String identifierSuffix = "[^" + notAllowedCharacters + "]*";
+  private static final String identifierSegment = identifierPrefix + identifierSuffix;
 
   private static final String METHOD_NAME_REGULAR_EXPRESSION =
       "(?:(" + identifierSegment + "|\\<init\\>|\\<clinit\\>))";
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 de7a1b5..38bb45b 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -27,6 +27,7 @@
 import com.android.tools.r8.cf.CfVersion;
 import com.android.tools.r8.debuginfo.DebugRepresentation;
 import com.android.tools.r8.dex.ApplicationReader.ProgramClassConflictResolver;
+import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.dex.Marker;
 import com.android.tools.r8.dex.Marker.Backend;
 import com.android.tools.r8.dex.Marker.Tool;
@@ -173,7 +174,7 @@
     }
   }
 
-  public static final CfVersion SUPPORTED_CF_VERSION = CfVersion.V21;
+  public static final CfVersion SUPPORTED_CF_VERSION = CfVersion.V22;
 
   public static final int SUPPORTED_DEX_VERSION =
       AndroidApiLevel.LATEST.getDexVersion().getIntValue();
@@ -1551,6 +1552,8 @@
     public int maxSizeForFilledNewArrayOfInts = 200;
     public int maxSizeForFilledNewArrayOfIntsWhenNewArrayFilledDataApplicable = 5;
     public int maxSizeForFilledNewArrayOfReferences = 200;
+    // All DEX aput instructions takes an 8-bit wide register value for the source.
+    public int maxMaterializingConstants = Constants.U8BIT_MAX - 16;
 
     // Arbitrary limits of number of inputs to fill-array-data.
     public int minSizeForFilledArrayData = 2;
@@ -1747,18 +1750,9 @@
         return simpleInliningInstructionLimit;
       }
       // Allow 4 instructions when using LIR regardless of backend.
-      if (options.testing.useLir) {
-        // TODO(b/288226522): We should reevaluate this for size and other inputs as it regresses
-        //  compared to DEX code with limit 5 for tivi. This is set to 5 to avoid discard errors
-        //  in chrome. Using 5 also improves size for chrome compared to a lower value.
-        return 5;
-      }
-      // Allow 3 instructions when generating to class files.
-      if (options.isGeneratingClassFiles()) {
-        return 3;
-      }
-      // Allow the size of the dex code to be up to 5 bytes.
-      assert options.isGeneratingDex();
+      // TODO(b/288226522): We should reevaluate this for size and other inputs as it regresses
+      //  compared to DEX code with limit 5 for tivi. This is set to 5 to avoid discard errors
+      //  in chrome. Using 5 also improves size for chrome compared to a lower value.
       return 5;
     }
 
@@ -2144,19 +2138,9 @@
   public static class TestingOptions {
 
     public boolean roundtripThroughLir = false;
-    private boolean useLir = System.getProperty("com.android.tools.r8.nolir") == null;
-    private boolean convertLir = System.getProperty("com.android.tools.r8.convertlir") == null;
-
-    public void enableLir() {
-      useLir = true;
-    }
-
-    public void disableLir() {
-      useLir = false;
-    }
 
     public boolean canUseLir(AppView<?> appView) {
-      return useLir && appView.enableWholeProgramOptimizations();
+      return appView.enableWholeProgramOptimizations();
     }
 
     // As part of integrating LIR the compiler is split in three phases: pre, supported, and post.
@@ -2173,7 +2157,7 @@
         throws ExecutionException {
       assert isPreLirPhase();
       currentPhase = LirPhase.SUPPORTED;
-      if (!canUseLir(appView) || !convertLir) {
+      if (!canUseLir(appView)) {
         return;
       }
       // Convert code objects to LIR.
diff --git a/src/main/java/com/android/tools/r8/utils/OptionalBool.java b/src/main/java/com/android/tools/r8/utils/OptionalBool.java
index 4443355..06cd098 100644
--- a/src/main/java/com/android/tools/r8/utils/OptionalBool.java
+++ b/src/main/java/com/android/tools/r8/utils/OptionalBool.java
@@ -15,6 +15,11 @@
         }
 
         @Override
+        public boolean toBoolean() {
+          return true;
+        }
+
+        @Override
         public int ordinal() {
           return 1;
         }
@@ -34,6 +39,11 @@
         }
 
         @Override
+        public boolean toBoolean() {
+          return false;
+        }
+
+        @Override
         public int ordinal() {
           return 0;
         }
@@ -53,6 +63,11 @@
         }
 
         @Override
+        public boolean toBoolean() {
+          throw new IllegalStateException("Cannot convert UNKNOWN to boolean");
+        }
+
+        @Override
         public int ordinal() {
           return 2;
         }
@@ -78,6 +93,8 @@
     return this;
   }
 
+  public abstract boolean toBoolean();
+
   @Override
   public boolean equals(Object other) {
     return this == other;
diff --git a/src/test/bootstrap/com/android/tools/r8/bootstrap/BootstrapCurrentEqualityTest.java b/src/test/bootstrap/com/android/tools/r8/bootstrap/BootstrapCurrentEqualityTest.java
index 34c1623..a6c99f8 100644
--- a/src/test/bootstrap/com/android/tools/r8/bootstrap/BootstrapCurrentEqualityTest.java
+++ b/src/test/bootstrap/com/android/tools/r8/bootstrap/BootstrapCurrentEqualityTest.java
@@ -22,8 +22,6 @@
 import com.android.tools.r8.TestRuntime.CfVm;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.ProcessResult;
-import com.android.tools.r8.dex.Marker.Backend;
-import com.android.tools.r8.examples.hello.HelloTestRunner;
 import com.android.tools.r8.retrace.ProguardMapProducer;
 import com.android.tools.r8.retrace.ProguardMappingSupplier;
 import com.android.tools.r8.retrace.Retrace;
@@ -51,17 +49,24 @@
 @RunWith(Parameterized.class)
 public class BootstrapCurrentEqualityTest extends TestBase {
 
+  static class HelloWorldProgram {
+    public static void main(String[] args) {
+      System.out.println("Hello, world!");
+    }
+  }
+
   private static final Path MAIN_KEEP =
       Paths.get(ToolHelper.getProjectRoot(), "src", "main", "keep.txt");
 
-  private static final String HELLO_NAME = HelloTestRunner.getHelloClass().getTypeName();
+  private static final Class<?> HELLO_CLASS = HelloWorldProgram.class;
+  private static final String HELLO_NAME = typeName(HELLO_CLASS);
   private static final String[] KEEP_HELLO = {
     "-keep class " + HELLO_NAME + " {",
     "  public static void main(...);",
     "}",
     "-allowaccessmodification"
   };
-  private static String HELLO_EXPECTED = HelloTestRunner.getExpectedOutput();
+  private static String HELLO_EXPECTED = StringUtils.lines("Hello, world!");
 
   private static Pair<Path, Path> r8R8Debug;
   private static Pair<Path, Path> r8R8Release;
@@ -203,7 +208,8 @@
 
   @Test
   public void test() throws Exception {
-    Path program = HelloTestRunner.writeHelloProgramJar(temp);
+    Path program = testFolder.newFolder().toPath().resolve("hello.jar");
+    writeClassesToJar(program, HELLO_CLASS);
     testForJvm(parameters)
         .addProgramFiles(program)
         .run(parameters.getRuntime(), HELLO_NAME)
diff --git a/src/test/bootstrap/com/android/tools/r8/bootstrap/HelloWorldCompiledOnArtTest.java b/src/test/bootstrap/com/android/tools/r8/bootstrap/HelloWorldCompiledOnArtTest.java
index 9541dd4..31a8108 100644
--- a/src/test/bootstrap/com/android/tools/r8/bootstrap/HelloWorldCompiledOnArtTest.java
+++ b/src/test/bootstrap/com/android/tools/r8/bootstrap/HelloWorldCompiledOnArtTest.java
@@ -19,8 +19,8 @@
 import com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification;
 import com.android.tools.r8.desugar.desugaredlibrary.test.DesugaredLibraryTestCompileResult;
 import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
-import com.android.tools.r8.examples.hello.HelloTestRunner;
 import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
 import com.google.common.collect.ImmutableList;
 import java.io.IOException;
 import java.nio.file.Path;
@@ -34,6 +34,16 @@
 @RunWith(Parameterized.class)
 public class HelloWorldCompiledOnArtTest extends DesugaredLibraryTestBase {
 
+  static class HelloWorldProgram {
+    public static void main(String[] args) {
+      System.out.println("Hello, world!");
+    }
+  }
+
+  private static final Class<?> HELLO_CLASS = HelloWorldProgram.class;
+  private static final String HELLO_NAME = typeName(HELLO_CLASS);
+  private static String HELLO_EXPECTED = StringUtils.lines("Hello, world!");
+
   private final TestParameters parameters;
   private final CompilationSpecification compilationSpecification;
   private final LibraryDesugaringSpecification libraryDesugaringSpecification;
@@ -62,13 +72,17 @@
     return Paths.get(string).toAbsolutePath().toString();
   }
 
-  private static final String HELLO_NAME = typeName(HelloTestRunner.getHelloClass());
+  private Path writeHelloProgramJar() throws IOException {
+    Path jar = temp.newFolder().toPath().resolve("hello.jar");
+    writeClassesToJar(jar, HELLO_CLASS);
+    return jar;
+  }
 
   @Test
   public void testHelloCompiledWithR8Dex() throws Exception {
     Path keepRules =
         writeTextToTempFile(keepMainProguardConfiguration(HELLO_NAME)).toAbsolutePath();
-    Path helloInput = HelloTestRunner.writeHelloProgramJar(temp);
+    Path helloInput = writeHelloProgramJar().toAbsolutePath();
     Path helloOutput = temp.newFolder("helloOutput").toPath().resolve("out.zip").toAbsolutePath();
     compileR8ToDexWithD8()
         .run(
@@ -88,7 +102,7 @@
 
   @Test
   public void testHelloCompiledWithD8Dex() throws Exception {
-    Path helloInput = HelloTestRunner.writeHelloProgramJar(temp).toAbsolutePath();
+    Path helloInput = writeHelloProgramJar().toAbsolutePath();
     Path helloOutput = temp.newFolder("helloOutput").toPath().resolve("out.zip").toAbsolutePath();
     compileR8ToDexWithD8()
         .run(
@@ -106,7 +120,7 @@
 
   private void verifyResult(Path helloOutput) throws IOException {
     ProcessResult processResult = ToolHelper.runArtRaw(helloOutput.toString(), HELLO_NAME);
-    assertEquals(HelloTestRunner.getExpectedOutput(), processResult.stdout);
+    assertEquals(HELLO_EXPECTED, processResult.stdout);
   }
 
   private DesugaredLibraryTestCompileResult<?> compileR8ToDexWithD8() throws Exception {
diff --git a/src/test/bootstrap/com/android/tools/r8/bootstrap/Java17R8BootstrapTest.java b/src/test/bootstrap/com/android/tools/r8/bootstrap/Java17R8BootstrapTest.java
index 6ef44bf..d410299 100644
--- a/src/test/bootstrap/com/android/tools/r8/bootstrap/Java17R8BootstrapTest.java
+++ b/src/test/bootstrap/com/android/tools/r8/bootstrap/Java17R8BootstrapTest.java
@@ -13,8 +13,8 @@
 import com.android.tools.r8.TestRuntime.CfVm;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.desugar.LibraryFilesHelper;
-import com.android.tools.r8.examples.hello.HelloTestRunner;
 import com.google.common.collect.ImmutableList;
+import java.io.IOException;
 import java.nio.file.Path;
 import org.junit.Assume;
 import org.junit.BeforeClass;
@@ -33,7 +33,13 @@
 @RunWith(Parameterized.class)
 public class Java17R8BootstrapTest extends TestBase {
 
-  private static final Class<?> HELLO_CLASS = HelloTestRunner.getHelloClass();
+  static class HelloWorldProgram {
+    public static void main(String[] args) {
+      System.out.println("Hello, world!");
+    }
+  }
+
+  private static final Class<?> HELLO_CLASS = HelloWorldProgram.class;
   private static final String[] HELLO_KEEP = {
     "-keep class " + HELLO_CLASS.getTypeName() + " {  public static void main(...);}"
   };
@@ -77,6 +83,12 @@
     return new Path[] {ToolHelper.R8_WITH_RELOCATED_DEPS_17_JAR, r8Lib17NoDesugar, r8Lib17Desugar};
   }
 
+  private Path writeHelloProgramJar() throws IOException {
+    Path jar = temp.newFolder().toPath().resolve("hello.jar");
+    writeClassesToJar(jar, HELLO_CLASS);
+    return jar;
+  }
+
   @Test
   public void testHello() throws Exception {
     Assume.assumeTrue(!ToolHelper.isWindows());
@@ -84,7 +96,7 @@
     Assume.assumeTrue(supportsSealedClassesWhenGeneratingCf());
     Path prevGeneratedJar = null;
     String prevRunResult = null;
-    Path helloJar = HelloTestRunner.writeHelloProgramJar(temp);
+    Path helloJar = writeHelloProgramJar();
     for (Path jar : jarsToCompare()) {
       Path generatedJar =
           testForExternalR8(Backend.CF, parameters.getRuntime())
@@ -119,7 +131,7 @@
     Assume.assumeTrue(JavaBootstrapUtils.exists(ToolHelper.R8_WITH_RELOCATED_DEPS_17_JAR));
     Assume.assumeTrue(supportsSealedClassesWhenGeneratingCf());
     Path prevGeneratedJar = null;
-    Path helloJar = HelloTestRunner.writeHelloProgramJar(temp);
+    Path helloJar = writeHelloProgramJar();
     for (Path jar : jarsToCompare()) {
       Path generatedJar =
           testForExternalR8(Backend.CF, parameters.getRuntime())
diff --git a/src/test/bootstrap/com/android/tools/r8/bootstrap/RetraceStackTraceFunctionalCompositionTest.java b/src/test/bootstrap/com/android/tools/r8/bootstrap/RetraceStackTraceFunctionalCompositionTest.java
index a5c56c1..7fcb455 100644
--- a/src/test/bootstrap/com/android/tools/r8/bootstrap/RetraceStackTraceFunctionalCompositionTest.java
+++ b/src/test/bootstrap/com/android/tools/r8/bootstrap/RetraceStackTraceFunctionalCompositionTest.java
@@ -274,7 +274,7 @@
         .apply(
             b ->
                 b.getBuilder()
-                    .setProguardInputMapFile(previousMappingFile)
+                    .setProguardMapInputFile(previousMappingFile)
                     .setProguardMapConsumer((string, handler) -> mappingComposed.append(string)))
         .compile()
         .writeToZip(jar);
diff --git a/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
index d3a155f..1d09697 100644
--- a/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
@@ -156,8 +156,7 @@
 
     Path getGeneratedRoot(Path testJarFile) {
       String sourceSet = testJarFile.getParent().toFile().getName();
-      Path parent = testJarFile.getParent().getParent().getParent();
-      return parent.resolve(Paths.get("generated", sourceSet, packageName));
+      return Paths.get(ToolHelper.THIRD_PARTY_DIR, sourceSet + "Generated", packageName);
     }
 
     AndroidApp compileClassFilesInIntermediate(
diff --git a/src/test/java/com/android/tools/r8/D8NonLazyRunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8NonLazyRunExamplesAndroidOTest.java
index c87ad97..7fc01c3 100644
--- a/src/test/java/com/android/tools/r8/D8NonLazyRunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8NonLazyRunExamplesAndroidOTest.java
@@ -6,8 +6,7 @@
 
 import java.nio.file.Path;
 
-public class D8NonLazyRunExamplesAndroidOTest
-    extends D8IncrementalRunExamplesAndroidOTest {
+public class D8NonLazyRunExamplesAndroidOTest extends D8IncrementalRunExamplesAndroidOTest {
   class D8LazyTestRunner extends D8IncrementalTestRunner {
 
     D8LazyTestRunner(String testName, String packageName, String mainClass) {
diff --git a/src/test/java/com/android/tools/r8/KotlinCompilerTool.java b/src/test/java/com/android/tools/r8/KotlinCompilerTool.java
index 642e319..e549627 100644
--- a/src/test/java/com/android/tools/r8/KotlinCompilerTool.java
+++ b/src/test/java/com/android/tools/r8/KotlinCompilerTool.java
@@ -12,13 +12,20 @@
 import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.TestRuntime.CfRuntime;
+import com.android.tools.r8.ToolHelper.CacheLookupKey;
+import com.android.tools.r8.ToolHelper.CommandResultCache;
 import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.errors.Unimplemented;
 import com.android.tools.r8.utils.ArrayUtils;
 import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.Pair;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.ThrowingConsumer;
 import com.android.tools.r8.utils.structural.Ordered;
+import com.google.common.hash.Hasher;
 import java.io.File;
 import java.io.IOException;
+import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
@@ -339,7 +346,45 @@
   }
 
   private ProcessResult compileInternal(Path output) throws IOException {
-    List<String> cmdline = new ArrayList<>();
+    CommandLineAndHasherConsumers commandLineAndHasherConsumers =
+        buildCommandLineAndHasherConsumers(output);
+    CacheLookupKey cacheLookupKey = null;
+    if (CommandResultCache.isEnabled()) {
+      cacheLookupKey =
+          new CacheLookupKey(
+              hasher ->
+                  commandLineAndHasherConsumers.hasherConsumers.forEach(
+                      hasherConsumer -> hasherConsumer.acceptWithRuntimeException(hasher)));
+      Pair<ProcessResult, Path> lookupResult =
+          CommandResultCache.getInstance().lookup(cacheLookupKey);
+      if (lookupResult != null
+          && lookupResult.getFirst().exitCode == 0
+          && lookupResult.getSecond() != null) {
+        Files.copy(lookupResult.getSecond(), output);
+        return lookupResult.getFirst();
+      }
+    }
+    ProcessBuilder builder = new ProcessBuilder(commandLineAndHasherConsumers.cmdline);
+    if (ToolHelper.isNewGradleSetup()) {
+      builder.directory(new File(ToolHelper.getProjectRoot()));
+    }
+    ProcessResult processResult = ToolHelper.runProcess(builder);
+    if (CommandResultCache.isEnabled() && output.toFile().isFile()) {
+      CommandResultCache.getInstance().putResult(processResult, cacheLookupKey, output);
+    }
+    return processResult;
+  }
+
+  public static class CommandLineAndHasherConsumers {
+    final List<String> cmdline = new ArrayList<>();
+    final List<ThrowingConsumer<Hasher, IOException>> hasherConsumers = new ArrayList<>();
+  }
+
+  private CommandLineAndHasherConsumers buildCommandLineAndHasherConsumers(Path output)
+      throws IOException {
+    CommandLineAndHasherConsumers commandLineAndHasherConsumers =
+        new CommandLineAndHasherConsumers();
+    List<String> cmdline = commandLineAndHasherConsumers.cmdline;
     cmdline.add(jdk.getJavaExecutable().toString());
     if (enableAssertions) {
       cmdline.add("-ea");
@@ -354,8 +399,15 @@
     cmdline.add(jdk.getJavaHome().toString());
     cmdline.add("-jvm-target");
     cmdline.add(targetVersion.getJvmTargetString());
+    // Until now this is just command line files, no inputs, hash existing command
+    String noneFileCommandLineArguments = StringUtils.join("", cmdline);
+    commandLineAndHasherConsumers.hasherConsumers.add(
+        hasher -> hasher.putString(noneFileCommandLineArguments, StandardCharsets.UTF_8));
+
     for (Path source : sources) {
       cmdline.add(source.toString());
+      commandLineAndHasherConsumers.hasherConsumers.add(
+          hasher -> hasher.putBytes(Files.readAllBytes(source)));
     }
     cmdline.add("-d");
     cmdline.add(output.toString());
@@ -365,12 +417,19 @@
           .stream()
           .map(Path::toString)
           .collect(Collectors.joining(isWindows() ? ";" : ":")));
+      for (Path path : classpath) {
+        commandLineAndHasherConsumers.hasherConsumers.add(
+            hasher -> {
+              hasher.putString("--cp", StandardCharsets.UTF_8);
+              hasher.putBytes(Files.readAllBytes(path));
+            });
+      }
     }
     cmdline.addAll(additionalArguments);
-    ProcessBuilder builder = new ProcessBuilder(cmdline);
-    if (ToolHelper.isNewGradleSetup()) {
-      builder.directory(new File(ToolHelper.getProjectRoot()));
-    }
-    return ToolHelper.runProcess(builder);
+    commandLineAndHasherConsumers.hasherConsumers.add(
+        hasher -> additionalArguments.forEach(s -> hasher.putString(s, StandardCharsets.UTF_8)));
+    return commandLineAndHasherConsumers;
   }
+
+
 }
diff --git a/src/test/java/com/android/tools/r8/KotlinTestBase.java b/src/test/java/com/android/tools/r8/KotlinTestBase.java
index f314547..a98d7f0 100644
--- a/src/test/java/com/android/tools/r8/KotlinTestBase.java
+++ b/src/test/java/com/android/tools/r8/KotlinTestBase.java
@@ -24,7 +24,6 @@
 import java.util.Map;
 import java.util.function.Consumer;
 import java.util.stream.Collectors;
-import org.hamcrest.Matcher;
 import org.junit.rules.TemporaryFolder;
 
 public abstract class KotlinTestBase extends TestBase {
@@ -91,15 +90,7 @@
   }
 
   protected Path getJavaJarFile(String folder) {
-    return Paths.get(ToolHelper.TESTS_BUILD_DIR, RSRC, folder + FileUtils.JAR_EXTENSION);
-  }
-
-  protected Path getMappingfile(String folder, String mappingFileName) {
-    return Paths.get(ToolHelper.TESTS_DIR, RSRC, folder, mappingFileName);
-  }
-
-  protected static Matcher<String> expectedInfoMessagesFromKotlinStdLib() {
-    return containsString("No VersionRequirement");
+    return Paths.get(ToolHelper.THIRD_PARTY_DIR, RSRC, folder + FileUtils.JAR_EXTENSION);
   }
 
   protected KotlinCompilerTool kotlinCompilerTool() {
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
index 9acb6cf..f2979f2 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
@@ -132,7 +132,6 @@
         .run(Paths.get(ToolHelper.THIRD_PARTY_DIR, "examplesAndroidOLegacy"));
 
     test("lambdadesugaring", "lambdadesugaring", "LambdaDesugaring")
-        .withOptionConsumer(o -> o.testing.enableLir())
         .withMinApiLevel(ToolHelper.getMinApiLevelForDexVmNoHigherThan(AndroidApiLevel.K))
         .withBuilderTransformation(
             b -> b.addProguardConfiguration(PROGUARD_OPTIONS, Origin.unknown()))
@@ -173,7 +172,6 @@
 
     test("lambdadesugaring", "lambdadesugaring", "LambdaDesugaring")
         .withMinApiLevel(AndroidApiLevel.N)
-        .withOptionConsumer(opts -> opts.testing.enableLir())
         .withBuilderTransformation(
             b -> b.addProguardConfiguration(PROGUARD_OPTIONS, Origin.unknown()))
         .withDexCheck(inspector -> checkLambdaCount(inspector, 1, "lambdadesugaring"))
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 8f31142..f550b71 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8;
 
+import static com.android.tools.r8.ToolHelper.TestDataSourceSet.computeLegacyOrGradleSpecifiedLocation;
 import static com.android.tools.r8.utils.FileUtils.CLASS_EXTENSION;
 import static com.android.tools.r8.utils.FileUtils.JAVA_EXTENSION;
 import static com.android.tools.r8.utils.FileUtils.isDexFile;
@@ -39,6 +40,7 @@
 import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.Pair;
 import com.android.tools.r8.utils.Reporter;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.Timing;
@@ -95,7 +97,7 @@
 public class ToolHelper {
 
   public static boolean isNewGradleSetup() {
-    return "true".equals(System.getenv("USE_NEW_GRADLE_SETUP"));
+    return "true".equals(System.getProperty("USE_NEW_GRADLE_SETUP"));
   }
 
   public static String getProjectRoot() {
@@ -109,12 +111,48 @@
     return current + "/";
   }
 
+  public enum TestDataSourceSet {
+    LEGACY(null),
+    TESTS_JAVA_8("tests_java_8/build/classes/java/test"),
+    TESTS_BOOTSTRAP("tests_bootstrap/build/classes/java/test"),
+    SPECIFIED_BY_GRADLE_PROPERTY(null);
+
+    private final String destination;
+
+    TestDataSourceSet(String destination) {
+      this.destination = destination;
+    }
+
+    public boolean isLegacy() {
+      return this == LEGACY;
+    }
+
+    public boolean isSpecifiedByGradleProperty() {
+      return this == SPECIFIED_BY_GRADLE_PROPERTY;
+    }
+
+    public Path getBuildDir() {
+      if (isLegacy()) {
+        return Paths.get(BUILD_DIR, "classes", "java", "test");
+      } else if (isSpecifiedByGradleProperty()) {
+        assert System.getProperty("TEST_DATA_LOCATION") != null;
+        return Paths.get(System.getProperty("TEST_DATA_LOCATION"));
+      } else {
+        return Paths.get(getProjectRoot(), "d8_r8", "test_modules", destination);
+      }
+    }
+
+    public static TestDataSourceSet computeLegacyOrGradleSpecifiedLocation() {
+      return isNewGradleSetup()
+          ? TestDataSourceSet.SPECIFIED_BY_GRADLE_PROPERTY
+          : TestDataSourceSet.LEGACY;
+    }
+  }
+
   public static final String SOURCE_DIR = getProjectRoot() + "src/";
   public static final String MAIN_SOURCE_DIR = getProjectRoot() + "src/main/java/";
   public static final String LIBRARY_DESUGAR_SOURCE_DIR = getProjectRoot() + "src/library_desugar/";
   public static final String BUILD_DIR = getProjectRoot() + "build/";
-  public static final String TEST_MODULE_DIR = getProjectRoot() + "d8_r8/test_modules/";
-  public static final String GENERATED_TEST_BUILD_DIR = BUILD_DIR + "generated/test/";
   public static final String LIBS_DIR = BUILD_DIR + "libs/";
   public static final String THIRD_PARTY_DIR = getProjectRoot() + "third_party/";
   public static final String DEPENDENCIES = THIRD_PARTY_DIR + "dependencies/";
@@ -125,17 +163,15 @@
   public static final String EXAMPLES_DIR = TESTS_DIR + "examples/";
   public static final String EXAMPLES_ANDROID_O_DIR = TESTS_DIR + "examplesAndroidO/";
   public static final String EXAMPLES_ANDROID_P_DIR = TESTS_DIR + "examplesAndroidP/";
-  public static final String TESTS_BUILD_DIR = BUILD_DIR + "test/";
-  public static final String EXAMPLES_BUILD_DIR = TESTS_BUILD_DIR + "examples/";
+  public static final String EXAMPLES_BUILD_DIR = THIRD_PARTY_DIR + "examples/";
   public static final String EXAMPLES_CF_DIR = EXAMPLES_BUILD_DIR + "classes/";
-  public static final String EXAMPLES_ANDROID_N_BUILD_DIR = TESTS_BUILD_DIR + "examplesAndroidN/";
-  public static final String EXAMPLES_ANDROID_O_BUILD_DIR = TESTS_BUILD_DIR + "examplesAndroidO/";
-  public static final String EXAMPLES_ANDROID_P_BUILD_DIR = TESTS_BUILD_DIR + "examplesAndroidP/";
+  public static final String EXAMPLES_ANDROID_N_BUILD_DIR = THIRD_PARTY_DIR + "examplesAndroidN/";
+  public static final String EXAMPLES_ANDROID_O_BUILD_DIR = THIRD_PARTY_DIR + "examplesAndroidO/";
+  public static final String EXAMPLES_ANDROID_P_BUILD_DIR = THIRD_PARTY_DIR + "examplesAndroidP/";
+  public static final String TESTS_BUILD_DIR = BUILD_DIR + "test/";
   public static final String EXAMPLES_JAVA9_BUILD_DIR = TESTS_BUILD_DIR + "examplesJava9/";
   public static final String EXAMPLES_JAVA10_BUILD_DIR = TESTS_BUILD_DIR + "examplesJava10/";
   public static final String EXAMPLES_JAVA11_JAR_DIR = TESTS_BUILD_DIR + "examplesJava11/";
-  public static final String EXAMPLES_PROTO_BUILD_DIR = TESTS_BUILD_DIR + "examplesProto/";
-  public static final String GENERATED_PROTO_BUILD_DIR = GENERATED_TEST_BUILD_DIR + "proto/";
   public static final String SMALI_BUILD_DIR = THIRD_PARTY_DIR + "smali/";
 
   public static String getExamplesJava11BuildDir() {
@@ -453,7 +489,7 @@
     }
 
     public static DexVm fromVersion(Version version) {
-      return SHORT_NAME_MAP.get(version.shortName + "_" + Kind.HOST.toString());
+      return SHORT_NAME_MAP.get(version.shortName + "_" + Kind.HOST);
     }
 
     public boolean isEqualTo(DexVm other) {
@@ -591,7 +627,7 @@
 
     private DexVm version;
     private boolean withArtFrameworks;
-    private ArtResultCacheLookupKey artResultCacheLookupKey;
+    private CacheLookupKey artResultCacheLookupKey;
     private boolean noCaching = false;
 
     public ArtCommandBuilder() {
@@ -646,7 +682,7 @@
     }
 
     private boolean useCache() {
-      return !noCaching && CommandResultCache.getInstance() != null;
+      return !noCaching && CommandResultCache.isEnabled();
     }
 
     public void cacheResult(ProcessResult result) {
@@ -654,18 +690,20 @@
       // put invalid entries into the cache.
       if (useCache() && result.exitCode == 0) {
         assert artResultCacheLookupKey != null;
-        CommandResultCache.getInstance().putResult(result, artResultCacheLookupKey);
+        CommandResultCache.getInstance().putResult(result, artResultCacheLookupKey, null);
       }
     }
 
-    public ProcessResult getCachedResults() {
+    public ProcessResult getCachedResults() throws IOException {
       if (!useCache()) {
         return null;
       }
       assert artResultCacheLookupKey == null;
       // Reuse the key when storing results if this is not already cached.
-      artResultCacheLookupKey = new ArtResultCacheLookupKey(this::hashParts);
-      return CommandResultCache.getInstance().lookup(artResultCacheLookupKey);
+      artResultCacheLookupKey = new CacheLookupKey(this::hashParts);
+      Pair<ProcessResult, Path> lookup =
+          CommandResultCache.getInstance().lookup(artResultCacheLookupKey);
+      return lookup == null ? null : lookup.getFirst();
     }
 
     private void hashParts(Hasher hasher) {
@@ -696,11 +734,11 @@
     }
   }
 
-  private static class ArtResultCacheLookupKey {
+  public static class CacheLookupKey {
     private final Consumer<Hasher> hasherConsumer;
     private String hash;
 
-    public ArtResultCacheLookupKey(Consumer<Hasher> hasherConsumer) {
+    public CacheLookupKey(Consumer<Hasher> hasherConsumer) {
       this.hasherConsumer = hasherConsumer;
     }
 
@@ -714,7 +752,7 @@
     }
   }
 
-  private static class CommandResultCache {
+  public static class CommandResultCache {
     private static CommandResultCache INSTANCE =
         System.getProperty("command_cache_dir") != null
             ? new CommandResultCache(Paths.get(System.getProperty("command_cache_dir")))
@@ -730,16 +768,24 @@
       return INSTANCE;
     }
 
-    private Path getStdoutFile(ArtResultCacheLookupKey artResultCacheLookupKey) {
-      return path.resolve(artResultCacheLookupKey.getHash() + ".stdout");
+    public static boolean isEnabled() {
+      return getInstance() != null;
     }
 
-    private Path getStderrFile(ArtResultCacheLookupKey artResultCacheLookupKey) {
-      return path.resolve(artResultCacheLookupKey.getHash() + ".stderr");
+    private Path getStdoutFile(CacheLookupKey cacheLookupKey) {
+      return path.resolve(cacheLookupKey.getHash() + ".stdout");
     }
 
-    private Path getExitCodeFile(ArtResultCacheLookupKey artResultCacheLookupKey) {
-      return path.resolve(artResultCacheLookupKey.getHash());
+    private Path getStderrFile(CacheLookupKey cacheLookupKey) {
+      return path.resolve(cacheLookupKey.getHash() + ".stderr");
+    }
+
+    private Path getOutputFile(CacheLookupKey cacheLookupKey) {
+      return path.resolve(cacheLookupKey.getHash() + ".output");
+    }
+
+    private Path getExitCodeFile(CacheLookupKey cacheLookupKey) {
+      return path.resolve(cacheLookupKey.getHash());
     }
 
     private Path getTempFile(Path path) {
@@ -758,38 +804,47 @@
       return "";
     }
 
-    public ProcessResult lookup(ArtResultCacheLookupKey artResultCacheLookupKey) {
+    public Pair<ProcessResult, Path> lookup(CacheLookupKey cacheLookupKey) {
       // TODO Add concurrency handling!
-      Path exitCodeFile = getExitCodeFile(artResultCacheLookupKey);
+      Path exitCodeFile = getExitCodeFile(cacheLookupKey);
       if (exitCodeFile.toFile().exists()) {
         int exitCode = Integer.parseInt(getStringContent(exitCodeFile));
         // Because of the temp files and order of writing we should never get here with an
         // inconsistent state. It is possible, although unlikely, that the stdout/stderr
         // (and even exitcode if art is non deterministic) are from different, process ids etc,
         // but this should have no impact.
-        return new ProcessResult(
-            exitCode,
-            getStringContent(getStdoutFile(artResultCacheLookupKey)),
-            getStringContent(getStderrFile(artResultCacheLookupKey)));
+
+        Path outputFile = getOutputFile(cacheLookupKey);
+        return new Pair(
+            new ProcessResult(
+                exitCode,
+                getStringContent(getStdoutFile(cacheLookupKey)),
+                getStringContent(getStderrFile(cacheLookupKey))),
+            outputFile.toFile().exists() ? outputFile : null);
       }
       return null;
     }
 
-    public void putResult(ProcessResult result, ArtResultCacheLookupKey artResultCacheLookupKey) {
+    public void putResult(ProcessResult result, CacheLookupKey cacheLookupKey, Path output) {
       try {
         String exitCode = "" + result.exitCode;
         // We avoid race conditions of writing vs reading by first writing all 3 files to temp
         // files, then moving these to the result files, moving last the exitcode file (which is
         // what we use as cache present check)
-        Path exitCodeFile = getExitCodeFile(artResultCacheLookupKey);
+        Path exitCodeFile = getExitCodeFile(cacheLookupKey);
         Path exitCodeTempFile = getTempFile(exitCodeFile);
-        Path stdoutFile = getStdoutFile(artResultCacheLookupKey);
+        Path stdoutFile = getStdoutFile(cacheLookupKey);
         Path stdoutTempFile = getTempFile(stdoutFile);
-        Path stderrFile = getStderrFile(artResultCacheLookupKey);
+        Path stderrFile = getStderrFile(cacheLookupKey);
         Path stderrTempFile = getTempFile(stderrFile);
+        Path outputfile = getOutputFile(cacheLookupKey);
+        Path outputTempFile = getTempFile(outputfile);
         Files.write(exitCodeTempFile, exitCode.getBytes(StandardCharsets.UTF_8));
         Files.write(stdoutTempFile, result.stdout.getBytes(StandardCharsets.UTF_8));
         Files.write(stderrTempFile, result.stderr.getBytes(StandardCharsets.UTF_8));
+        if (output != null) {
+          Files.copy(output, outputTempFile);
+        }
         // Order is important, move exitcode file last!
         Files.move(
             stdoutTempFile,
@@ -801,6 +856,13 @@
             stderrFile,
             StandardCopyOption.ATOMIC_MOVE,
             StandardCopyOption.REPLACE_EXISTING);
+        if (output != null) {
+          Files.move(
+              outputTempFile,
+              outputfile,
+              StandardCopyOption.ATOMIC_MOVE,
+              StandardCopyOption.REPLACE_EXISTING);
+        }
         Files.move(
             exitCodeTempFile,
             exitCodeFile,
@@ -1006,11 +1068,16 @@
     }
   }
 
-  public static byte[] getClassAsBytes(Class clazz) throws IOException {
+  public static byte[] getClassAsBytes(Class<?> clazz) throws IOException {
     return Files.readAllBytes(getClassFileForTestClass(clazz));
   }
 
-  public static long getClassByteCrc(Class clazz) {
+  public static byte[] getClassAsBytes(Class<?> clazz, TestDataSourceSet dataSourceSet)
+      throws IOException {
+    return Files.readAllBytes(getClassFileForTestClass(clazz, dataSourceSet));
+  }
+
+  public static long getClassByteCrc(Class<?> clazz) {
     byte[] bytes = null;
     try {
       bytes = getClassAsBytes(clazz);
@@ -1406,12 +1473,11 @@
   }
 
   public static Path getClassPathForTests() {
-    if (isNewGradleSetup()) {
-      assert System.getenv("TEST_CLASSES_LOCATIONS") != null;
-      return Paths.get(System.getenv("TEST_CLASSES_LOCATIONS"));
-    } else {
-      return Paths.get(BUILD_DIR, "classes", "java", "test");
-    }
+    return getClassPathForTestDataSourceSet(computeLegacyOrGradleSpecifiedLocation());
+  }
+
+  public static Path getClassPathForTestDataSourceSet(TestDataSourceSet sourceSet) {
+    return sourceSet.getBuildDir();
   }
 
   private static List<String> getNamePartsForTestPackage(Package pkg) {
@@ -1456,29 +1522,41 @@
   }
 
   public static Path getClassFileForTestClass(Class<?> clazz) {
+    return getClassFileForTestClass(clazz, computeLegacyOrGradleSpecifiedLocation());
+  }
+
+  public static Path getClassFileForTestClass(Class<?> clazz, TestDataSourceSet sourceSet) {
     List<String> parts = getNamePartsForTestClass(clazz);
     Path resolve =
-        getClassPathForTests().resolve(Paths.get("", parts.toArray(StringUtils.EMPTY_ARRAY)));
+        getClassPathForTestDataSourceSet(sourceSet)
+            .resolve(Paths.get("", parts.toArray(StringUtils.EMPTY_ARRAY)));
     if (!Files.exists(resolve)) {
       throw new RuntimeException("Could not find: " + resolve.toString());
     }
     return resolve;
   }
 
-  public static Collection<Path> getClassFilesForInnerClasses(Path path) throws IOException {
-    Set<Path> paths = new HashSet<>();
-    String prefix = path.toString().replace(CLASS_EXTENSION, "$");
-    paths.addAll(
-        ToolHelper.getClassFilesForTestDirectory(
-            path.getParent(), p -> p.toString().startsWith(prefix)));
-    return paths;
-  }
-
   public static Collection<Path> getClassFilesForInnerClasses(Collection<Class<?>> classes)
       throws IOException {
+    return getClassFilesForInnerClasses(computeLegacyOrGradleSpecifiedLocation(), classes);
+  }
+
+  public static Collection<Path> getClassFilesForInnerClasses(Class<?>... classes)
+      throws IOException {
+    return getClassFilesForInnerClasses(
+        computeLegacyOrGradleSpecifiedLocation(), Arrays.asList(classes));
+  }
+
+  public static Collection<Path> getClassFilesForInnerClasses(
+      TestDataSourceSet sourceSet, Class<?>... classes) throws IOException {
+    return getClassFilesForInnerClasses(sourceSet, Arrays.asList(classes));
+  }
+
+  public static Collection<Path> getClassFilesForInnerClasses(
+      TestDataSourceSet sourceSet, Collection<Class<?>> classes) throws IOException {
     Set<Path> paths = new HashSet<>();
     for (Class<?> clazz : classes) {
-      Path path = ToolHelper.getClassFileForTestClass(clazz);
+      Path path = ToolHelper.getClassFileForTestClass(clazz, sourceSet);
       String prefix = path.toString().replace(CLASS_EXTENSION, "$");
       paths.addAll(
           ToolHelper.getClassFilesForTestDirectory(
@@ -1487,17 +1565,12 @@
     return paths;
   }
 
-  public static Collection<Path> getClassFilesForInnerClasses(Class<?>... classes)
-      throws IOException {
-    return getClassFilesForInnerClasses(Arrays.asList(classes));
-  }
-
-  public static Path getFileNameForTestClass(Class clazz) {
+  public static Path getFileNameForTestClass(Class<?> clazz) {
     List<String> parts = getNamePartsForTestClass(clazz);
     return Paths.get("", parts.toArray(StringUtils.EMPTY_ARRAY));
   }
 
-  public static String getJarEntryForTestClass(Class clazz) {
+  public static String getJarEntryForTestClass(Class<?> clazz) {
     List<String> parts = getNamePartsForTestClass(clazz);
     return String.join("/", parts);
   }
diff --git a/src/test/java/com/android/tools/r8/cf/ClassFileVersion66Test.java b/src/test/java/com/android/tools/r8/cf/ClassFileVersion66Test.java
new file mode 100644
index 0000000..d97f14c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/ClassFileVersion66Test.java
@@ -0,0 +1,53 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.cf;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class ClassFileVersion66Test extends TestBase {
+
+  static final String EXPECTED = StringUtils.lines("Hello, world");
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters()
+        .withDefaultRuntimes()
+        .enableApiLevelsForCf()
+        .withApiLevel(AndroidApiLevel.B)
+        .build();
+  }
+
+  public ClassFileVersion66Test(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  // TODO(b/301189814): Update ASM once it has a release with v22 support.
+  @Test(expected = CompilationFailedException.class)
+  public void test() throws Exception {
+    testForD8(parameters.getBackend())
+        .addProgramClassFileData(transformer(TestClass.class).setVersion(CfVersion.V22).transform())
+        .setMinApi(parameters)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(EXPECTED);
+  }
+
+  static class TestClass {
+
+    public static void main(String[] args) {
+      System.out.println("Hello, world");
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapTest.java b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapTest.java
index ee0ff21..c381b0d 100644
--- a/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapTest.java
+++ b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapTest.java
@@ -12,8 +12,8 @@
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.ProcessResult;
-import com.android.tools.r8.examples.hello.HelloTestRunner;
 import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.StringUtils;
 import com.google.common.base.Charsets;
 import java.nio.file.Path;
 import java.nio.file.Paths;
@@ -27,12 +27,18 @@
 @RunWith(Parameterized.class)
 public class BootstrapTest extends TestBase {
 
+  static class HelloWorldProgram {
+    public static void main(String[] args) {
+      System.out.println("Hello, world!");
+    }
+  }
+
+  private static final Class<?> HELLO_CLASS = HelloWorldProgram.class;
+  private static String HELLO_EXPECTED = StringUtils.lines("Hello, world!");
+
   private static final Path R8_STABLE_JAR =
       Paths.get(ToolHelper.THIRD_PARTY_DIR, "r8-releases", "3.2.54", "r8.jar");
 
-  private static final Class<?> HELLO_CLASS = HelloTestRunner.getHelloClass();
-  private static final String HELLO_EXPECTED = HelloTestRunner.getExpectedOutput();
-
   private static class R8Result {
 
     final ProcessResult processResult;
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/ExceptionTablesTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/ExceptionTablesTest.java
index f694cd7..1ba7dcf 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/ExceptionTablesTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/ExceptionTablesTest.java
@@ -44,7 +44,6 @@
         .addKeepMainRule(TestClass.class)
         .addVerticallyMergedClassesInspector(this::inspectVerticallyMergedClasses)
         .setMinApi(parameters)
-        .addOptionsModification(o -> o.testing.enableLir())
         .compile()
         .inspect(this::inspect)
         .run(parameters.getRuntime(), TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
index 162f678..7ec72c7 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
@@ -65,9 +65,9 @@
 public class VerticalClassMergerTest extends TestBase {
 
   private static final Path CF_DIR =
-      Paths.get(ToolHelper.BUILD_DIR).resolve("test/examples/classes/classmerging");
+      Paths.get(ToolHelper.EXAMPLES_BUILD_DIR).resolve("classes/classmerging");
   private static final Path JAVA8_CF_DIR =
-      Paths.get(ToolHelper.BUILD_DIR).resolve("test/examplesAndroidO/classes/classmerging");
+      Paths.get(ToolHelper.THIRD_PARTY_DIR).resolve("examplesAndroidO/classes/classmerging");
   private static final Path EXAMPLE_JAR = Paths.get(ToolHelper.EXAMPLES_BUILD_DIR)
       .resolve("classmerging.jar");
   private static final Path EXAMPLE_KEEP = Paths.get(ToolHelper.EXAMPLES_DIR)
diff --git a/src/test/java/com/android/tools/r8/compilerapi/BinaryCompatibilityTestCollection.java b/src/test/java/com/android/tools/r8/compilerapi/BinaryCompatibilityTestCollection.java
index 2bfee60..f39b8ba 100644
--- a/src/test/java/com/android/tools/r8/compilerapi/BinaryCompatibilityTestCollection.java
+++ b/src/test/java/com/android/tools/r8/compilerapi/BinaryCompatibilityTestCollection.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.compilerapi;
 
+import static com.android.tools.r8.ToolHelper.TestDataSourceSet.computeLegacyOrGradleSpecifiedLocation;
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
@@ -13,6 +14,7 @@
 import com.android.tools.r8.TestRuntime;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.ToolHelper.TestDataSourceSet;
 import com.android.tools.r8.transformers.ClassFileTransformer;
 import com.android.tools.r8.transformers.ClassFileTransformer.InnerClassPredicate;
 import com.android.tools.r8.utils.DescriptorUtils;
@@ -89,7 +91,11 @@
   public void runJunitOnTestClass(Class<? extends T> test) throws Exception {
     List<Class<? extends T>> testClasses = Collections.singletonList(test);
     runJunitOnTestClasses(
-        generateJarForTestClasses(testClasses, getPendingAdditionalClassesForTests()), testClasses);
+        generateJarForTestClasses(
+            computeLegacyOrGradleSpecifiedLocation(),
+            testClasses,
+            getPendingAdditionalClassesForTests()),
+        testClasses);
   }
 
   private void runJunitOnTestClasses(Path testJar, Collection<Class<? extends T>> tests)
@@ -128,35 +134,40 @@
     return ToolHelper.getHamcrestFromDeps();
   }
 
-  public Path generateJarForCheckedInTestClasses() throws Exception {
-    return generateJarForTestClasses(getCheckedInTestClasses(), Collections.emptyList());
+  public Path generateJarForCheckedInTestClasses(TestDataSourceSet testDataSourceSet)
+      throws Exception {
+    return generateJarForTestClasses(
+        testDataSourceSet, getCheckedInTestClasses(), Collections.emptyList());
   }
 
   private Path generateJarForTestClasses(
-      Collection<Class<? extends T>> classes, List<Class<?>> additionalPendingClassesForTest)
+      TestDataSourceSet testDataSourceSet,
+      Collection<Class<? extends T>> classes,
+      List<Class<?>> additionalPendingClassesForTest)
       throws Exception {
     Path jar = getTemp().newFolder().toPath().resolve("test.jar");
     ZipBuilder zipBuilder = ZipBuilder.builder(jar);
     for (Class<? extends T> test : classes) {
       zipBuilder.addFilesRelative(
-          ToolHelper.getClassPathForTests(), ToolHelper.getClassFilesForInnerClasses(test));
+          ToolHelper.getClassPathForTestDataSourceSet(testDataSourceSet),
+          ToolHelper.getClassFilesForInnerClasses(testDataSourceSet, test));
       zipBuilder.addBytes(
           ZipUtils.zipEntryNameForClass(test),
-          ClassFileTransformer.create(test)
+          ClassFileTransformer.create(test, testDataSourceSet)
               .removeInnerClasses(
                   InnerClassPredicate.onName(
                       DescriptorUtils.getBinaryNameFromJavaType(test.getTypeName())))
               .transform());
     }
     zipBuilder.addFilesRelative(
-        ToolHelper.getClassPathForTests(),
+        ToolHelper.getClassPathForTestDataSourceSet(testDataSourceSet),
         getAdditionalClassesForTests().stream()
-            .map(ToolHelper::getClassFileForTestClass)
+            .map(clazz -> ToolHelper.getClassFileForTestClass(clazz, testDataSourceSet))
             .collect(Collectors.toList()));
     zipBuilder.addFilesRelative(
-        ToolHelper.getClassPathForTests(),
+        ToolHelper.getClassPathForTestDataSourceSet(testDataSourceSet),
         additionalPendingClassesForTest.stream()
-            .map(ToolHelper::getClassFileForTestClass)
+            .map(clazz -> ToolHelper.getClassFileForTestClass(clazz, testDataSourceSet))
             .collect(Collectors.toList()));
     return zipBuilder.build();
   }
@@ -166,7 +177,9 @@
     Path checkedInContents = temp.newFolder().toPath();
     Path generatedContents = temp.newFolder().toPath();
     ZipUtils.unzip(getCheckedInTestJar(), checkedInContents);
-    ZipUtils.unzip(generateJarForCheckedInTestClasses(), generatedContents);
+    ZipUtils.unzip(
+        generateJarForCheckedInTestClasses(computeLegacyOrGradleSpecifiedLocation()),
+        generatedContents);
     try (Stream<Path> existingPaths = Files.walk(checkedInContents);
         Stream<Path> generatedPaths = Files.walk(generatedContents)) {
       List<Path> existing =
@@ -185,14 +198,14 @@
     }
   }
 
-  public void replaceJarForCheckedInTestClasses() throws Exception {
+  public void replaceJarForCheckedInTestClasses(TestDataSourceSet sourceSet) throws Exception {
     Path checkedInJar = getCheckedInTestJar();
     Path tarballDir = checkedInJar.getParent();
     Path parentDir = tarballDir.getParent();
     if (!Files.exists(Paths.get(tarballDir + ".tar.gz.sha1"))) {
       throw new RuntimeException("Could not locate the SHA file for " + tarballDir);
     }
-    Path generatedJar = generateJarForCheckedInTestClasses();
+    Path generatedJar = generateJarForCheckedInTestClasses(sourceSet);
     Files.move(generatedJar, checkedInJar, StandardCopyOption.REPLACE_EXISTING);
     System.out.println(
         "Updated file in: "
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 bb72b7a..9441ae2 100644
--- a/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTest.java
+++ b/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTest.java
@@ -107,15 +107,12 @@
         "-keep class " + clazz.getName() + " { public static void main(java.lang.String[]); }");
   }
 
-  public boolean isNewGradleSetup() {
-    return "true".equals(System.getenv("USE_NEW_GRADLE_SETUP"));
-  }
-
   public Path getPathForClass(Class<?> clazz) {
     String file = clazz.getName().replace('.', '/') + ".class";
-    if (isNewGradleSetup()) {
-      return Paths.get(System.getenv("TEST_CLASSES_LOCATIONS"), file);
+    if (System.getProperty("TEST_DATA_LOCATION") != null) {
+      return Paths.get(System.getProperty("TEST_DATA_LOCATION"), file);
     } else {
+      assert System.getProperty("USE_NEW_GRADLE_SETUP") == null;
       return Paths.get("build", "classes", "java", "test", file);
     }
   }
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 6a28f4d..b1e473f 100644
--- a/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java
+++ b/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.compilerapi;
 
+import static com.android.tools.r8.ToolHelper.getClassPathForTests;
 import static com.android.tools.r8.ToolHelper.isTestingR8Lib;
 
 import com.android.tools.r8.ToolHelper;
@@ -18,6 +19,7 @@
 import com.android.tools.r8.compilerapi.globalsynthetics.GlobalSyntheticsTest;
 import com.android.tools.r8.compilerapi.globalsyntheticsgenerator.GlobalSyntheticsGeneratorTest;
 import com.android.tools.r8.compilerapi.inputdependencies.InputDependenciesTest;
+import com.android.tools.r8.compilerapi.inputmap.InputMapTest;
 import com.android.tools.r8.compilerapi.mapid.CustomMapIdTest;
 import com.android.tools.r8.compilerapi.mockdata.MockClass;
 import com.android.tools.r8.compilerapi.mockdata.MockClassWithAssertion;
@@ -63,7 +65,8 @@
           ExtractMarkerApiTest.ApiTest.class,
           PartitionMapCommandTest.ApiTest.class,
           CancelCompilationCheckerTest.ApiTest.class,
-          GlobalSyntheticsGeneratorTest.ApiTest.class);
+          GlobalSyntheticsGeneratorTest.ApiTest.class,
+          InputMapTest.ApiTest.class);
 
   private static final List<Class<? extends CompilerApiTest>> CLASSES_PENDING_BINARY_COMPATIBILITY =
       ImmutableList.of();
@@ -120,6 +123,7 @@
   public List<String> getVmArgs() {
     return ImmutableList.of(
         makeProperty("com.android.tools.r8.enableTestAssertions", "1"),
+        makeProperty("TEST_DATA_LOCATION", getClassPathForTests().toString()),
         makeProperty(CompilerApiTest.API_TEST_MODE_KEY, CompilerApiTest.API_TEST_MODE_EXTERNAL),
         makeProperty(
             CompilerApiTest.API_TEST_LIB_KEY,
diff --git a/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollectionTest.java b/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollectionTest.java
index a2d7693..e64b136 100644
--- a/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollectionTest.java
+++ b/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollectionTest.java
@@ -6,6 +6,7 @@
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper.TestDataSourceSet;
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
 import org.junit.runner.RunWith;
@@ -53,6 +54,7 @@
   public static void main(String[] args) throws Exception {
     TemporaryFolder temp = new TemporaryFolder();
     temp.create();
-    new CompilerApiTestCollection(temp).replaceJarForCheckedInTestClasses();
+    new CompilerApiTestCollection(temp)
+        .replaceJarForCheckedInTestClasses(TestDataSourceSet.TESTS_JAVA_8);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/compilerapi/inputmap/InputMapTest.java b/src/test/java/com/android/tools/r8/compilerapi/inputmap/InputMapTest.java
new file mode 100644
index 0000000..af8c674
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/compilerapi/inputmap/InputMapTest.java
@@ -0,0 +1,123 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.compilerapi.inputmap;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.D8;
+import com.android.tools.r8.D8Command;
+import com.android.tools.r8.DexIndexedConsumer;
+import com.android.tools.r8.ProgramConsumer;
+import com.android.tools.r8.R8;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.StringConsumer;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.compilerapi.CompilerApiTest;
+import com.android.tools.r8.compilerapi.CompilerApiTestRunner;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.BooleanBox;
+import com.android.tools.r8.utils.ThrowingBiConsumer;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import java.util.Arrays;
+import java.util.List;
+import org.junit.Test;
+
+public class InputMapTest extends CompilerApiTestRunner {
+
+  public InputMapTest(TestParameters parameters) {
+    super(parameters);
+  }
+
+  @Override
+  public Class<? extends CompilerApiTest> binaryTestClass() {
+    return ApiTest.class;
+  }
+
+  @Test
+  public void testD8() throws Exception {
+    ApiTest test = new ApiTest(ApiTest.PARAMETERS);
+    runTest(test::runTestD8);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    ApiTest test = new ApiTest(ApiTest.PARAMETERS);
+    runTest(test::runTestR8);
+  }
+
+  private void runTest(ThrowingBiConsumer<ProgramConsumer, StringConsumer, Exception> test)
+      throws Exception {
+    Path output = temp.newFolder().toPath().resolve("out.jar");
+    StringBuilder mappingBuilder = new StringBuilder();
+    BooleanBox didGetMappingContent = new BooleanBox(false);
+    test.accept(
+        new DexIndexedConsumer.ArchiveConsumer(output),
+        (mappingContent, handler) -> {
+          mappingBuilder.append(mappingContent);
+          didGetMappingContent.set(true);
+        });
+    assertTrue(didGetMappingContent.get());
+
+    // Extract the map hash from the file. This is always set by R8 to a SHA 256 hash.
+    String mappingContent = mappingBuilder.toString();
+    assertThat(mappingContent, containsString("com.android.tools.r8.compilerapi.originaldata ->"));
+  }
+
+  public static class ApiTest extends CompilerApiTest {
+    private static final List<String> mappingLines =
+        Arrays.asList(
+            "# {'id':'com.android.tools.r8.mapping','version':'2.2'}",
+            "com.android.tools.r8.compilerapi.originaldata ->"
+                + " com.android.tools.r8.compilerapi.mockdata:");
+
+    public ApiTest(Object parameters) {
+      super(parameters);
+    }
+
+    public void runTestR8(ProgramConsumer programConsumer, StringConsumer mappingConsumer)
+        throws Exception {
+      temp.create();
+      Path inputMap = temp.newFolder().toPath().resolve("r8inputmapping.txt");
+      Files.write(inputMap, mappingLines, StandardOpenOption.CREATE_NEW);
+      R8.run(
+          R8Command.builder()
+              .addClassProgramData(getBytesForClass(getMockClass()), Origin.unknown())
+              .addProguardConfiguration(getKeepMainRules(getMockClass()), Origin.unknown())
+              .addLibraryFiles(getJava8RuntimeJar())
+              .setProgramConsumer(programConsumer)
+              .setProguardMapConsumer(mappingConsumer)
+              .setProguardMapInputFile(inputMap)
+              .build());
+    }
+
+    public void runTestD8(ProgramConsumer programConsumer, StringConsumer mappingConsumer)
+        throws Exception {
+      temp.create();
+      Path inputMap = temp.newFolder().toPath().resolve("d8inputmap.txt");
+      Files.write(inputMap, mappingLines, StandardOpenOption.CREATE_NEW);
+      D8.run(
+          D8Command.builder()
+              .addClassProgramData(getBytesForClass(getMockClass()), Origin.unknown())
+              .addLibraryFiles(getJava8RuntimeJar())
+              .setProgramConsumer(programConsumer)
+              .setProguardMapConsumer(mappingConsumer)
+              .setProguardMapInputFile(inputMap)
+              .build());
+    }
+
+    @Test
+    public void testD8() throws Exception {
+      runTestD8(DexIndexedConsumer.emptyConsumer(), StringConsumer.emptyConsumer());
+    }
+
+    @Test
+    public void testR8() throws Exception {
+      runTestR8(DexIndexedConsumer.emptyConsumer(), StringConsumer.emptyConsumer());
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/d8/ProguardMapSortByTest.java b/src/test/java/com/android/tools/r8/d8/ProguardMapSortByTest.java
index eac78c0..e7169c3 100644
--- a/src/test/java/com/android/tools/r8/d8/ProguardMapSortByTest.java
+++ b/src/test/java/com/android/tools/r8/d8/ProguardMapSortByTest.java
@@ -58,7 +58,7 @@
         .release()
         .addProgramClasses(A.class, B.class, C.class, D.class, E.class)
         .setMinApi(parameters)
-        .apply(b -> b.getBuilder().setProguardInputMapFile(mappingFile))
+        .apply(b -> b.getBuilder().setProguardMapInputFile(mappingFile))
         .run(parameters.getRuntime(), A.class)
         .assertSuccessWithOutputLines("Hello world!")
         .app()
diff --git a/src/test/java/com/android/tools/r8/dagger/DaggerBasicNotSingletonUsingBindsTest.java b/src/test/java/com/android/tools/r8/dagger/DaggerBasicNotSingletonUsingBindsTest.java
index 173efa1..bffbe9d 100644
--- a/src/test/java/com/android/tools/r8/dagger/DaggerBasicNotSingletonUsingBindsTest.java
+++ b/src/test/java/com/android/tools/r8/dagger/DaggerBasicNotSingletonUsingBindsTest.java
@@ -77,7 +77,6 @@
         .addProgramFiles(getProgramFiles(target))
         .setMinApi(parameters)
         .addKeepMainRule(MAIN_CLASS)
-        .addOptionsModification(o -> o.testing.enableLir())
         .run(parameters.getRuntime(), MAIN_CLASS)
         .inspect(this::inspect)
         .assertSuccessWithOutputLines(EXPECTED_OUTPUT);
diff --git a/src/test/java/com/android/tools/r8/debuginfo/composepc/ComposePcEncodingTest.java b/src/test/java/com/android/tools/r8/debuginfo/composepc/ComposePcEncodingTest.java
index 311ea41..5aca406 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/composepc/ComposePcEncodingTest.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/composepc/ComposePcEncodingTest.java
@@ -111,7 +111,7 @@
         // We only optimize line info in release mode and with a mapping file output enabled.
         .release()
         .internalEnableMappingOutput()
-        .apply(b -> b.getBuilder().setProguardInputMapFile(r8OutputMap))
+        .apply(b -> b.getBuilder().setProguardMapInputFile(r8OutputMap))
         // Forcing jumbo processing will shift the PC values on the methods.
         .addOptionsModification(o -> o.testing.forceJumboStringProcessing = true)
         .compile()
diff --git a/src/test/java/com/android/tools/r8/desugar/b72538146/B72538146.java b/src/test/java/com/android/tools/r8/desugar/b72538146/B72538146.java
index 85362c4..b385299 100644
--- a/src/test/java/com/android/tools/r8/desugar/b72538146/B72538146.java
+++ b/src/test/java/com/android/tools/r8/desugar/b72538146/B72538146.java
@@ -59,7 +59,7 @@
     // Run the classloader test loading the two dex applications.
     testForD8()
         .addProgramFiles(
-            Paths.get(ToolHelper.TESTS_BUILD_DIR)
+            Paths.get(ToolHelper.THIRD_PARTY_DIR)
                 .resolve("examplesAndroidO")
                 .resolve("classes")
                 .resolve("classloader")
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java
index 09d3213..df487eb 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java
@@ -89,7 +89,6 @@
         .addOptionsModification(
             options -> {
               // The check for $default$query relies on inlining.
-              options.testing.enableLir();
               options.inlinerOptions().simpleInliningInstructionLimit = 5;
             })
         .compile()
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxNull2ArgumentTest.java b/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxNull2ArgumentTest.java
index 1130986..ccff4dc 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxNull2ArgumentTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxNull2ArgumentTest.java
@@ -31,7 +31,6 @@
         .addInnerClasses(getClass())
         .setMinApi(parameters)
         .addKeepMainRule(Main.class)
-        .addOptionsModification(options -> options.testing.disableLir())
         .run(parameters.getRuntime(), Main.class)
         .assertSuccessWithOutputLines("true", "null");
   }
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxNullArgumentTest.java b/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxNullArgumentTest.java
index 1f0492d..7df511e 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxNullArgumentTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxNullArgumentTest.java
@@ -30,7 +30,6 @@
         .addInnerClasses(getClass())
         .setMinApi(parameters)
         .addKeepMainRule(Main.class)
-        .addOptionsModification(options -> options.testing.disableLir())
         .run(parameters.getRuntime(), Main.class)
         .assertFailureWithErrorThatThrows(NullPointerException.class);
   }
diff --git a/src/test/java/com/android/tools/r8/examples/hello/HelloTestRunner.java b/src/test/java/com/android/tools/r8/examples/hello/HelloTestRunner.java
index cb305d2..55baca1 100644
--- a/src/test/java/com/android/tools/r8/examples/hello/HelloTestRunner.java
+++ b/src/test/java/com/android/tools/r8/examples/hello/HelloTestRunner.java
@@ -7,10 +7,7 @@
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.examples.ExamplesTestBase;
 import com.android.tools.r8.utils.StringUtils;
-import java.io.IOException;
-import java.nio.file.Path;
 import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 
@@ -22,34 +19,18 @@
     return getTestParameters().withAllRuntimesAndApiLevels().enableApiLevelsForCf().build();
   }
 
-  // The "hello" program is reused in various tests via these static methods.
-
-  public static Class<?> getHelloClass() {
-    return Hello.class;
-  }
-
-  public static String getExpectedOutput() {
-    return StringUtils.lines("Hello, world");
-  }
-
-  public static Path writeHelloProgramJar(TemporaryFolder temp) throws IOException {
-    Path jar = temp.newFolder().toPath().resolve("hello.jar");
-    writeClassesToJar(jar, Hello.class);
-    return jar;
-  }
-
   public HelloTestRunner(TestParameters parameters) {
     super(parameters);
   }
 
   @Override
   public Class<?> getMainClass() {
-    return getHelloClass();
+    return Hello.class;
   }
 
   @Override
   public String getExpected() {
-    return getExpectedOutput();
+    return StringUtils.lines("Hello, world");
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java b/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java
index 070e9e3..c786a4b 100644
--- a/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java
+++ b/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java
@@ -31,6 +31,43 @@
 @RunWith(Parameterized.class)
 public class Proto2BuilderShrinkingTest extends ProtoShrinkingTestBase {
 
+  private enum MainClassesConfig {
+    ALL,
+    BUILDER_WITH_ONE_OF_SETTER_TEST_CLASS,
+    BUILDER_WITH_PRIMITIVE_SETTERS_TEST_CLASS,
+    BUILDER_WITH_PROTO_BUILDER_SETTER_TEST_CLASS,
+    BUILDER_WITH_PROTO_SETTER_TEST_CLASS,
+    BUILDER_WITH_REUSED_SETTERS_TEST_CLASS,
+    HAS_FLAGGED_OFF_EXTENSION_BUILDER_TEST_CLASS;
+
+    List<String> getMainClasses() {
+      switch (this) {
+        case ALL:
+          return ImmutableList.of(
+              "proto2.BuilderWithOneofSetterTestClass",
+              "proto2.BuilderWithPrimitiveSettersTestClass",
+              "proto2.BuilderWithProtoBuilderSetterTestClass",
+              "proto2.BuilderWithProtoSetterTestClass",
+              "proto2.BuilderWithReusedSettersTestClass",
+              "proto2.HasFlaggedOffExtensionBuilderTestClass");
+        case BUILDER_WITH_ONE_OF_SETTER_TEST_CLASS:
+          return ImmutableList.of("proto2.BuilderWithOneofSetterTestClass");
+        case BUILDER_WITH_PRIMITIVE_SETTERS_TEST_CLASS:
+          return ImmutableList.of("proto2.BuilderWithPrimitiveSettersTestClass");
+        case BUILDER_WITH_PROTO_BUILDER_SETTER_TEST_CLASS:
+          return ImmutableList.of("proto2.BuilderWithProtoBuilderSetterTestClass");
+        case BUILDER_WITH_PROTO_SETTER_TEST_CLASS:
+          return ImmutableList.of("proto2.BuilderWithProtoSetterTestClass");
+        case BUILDER_WITH_REUSED_SETTERS_TEST_CLASS:
+          return ImmutableList.of("proto2.BuilderWithReusedSettersTestClass");
+        case HAS_FLAGGED_OFF_EXTENSION_BUILDER_TEST_CLASS:
+          return ImmutableList.of("proto2.HasFlaggedOffExtensionBuilderTestClass");
+        default:
+          throw new Unreachable();
+      }
+    }
+  }
+
   private static final String METHOD_TO_INVOKE_ENUM =
       "com.google.protobuf.GeneratedMessageLite$MethodToInvoke";
 
@@ -38,7 +75,7 @@
       ImmutableList.of(PROTO2_EXAMPLES_JAR, PROTO2_PROTO_JAR, PROTOBUF_LITE_JAR);
 
   @Parameter(0)
-  public List<String> mains;
+  public MainClassesConfig config;
 
   @Parameter(1)
   public TestParameters parameters;
@@ -46,20 +83,7 @@
   @Parameters(name = "{1}, {0}")
   public static List<Object[]> data() {
     return buildParameters(
-        ImmutableList.of(
-            ImmutableList.of("proto2.BuilderWithOneofSetterTestClass"),
-            ImmutableList.of("proto2.BuilderWithPrimitiveSettersTestClass"),
-            ImmutableList.of("proto2.BuilderWithProtoBuilderSetterTestClass"),
-            ImmutableList.of("proto2.BuilderWithProtoSetterTestClass"),
-            ImmutableList.of("proto2.BuilderWithReusedSettersTestClass"),
-            ImmutableList.of("proto2.HasFlaggedOffExtensionBuilderTestClass"),
-            ImmutableList.of(
-                "proto2.BuilderWithOneofSetterTestClass",
-                "proto2.BuilderWithPrimitiveSettersTestClass",
-                "proto2.BuilderWithProtoBuilderSetterTestClass",
-                "proto2.BuilderWithProtoSetterTestClass",
-                "proto2.BuilderWithReusedSettersTestClass",
-                "proto2.HasFlaggedOffExtensionBuilderTestClass")),
+        MainClassesConfig.values(),
         getTestParameters().withDefaultDexRuntime().withAllApiLevels().build());
   }
 
@@ -68,7 +92,7 @@
     R8TestCompileResult result =
         testForR8(parameters.getBackend())
             .addProgramFiles(PROGRAM_FILES)
-            .addKeepMainRules(mains)
+            .addKeepMainRules(config.getMainClasses())
             .addKeepRuleFiles(PROTOBUF_LITE_PROGUARD_RULES)
             .allowAccessModification()
             .allowDiagnosticMessages()
@@ -83,7 +107,7 @@
             .apply(this::inspectWarningMessages)
             .inspect(this::inspect);
 
-    for (String main : mains) {
+    for (String main : config.getMainClasses()) {
       result.run(parameters.getRuntime(), main).assertSuccessWithOutput(getExpectedOutput(main));
     }
   }
@@ -195,7 +219,7 @@
 
     DexType methodToInvokeType =
         outputInspector.clazz(METHOD_TO_INVOKE_ENUM).getDexProgramClass().getType();
-    for (String main : mains) {
+    for (String main : config.getMainClasses()) {
       MethodSubject mainMethodSubject = outputInspector.clazz(main).mainMethod();
       assertThat(mainMethodSubject, isPresent());
 
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
index 00e0441..b00cc7e 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
@@ -123,7 +123,6 @@
               o.inlinerOptions().enableInlining = inlining;
               o.inlinerOptions().enableInliningOfInvokesWithNullableReceivers = false;
               o.inlinerOptions().simpleInliningInstructionLimit = 7;
-              o.testing.enableLir();
               o.testing.horizontallyMergedClassesConsumer = this::fixInliningNullabilityClass;
               o.testing.horizontalClassMergingTarget =
                   (appView, candidates, target) -> {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
index 483ed33..da23b67 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
@@ -310,8 +310,6 @@
             .allowAccessModification()
             .enableInliningAnnotations()
             .addDontObfuscate()
-            // Using LIR changes the inlining heuristics so enable it consistently.
-            .addOptionsModification(o -> o.testing.enableLir())
             .setMinApi(parameters)
             .run(parameters.getRuntime(), main)
             .assertSuccessWithOutput(javaOutput);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineFunctionalInterfaceMethodImplementedByLambdasTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineFunctionalInterfaceMethodImplementedByLambdasTest.java
index ccd59c6..7969f45 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineFunctionalInterfaceMethodImplementedByLambdasTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineFunctionalInterfaceMethodImplementedByLambdasTest.java
@@ -38,7 +38,6 @@
         .addKeepMainRule(TestClass.class)
         .enableNeverClassInliningAnnotations()
         .setMinApi(parameters)
-        .addOptionsModification(o -> o.testing.enableLir())
         .compile()
         .inspect(this::inspect)
         .run(parameters.getRuntime(), TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InliningOptimize.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InliningOptimize.java
index dd9edd7..55230e8 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InliningOptimize.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InliningOptimize.java
@@ -30,7 +30,6 @@
         .addKeepRules("-keep,allowoptimization class ** {\n" + "*;\n" + "}")
         .addOptionsModification(
             options -> {
-              options.testing.enableLir();
               options.inlinerOptions().simpleInliningInstructionLimit = 5;
             })
         .compile()
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FieldReadForWriteTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FieldReadForWriteTest.java
index 37e6c57..8de4937 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FieldReadForWriteTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FieldReadForWriteTest.java
@@ -39,7 +39,6 @@
             HorizontallyMergedClassesInspector::assertNoClassesMerged)
         .enableNoHorizontalClassMergingAnnotations()
         .setMinApi(parameters)
-        .addOptionsModification(o -> o.testing.enableLir())
         .run(parameters.getRuntime(), Main.class)
         .assertSuccessWithOutputLines("42")
         .inspect(inspector -> assertThat(inspector.clazz(anim.class), isAbsent()));
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java
index 5c3fe96..e3e4fa2 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java
@@ -298,7 +298,6 @@
             .allowAccessModification()
             .addDontObfuscate()
             .addOptionsModification(this::configure)
-            .addOptionsModification(o -> o.testing.enableLir())
             .setMinApi(parameters)
             .run(parameters.getRuntime(), main)
             .assertSuccessWithOutput(javaOutput);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/NestedInterfaceMethodTest.java b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/NestedInterfaceMethodTest.java
index 9626829..83c7f4f 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/NestedInterfaceMethodTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/NestedInterfaceMethodTest.java
@@ -60,7 +60,6 @@
             .enableNoVerticalClassMergingAnnotations()
             .addOptionsModification(
                 options -> {
-                  options.testing.enableLir();
                   options.enableDevirtualization = false;
                   options.inlinerOptions().enableInliningOfInvokesWithNullableReceivers = false;
                   // The checks for I being present rely on not simple inlining.
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java
index e74dd87..aad5246 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java
@@ -129,7 +129,6 @@
             testBuilder ->
                 testBuilder
                     .addKeepRules(keepClassMethod(mainClassName, testMethodSignature))
-                    .addOptionsModification(o -> o.testing.enableLir())
                     .addOptionsModification(disableClassInliner))
         .inspect(
             inspector -> {
diff --git a/src/test/java/com/android/tools/r8/maindexlist/b72312389/B72312389.java b/src/test/java/com/android/tools/r8/maindexlist/b72312389/B72312389.java
index e303689..8c717a0 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/b72312389/B72312389.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/b72312389/B72312389.java
@@ -43,7 +43,7 @@
         .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.O))
         .addProgramFiles(
             Paths.get(
-                ToolHelper.TESTS_BUILD_DIR,
+                ToolHelper.THIRD_PARTY_DIR,
                 "examplesAndroidO",
                 "classes",
                 "instrumentationtest",
diff --git a/src/test/java/com/android/tools/r8/mappingcompose/ComposeUnknownJsonD8Test.java b/src/test/java/com/android/tools/r8/mappingcompose/ComposeUnknownJsonD8Test.java
index 032f5da..a8e22c5 100644
--- a/src/test/java/com/android/tools/r8/mappingcompose/ComposeUnknownJsonD8Test.java
+++ b/src/test/java/com/android/tools/r8/mappingcompose/ComposeUnknownJsonD8Test.java
@@ -53,7 +53,7 @@
         .addOptionsModification(
             options ->
                 assertTrue(options.mappingComposeOptions().enableExperimentalMappingComposition))
-        .apply(b -> b.getBuilder().setProguardInputMapFile(inputMap))
+        .apply(b -> b.getBuilder().setProguardMapInputFile(inputMap))
         .apply(testBuilder)
         .allowStdoutMessages()
         .collectStdout()
diff --git a/src/test/java/com/android/tools/r8/mappingcompose/ComposeWithMissingDebugInfoTest.java b/src/test/java/com/android/tools/r8/mappingcompose/ComposeWithMissingDebugInfoTest.java
index 950abdc..02d145d 100644
--- a/src/test/java/com/android/tools/r8/mappingcompose/ComposeWithMissingDebugInfoTest.java
+++ b/src/test/java/com/android/tools/r8/mappingcompose/ComposeWithMissingDebugInfoTest.java
@@ -57,7 +57,7 @@
         .apply(
             b ->
                 b.getBuilder()
-                    .setProguardInputMapFile(inputMap)
+                    .setProguardMapInputFile(inputMap)
                     .setProguardMapConsumer((string, handler) -> mappingComposed.append(string)))
         .run(parameters.getRuntime(), Main.class)
         .assertSuccessWithOutputLines("42");
@@ -77,8 +77,7 @@
             .map(method -> method.getRetracedMethod().getMethodName())
             .collect(Collectors.toSet());
     Set<String> expectedMethods = new HashSet<>();
-    expectedMethods.add("a");
-    // TODO(b/297970886): We should observe 'foo' and not 'a'.
+    expectedMethods.add("foo");
     assertEquals(expectedMethods, foundMethods);
   }
 
diff --git a/src/test/java/com/android/tools/r8/regress/Regress160394262Test.java b/src/test/java/com/android/tools/r8/regress/Regress160394262Test.java
index a39c770..e21af03 100644
--- a/src/test/java/com/android/tools/r8/regress/Regress160394262Test.java
+++ b/src/test/java/com/android/tools/r8/regress/Regress160394262Test.java
@@ -50,7 +50,6 @@
         .addKeepMainRule(TestClass.class)
         .addInnerClasses(Regress160394262Test.class)
         .setMinApi(parameters)
-        .addOptionsModification(o -> o.testing.enableLir())
         .run(parameters.getRuntime(), TestClass.class)
         .assertSuccessWithOutput(EXPECTED)
         .inspect(this::checkJoinerIsClassInlined);
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 6abc4c7..984d8e2 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
@@ -35,6 +35,7 @@
 import com.android.tools.r8.retrace.stacktraces.ClassWithDashStackTrace;
 import com.android.tools.r8.retrace.stacktraces.ColonInFileNameStackTrace;
 import com.android.tools.r8.retrace.stacktraces.DifferentLineNumberSpanStackTrace;
+import com.android.tools.r8.retrace.stacktraces.ExceptionMessageWithClassNameInMessage;
 import com.android.tools.r8.retrace.stacktraces.FileNameExtensionStackTrace;
 import com.android.tools.r8.retrace.stacktraces.FoundMethodVerboseStackTrace;
 import com.android.tools.r8.retrace.stacktraces.IdentityMappingStackTrace;
@@ -46,6 +47,7 @@
 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.InlineRemoveFrameJava17StackTrace;
 import com.android.tools.r8.retrace.stacktraces.InlineSourceFileContextStackTrace;
 import com.android.tools.r8.retrace.stacktraces.InlineSourceFileStackTrace;
 import com.android.tools.r8.retrace.stacktraces.InlineWithLineNumbersStackTrace;
@@ -296,6 +298,11 @@
   }
 
   @Test
+  public void testExceptionMessageWithClassNameInMessage() throws Exception {
+    runRetraceTest(new ExceptionMessageWithClassNameInMessage());
+  }
+
+  @Test
   public void testUnknownSourceStackTrace() throws Exception {
     runRetraceTest(new UnknownSourceStackTrace());
   }
@@ -311,6 +318,11 @@
   }
 
   @Test
+  public void testInlineRemoveFrameJava17StackTrace() throws Exception {
+    runRetraceTest(new InlineRemoveFrameJava17StackTrace());
+  }
+
+  @Test
   public void testColonInSourceFileNameStackTrace() throws Exception {
     runRetraceTest(new ColonInFileNameStackTrace());
   }
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 480f8bf..abcf319 100644
--- a/src/test/java/com/android/tools/r8/retrace/StackTraceRegularExpressionParserTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/StackTraceRegularExpressionParserTests.java
@@ -1142,6 +1142,60 @@
     runRetraceTest("(?:.*?\\(\\s*%s(?:\\s*:\\s*%l\\s*)?\\)\\s*%c\\.%m)|", new LongLineStackTrace());
   }
 
+  /** This is a regression test for b/300416467. */
+  @Test
+  public void testGraphsModule() {
+    runRetraceTest(
+        "(?:.*, %c,.*)",
+        new StackTraceForTest() {
+          @Override
+          public List<String> obfuscatedStackTrace() {
+            return ImmutableList.of(
+                "TaskGraph@4b0b0f5(\"SomePipeline\") [created at 08-19 18:02:01.531]",
+                "                                     method                 future  (Note: all"
+                    + " times are in ms. relative to TaskGraph creation)",
+                "    requested,   queued,  started, finished (+  latency), finished, task",
+                "        0.057,    1.290,    2.690,    3.408 (+    0.718),    66.376,"
+                    + " foo.bar.baz.c.b.a.c.b.a.a.b, 0, 6, 0",
+                "");
+          }
+
+          @Override
+          public String mapping() {
+            return StringUtils.unixLines("some.original.factory -> foo.bar.baz.c.b.a.c.b.a.a.b:");
+          }
+
+          @Override
+          public List<String> retracedStackTrace() {
+            return ImmutableList.of(
+                "TaskGraph@4b0b0f5(\"SomePipeline\") [created at 08-19 18:02:01.531]",
+                "                                     method                 future  (Note: all"
+                    + " times are in ms. relative to TaskGraph creation)",
+                "    requested,   queued,  started, finished (+  latency), finished, task",
+                "        0.057,    1.290,    2.690,    3.408 (+    0.718),    66.376,"
+                    + " some.original.factory, 0, 6, 0",
+                "");
+          }
+
+          @Override
+          public List<String> retraceVerboseStackTrace() {
+            return ImmutableList.of(
+                "TaskGraph@4b0b0f5(\"SomePipeline\") [created at 08-19 18:02:01.531]",
+                "                                     method                 future  (Note: all"
+                    + " times are in ms. relative to TaskGraph creation)",
+                "    requested,   queued,  started, finished (+  latency), finished, task",
+                "        0.057,    1.290,    2.690,    3.408 (+    0.718),    66.376,"
+                    + " some.original.factory, 0, 6, 0",
+                "");
+          }
+
+          @Override
+          public int expectedWarnings() {
+            return 0;
+          }
+        });
+  }
+
   private TestDiagnosticMessagesImpl runRetraceTest(
       String regularExpression, StackTraceForTest stackTraceForTest) {
     TestDiagnosticMessagesImpl diagnosticsHandler = new TestDiagnosticMessagesImpl();
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiBinaryCompatibilityTest.java b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiBinaryCompatibilityTest.java
index a4983b2..bd9e4ec 100644
--- a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiBinaryCompatibilityTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiBinaryCompatibilityTest.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper.TestDataSourceSet;
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
 import org.junit.runner.RunWith;
@@ -42,6 +43,7 @@
   public static void main(String[] args) throws Exception {
     TemporaryFolder temp = new TemporaryFolder();
     temp.create();
-    new RetraceApiTestCollection(temp).replaceJarForCheckedInTestClasses();
+    new RetraceApiTestCollection(temp)
+        .replaceJarForCheckedInTestClasses(TestDataSourceSet.TESTS_JAVA_8);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/ExceptionMessageWithClassNameInMessage.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/ExceptionMessageWithClassNameInMessage.java
new file mode 100644
index 0000000..496afb3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/ExceptionMessageWithClassNameInMessage.java
@@ -0,0 +1,49 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.retrace.stacktraces;
+
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+
+/** This is a reproduction of b/300416467 */
+public class ExceptionMessageWithClassNameInMessage implements StackTraceForTest {
+
+  @Override
+  public List<String> obfuscatedStackTrace() {
+    return ImmutableList.of(
+        "10-26 19:26:24.749 10159 26250 26363 E Tycho.crl: Exception",
+        "10-26 19:26:24.749 10159 26250 26363 E Tycho.crl: java.util.concurrent.ExecutionException:"
+            + " ary: eu: Exception in CronetUrlRequest: net::ERR_CONNECTION_CLOSED, ErrorCode=5,"
+            + " InternalErrorCode=-100, Retryable=true");
+  }
+
+  @Override
+  public String mapping() {
+    return "foo.bar.baz -> net:";
+  }
+
+  @Override
+  public List<String> retracedStackTrace() {
+    return ImmutableList.of(
+        "10-26 19:26:24.749 10159 26250 26363 E Tycho.crl: Exception",
+        "10-26 19:26:24.749 10159 26250 26363 E Tycho.crl: java.util.concurrent.ExecutionException:"
+            + " ary: eu: Exception in CronetUrlRequest: foo.bar.baz::ERR_CONNECTION_CLOSED,"
+            + " ErrorCode=5, InternalErrorCode=-100, Retryable=true");
+  }
+
+  @Override
+  public List<String> retraceVerboseStackTrace() {
+    return ImmutableList.of(
+        "10-26 19:26:24.749 10159 26250 26363 E Tycho.crl: Exception",
+        "10-26 19:26:24.749 10159 26250 26363 E Tycho.crl: java.util.concurrent.ExecutionException:"
+            + " ary: eu: Exception in CronetUrlRequest: foo.bar.baz::ERR_CONNECTION_CLOSED,"
+            + " ErrorCode=5, InternalErrorCode=-100, Retryable=true");
+  }
+
+  @Override
+  public int expectedWarnings() {
+    return 0;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineRemoveFrameJava17StackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineRemoveFrameJava17StackTrace.java
new file mode 100644
index 0000000..b899077
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/InlineRemoveFrameJava17StackTrace.java
@@ -0,0 +1,59 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.retrace.stacktraces;
+
+import com.android.tools.r8.utils.StringUtils;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Manually throwing an NPE has a different syntax than it happening where there is no message
+ * afterwards.
+ */
+public class InlineRemoveFrameJava17StackTrace implements StackTraceForTest {
+
+  @Override
+  public List<String> obfuscatedStackTrace() {
+    return Arrays.asList(
+        "java.lang.NullPointerException", "\tat A.a(SourceFile:1)", "\tat A.main(SourceFile:1)");
+  }
+
+  @Override
+  public String mapping() {
+    return StringUtils.joinLines(
+        "# {'id':'com.android.tools.r8.mapping','version':'2.2'}",
+        "foo.Class -> A:",
+        "    1:5:void inlinable():90:90 -> a",
+        "    1:5:void caller():97 -> a",
+        "      # {'id':'com.android.tools.r8.rewriteFrame',"
+            + "'conditions':['throws(Ljava/lang/NullPointerException;)'],"
+            + "'actions':['removeInnerFrames(1)']}",
+        "    1:5:void outerCaller():107 -> a",
+        "    1:1:void main():111:111 -> main");
+  }
+
+  @Override
+  public List<String> retracedStackTrace() {
+    return Arrays.asList(
+        "java.lang.NullPointerException",
+        "\tat foo.Class.caller(Class.java:97)",
+        "\tat foo.Class.outerCaller(Class.java:107)",
+        "\tat foo.Class.main(Class.java:111)");
+  }
+
+  @Override
+  public List<String> retraceVerboseStackTrace() {
+    return Arrays.asList(
+        "java.lang.NullPointerException",
+        "\tat foo.Class.void caller()(Class.java:97)",
+        "\tat foo.Class.void outerCaller()(Class.java:107)",
+        "\tat foo.Class.void main()(Class.java:111)");
+  }
+
+  @Override
+  public int expectedWarnings() {
+    return 0;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/arrays/ConstClassArrayWithNonUniqueValuesTest.java b/src/test/java/com/android/tools/r8/rewrite/arrays/ConstClassArrayWithNonUniqueValuesTest.java
index cc339a8..213e3e4 100644
--- a/src/test/java/com/android/tools/r8/rewrite/arrays/ConstClassArrayWithNonUniqueValuesTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/arrays/ConstClassArrayWithNonUniqueValuesTest.java
@@ -9,12 +9,15 @@
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestCompilerBuilder;
 import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.InstructionSubject;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
 import java.util.Arrays;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -31,11 +34,15 @@
   @Parameter(1)
   public CompilationMode compilationMode;
 
-  @Parameters(name = "{0}, mode = {1}")
+  @Parameter(2)
+  public Integer maxMaterializingConstants;
+
+  @Parameters(name = "{0}, mode = {1}, maxMaterializingConstants = {2}")
   public static Iterable<?> data() {
     return buildParameters(
         getTestParameters().withDefaultCfRuntime().withDexRuntimesAndAllApiLevels().build(),
-        CompilationMode.values());
+        CompilationMode.values(),
+        ImmutableList.of(Constants.U8BIT_MAX - 16, 2));
   }
 
   private static final String EXPECTED_OUTPUT = StringUtils.lines("100", "104");
@@ -60,9 +67,19 @@
         method.streamInstructions().filter(InstructionSubject::isConstClass).count());
   }
 
-  private void inspect(CodeInspector inspector) {
+  private void configure(TestCompilerBuilder<?, ?, ?, ?, ?> builder) {
+    builder.addOptionsModification(
+        options ->
+            options.rewriteArrayOptions().maxMaterializingConstants = maxMaterializingConstants);
+  }
+
+  private void inspectD8(CodeInspector inspector) {
     inspect(inspector.clazz(TestClass.class).uniqueMethodWithOriginalName("m1"), 1, 100, false);
-    inspect(inspector.clazz(TestClass.class).uniqueMethodWithOriginalName("m2"), 26, 104, false);
+    inspect(
+        inspector.clazz(TestClass.class).uniqueMethodWithOriginalName("m2"),
+        maxMaterializingConstants == 2 ? 98 : 26,
+        104,
+        false);
   }
 
   @Test
@@ -71,11 +88,21 @@
     testForD8(parameters.getBackend())
         .addInnerClasses(getClass())
         .setMinApi(parameters)
+        .apply(this::configure)
         .run(parameters.getRuntime(), TestClass.class)
-        .inspect(this::inspect)
+        .inspect(this::inspectD8)
         .assertSuccessWithOutput(EXPECTED_OUTPUT);
   }
 
+  private void inspectR8(CodeInspector inspector) {
+    inspect(inspector.clazz(TestClass.class).uniqueMethodWithOriginalName("m1"), 1, 100, false);
+    inspect(
+        inspector.clazz(TestClass.class).uniqueMethodWithOriginalName("m2"),
+        maxMaterializingConstants == 2 ? 32 : 26,
+        104,
+        false);
+  }
+
   @Test
   public void testR8() throws Exception {
     testForR8(parameters.getBackend())
@@ -84,8 +111,9 @@
         .setMinApi(parameters)
         .enableInliningAnnotations()
         .addDontObfuscate()
+        .apply(this::configure)
         .run(parameters.getRuntime(), TestClass.class)
-        .inspect(this::inspect)
+        .inspect(this::inspectR8)
         .assertSuccessWithOutput(EXPECTED_OUTPUT);
   }
 
diff --git a/src/test/java/com/android/tools/r8/rewrite/arrays/ConstClassArrayWithUniqueValuesTest.java b/src/test/java/com/android/tools/r8/rewrite/arrays/ConstClassArrayWithUniqueValuesTest.java
index 4dca11b..d8227dc 100644
--- a/src/test/java/com/android/tools/r8/rewrite/arrays/ConstClassArrayWithUniqueValuesTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/arrays/ConstClassArrayWithUniqueValuesTest.java
@@ -10,11 +10,13 @@
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.InstructionSubject;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
 import java.util.Arrays;
 import java.util.Iterator;
 import org.junit.Test;
@@ -32,11 +34,15 @@
   @Parameter(1)
   public CompilationMode compilationMode;
 
-  @Parameters(name = "{0}, mode = {1}")
+  @Parameter(2)
+  public Integer maxMaterializingConstants;
+
+  @Parameters(name = "{0}, mode = {1}, maxMaterializingConstants = {2}")
   public static Iterable<?> data() {
     return buildParameters(
         getTestParameters().withDefaultCfRuntime().withDexRuntimesAndAllApiLevels().build(),
-        CompilationMode.values());
+        CompilationMode.values(),
+        ImmutableList.of(Constants.U8BIT_MAX - 16, 2));
   }
 
   private static final String EXPECTED_OUTPUT =
diff --git a/src/test/java/com/android/tools/r8/rewrite/arrays/SimplifyArrayConstructionTest.java b/src/test/java/com/android/tools/r8/rewrite/arrays/SimplifyArrayConstructionTest.java
index 9f67d23..3f75ac1 100644
--- a/src/test/java/com/android/tools/r8/rewrite/arrays/SimplifyArrayConstructionTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/arrays/SimplifyArrayConstructionTest.java
@@ -316,7 +316,7 @@
       twoDimensionalArrays();
       objectArraysFilledNewArrayRange();
       arraysThatUseFilledData();
-      arraysThatUseNewArrayEmpty();
+      arraysThatUseNewArrayEmpty(args.length);
       reversedArray();
       arrayWithCorrectCountButIncompleteCoverage();
       arrayWithExtraInitialPuts();
@@ -536,7 +536,7 @@
     }
 
     @NeverInline
-    private static void arraysThatUseNewArrayEmpty() {
+    private static void arraysThatUseNewArrayEmpty(int trickyZero) {
       // int/object of size zero should not use filled-new-array.
       int[] intArr = {};
       System.out.println(Arrays.toString(intArr));
@@ -569,7 +569,6 @@
       System.out.println(Arrays.toString(partialArray));
 
       // Non-constant array size.
-      int trickyZero = (int) (System.currentTimeMillis() / System.nanoTime());
       Object[] nonConstSize = new Object[trickyZero + 1];
       nonConstSize[0] = "a";
       System.out.println(Arrays.toString(nonConstSize));
diff --git a/src/test/java/com/android/tools/r8/rewrite/arrays/StaticGetArrayWithNonUniqueValuesTest.java b/src/test/java/com/android/tools/r8/rewrite/arrays/StaticGetArrayWithNonUniqueValuesTest.java
new file mode 100644
index 0000000..8623e55
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/arrays/StaticGetArrayWithNonUniqueValuesTest.java
@@ -0,0 +1,189 @@
+// 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.rewrite.arrays;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestCompilerBuilder;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
+import java.util.Arrays;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class StaticGetArrayWithNonUniqueValuesTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameter(1)
+  public CompilationMode compilationMode;
+
+  @Parameter(2)
+  public Integer maxMaterializingConstants;
+
+  @Parameters(name = "{0}, mode = {1}, maxMaterializingConstants = {2}")
+  public static Iterable<?> data() {
+    return buildParameters(
+        getTestParameters().withDefaultCfRuntime().withDexRuntimesAndAllApiLevels().build(),
+        CompilationMode.values(),
+        ImmutableList.of(Constants.U8BIT_MAX - 16, 2));
+  }
+
+  private static final String EXPECTED_OUTPUT = StringUtils.lines("100", "50");
+
+  public boolean canUseFilledNewArrayOfObject(TestParameters parameters) {
+    return parameters.isDexRuntime()
+        && parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N);
+  }
+
+  private void inspect(MethodSubject method, int staticGets, int puts, boolean insideCatchHandler) {
+    boolean expectingFilledNewArray =
+        canUseFilledNewArrayOfObject(parameters) && !insideCatchHandler;
+    assertEquals(
+        expectingFilledNewArray ? 0 : puts,
+        method.streamInstructions().filter(InstructionSubject::isArrayPut).count());
+    assertEquals(
+        expectingFilledNewArray ? 1 : 0,
+        method.streamInstructions().filter(InstructionSubject::isFilledNewArray).count());
+    assertEquals(
+        staticGets, method.streamInstructions().filter(InstructionSubject::isStaticGet).count());
+  }
+
+  private void configure(TestCompilerBuilder<?, ?, ?, ?, ?> builder) {
+    builder.addOptionsModification(
+        options ->
+            options.rewriteArrayOptions().maxMaterializingConstants = maxMaterializingConstants);
+  }
+
+  private void inspectD8(CodeInspector inspector) {
+    inspect(
+        inspector.clazz(TestClass.class).uniqueMethodWithOriginalName("m1"),
+        canUseFilledNewArrayOfObject(parameters) ? 100 : 1,
+        100,
+        false);
+    inspect(
+        inspector.clazz(TestClass.class).uniqueMethodWithOriginalName("m2"),
+        canUseFilledNewArrayOfObject(parameters) ? 50 : (maxMaterializingConstants == 2 ? 42 : 10),
+        50,
+        false);
+  }
+
+  @Test
+  public void testD8() throws Exception {
+    parameters.assumeDexRuntime();
+    testForD8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .setMinApi(parameters)
+        .apply(this::configure)
+        .run(parameters.getRuntime(), TestClass.class)
+        .inspect(this::inspectD8)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  private void inspectR8(CodeInspector inspector) {
+    inspect(inspector.clazz(TestClass.class).uniqueMethodWithOriginalName("m1"), 1, 100, false);
+    inspect(
+        inspector.clazz(TestClass.class).uniqueMethodWithOriginalName("m2"),
+        (parameters.isCfRuntime() && maxMaterializingConstants == 2) ? 42 : 10,
+        50,
+        false);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(TestClass.class)
+        .setMinApi(parameters)
+        .enableInliningAnnotations()
+        .addDontObfuscate()
+        .apply(this::configure)
+        .run(parameters.getRuntime(), TestClass.class)
+        .inspect(this::inspectR8)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  public static final class TestClass {
+
+    @NeverInline
+    public static void m1() {
+      A[] array =
+          new A[] {
+            A.A00, A.A00, A.A00, A.A00, A.A00, A.A00, A.A00, A.A00, A.A00, A.A00,
+            A.A00, A.A00, A.A00, A.A00, A.A00, A.A00, A.A00, A.A00, A.A00, A.A00,
+            A.A00, A.A00, A.A00, A.A00, A.A00, A.A00, A.A00, A.A00, A.A00, A.A00,
+            A.A00, A.A00, A.A00, A.A00, A.A00, A.A00, A.A00, A.A00, A.A00, A.A00,
+            A.A00, A.A00, A.A00, A.A00, A.A00, A.A00, A.A00, A.A00, A.A00, A.A00,
+            A.A00, A.A00, A.A00, A.A00, A.A00, A.A00, A.A00, A.A00, A.A00, A.A00,
+            A.A00, A.A00, A.A00, A.A00, A.A00, A.A00, A.A00, A.A00, A.A00, A.A00,
+            A.A00, A.A00, A.A00, A.A00, A.A00, A.A00, A.A00, A.A00, A.A00, A.A00,
+            A.A00, A.A00, A.A00, A.A00, A.A00, A.A00, A.A00, A.A00, A.A00, A.A00,
+            A.A00, A.A00, A.A00, A.A00, A.A00, A.A00, A.A00, A.A00, A.A00, A.A00,
+          };
+      printArraySize(array);
+    }
+
+    @NeverInline
+    public static void m2() {
+      A[] array =
+          new A[] {
+            A.A00, A.A01, A.A02, A.A03, A.A04, A.A05, A.A06, A.A07, A.A08, A.A09,
+            A.A00, A.A01, A.A02, A.A03, A.A04, A.A05, A.A06, A.A07, A.A08, A.A09,
+            A.A00, A.A01, A.A02, A.A03, A.A04, A.A05, A.A06, A.A07, A.A08, A.A09,
+            A.A00, A.A01, A.A02, A.A03, A.A04, A.A05, A.A06, A.A07, A.A08, A.A09,
+            A.A00, A.A01, A.A02, A.A03, A.A04, A.A05, A.A06, A.A07, A.A08, A.A09,
+          };
+      printArraySize(array);
+    }
+
+    @NeverInline
+    public static void printArraySize(A[] array) {
+      System.out.println(Arrays.asList(array).size());
+    }
+
+    public static void main(String[] args) {
+      m1();
+      m2();
+    }
+  }
+
+  static class A {
+
+    private final String name;
+
+    private A(String name) {
+      this.name = name;
+    }
+
+    public String toString() {
+      return name;
+    }
+
+    static A A00 = new A("A00");
+    static A A01 = new A("A01");
+    static A A02 = new A("A02");
+    static A A03 = new A("A03");
+    static A A04 = new A("A04");
+    static A A05 = new A("A05");
+    static A A06 = new A("A06");
+    static A A07 = new A("A07");
+    static A A08 = new A("A08");
+    static A A09 = new A("A09");
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/arrays/StaticGetArrayWithUniqueValuesTest.java b/src/test/java/com/android/tools/r8/rewrite/arrays/StaticGetArrayWithUniqueValuesTest.java
new file mode 100644
index 0000000..b5fe270
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/arrays/StaticGetArrayWithUniqueValuesTest.java
@@ -0,0 +1,304 @@
+// 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.rewrite.arrays;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
+import java.util.Arrays;
+import java.util.Iterator;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class StaticGetArrayWithUniqueValuesTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameter(1)
+  public CompilationMode compilationMode;
+
+  @Parameter(2)
+  public Integer maxMaterializingConstants;
+
+  @Parameters(name = "{0}, mode = {1}, maxMaterializingConstants = {2}")
+  public static Iterable<?> data() {
+    return buildParameters(
+        getTestParameters().withDefaultCfRuntime().withDexRuntimesAndAllApiLevels().build(),
+        CompilationMode.values(),
+        ImmutableList.of(Constants.U8BIT_MAX - 16, 2));
+  }
+
+  private static final String EXPECTED_OUTPUT =
+      StringUtils.lines("[A00, A01, A02, A03, A04]", "[A00, A01, A02, A03, A04]", "100");
+
+  public boolean canUseFilledNewArrayOfObject(TestParameters parameters) {
+    return parameters.isDexRuntime()
+        && parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N);
+  }
+
+  private enum State {
+    EXPECTING_GETSTATIC,
+    EXPECTING_APUTOBJECT
+  }
+
+  private void inspect(MethodSubject method, int puts, boolean insideCatchHandler) {
+    boolean expectingFilledNewArray =
+        canUseFilledNewArrayOfObject(parameters) && !insideCatchHandler;
+    assertEquals(
+        expectingFilledNewArray ? 0 : puts,
+        method.streamInstructions().filter(InstructionSubject::isArrayPut).count());
+    assertEquals(
+        expectingFilledNewArray ? 1 : 0,
+        method.streamInstructions().filter(InstructionSubject::isFilledNewArray).count());
+    assertEquals(puts, method.streamInstructions().filter(InstructionSubject::isStaticGet).count());
+    if (!expectingFilledNewArray) {
+      // Test that sget and aput instructions are interleaved by the lowering of
+      // filled-new-array.
+      int aputCount = 0;
+      State state = State.EXPECTING_GETSTATIC;
+      Iterator<InstructionSubject> iterator = method.iterateInstructions();
+      while (iterator.hasNext()) {
+        InstructionSubject instruction = iterator.next();
+        if (instruction.isStaticGet()) {
+          assertEquals(State.EXPECTING_GETSTATIC, state);
+          state = State.EXPECTING_APUTOBJECT;
+        } else if (instruction.isArrayPut()) {
+          assertEquals(State.EXPECTING_APUTOBJECT, state);
+          state = State.EXPECTING_GETSTATIC;
+          aputCount++;
+        }
+      }
+      assertEquals(State.EXPECTING_GETSTATIC, state);
+      assertEquals(puts, aputCount);
+    }
+  }
+
+  private void inspect(CodeInspector inspector) {
+    inspect(inspector.clazz(TestClass.class).uniqueMethodWithOriginalName("m1"), 5, false);
+    inspect(inspector.clazz(TestClass.class).uniqueMethodWithOriginalName("m2"), 5, true);
+    inspect(inspector.clazz(TestClass.class).uniqueMethodWithOriginalName("m3"), 100, false);
+  }
+
+  @Test
+  public void testD8() throws Exception {
+    parameters.assumeDexRuntime();
+    testForD8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .setMinApi(parameters)
+        .run(parameters.getRuntime(), TestClass.class)
+        .inspect(this::inspect)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(TestClass.class)
+        .setMinApi(parameters)
+        .enableInliningAnnotations()
+        .addDontObfuscate()
+        .run(parameters.getRuntime(), TestClass.class)
+        .inspect(this::inspect)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  public static final class TestClass {
+
+    @NeverInline
+    public static void m1() {
+      A[] array = {A.A00, A.A01, A.A02, A.A03, A.A04};
+      printArray(array);
+    }
+
+    @NeverInline
+    public static void m2() {
+      try {
+        A[] array = {A.A00, A.A01, A.A02, A.A03, A.A04};
+        printArray(array);
+      } catch (Exception e) {
+        throw new RuntimeException();
+      }
+    }
+
+    @NeverInline
+    public static void m3() {
+      A[] array =
+          new A[] {
+            A.A00, A.A01, A.A02, A.A03, A.A04, A.A05, A.A06, A.A07,
+            A.A08, A.A09, A.A10, A.A11, A.A12, A.A13, A.A14, A.A15,
+            A.A16, A.A17, A.A18, A.A19, A.A20, A.A21, A.A22, A.A23,
+            A.A24, A.A25, A.A26, A.A27, A.A28, A.A29, A.A30, A.A31,
+            A.A32, A.A33, A.A34, A.A35, A.A36, A.A37, A.A38, A.A39,
+            A.A40, A.A41, A.A42, A.A43, A.A44, A.A45, A.A46, A.A47,
+            A.A48, A.A49, A.A50, A.A51, A.A52, A.A53, A.A54, A.A55,
+            A.A56, A.A57, A.A58, A.A59, A.A60, A.A61, A.A62, A.A63,
+            A.A64, A.A65, A.A66, A.A67, A.A68, A.A69, A.A70, A.A71,
+            A.A72, A.A73, A.A74, A.A75, A.A76, A.A77, A.A78, A.A79,
+            A.A80, A.A81, A.A82, A.A83, A.A84, A.A85, A.A86, A.A87,
+            A.A88, A.A89, A.A90, A.A91, A.A92, A.A93, A.A94, A.A95,
+            A.A96, A.A97, A.A98, A.A99,
+          };
+      printArraySize(array);
+    }
+
+    @NeverInline
+    public static void printArray(A[] array) {
+      System.out.print("[");
+      for (A a : array) {
+        if (a != array[0]) {
+          System.out.print(", ");
+        }
+        System.out.print(a);
+      }
+      System.out.println("]");
+    }
+
+    @NeverInline
+    public static void printArraySize(A[] array) {
+      System.out.println(Arrays.asList(array).size());
+    }
+
+    public static void main(String[] args) {
+      m1();
+      m2();
+      m3();
+    }
+  }
+
+  static class A {
+    private final String name;
+
+    private A(String name) {
+      this.name = name;
+    }
+
+    public String toString() {
+      return name;
+    }
+
+    static A A00 = new A("A00");
+    static A A01 = new A("A01");
+    static A A02 = new A("A02");
+    static A A03 = new A("A03");
+    static A A04 = new A("A04");
+    static A A05 = new A("A05");
+    static A A06 = new A("A06");
+    static A A07 = new A("A07");
+    static A A08 = new A("A08");
+    static A A09 = new A("A09");
+    static A A10 = new A("A10");
+    static A A11 = new A("A11");
+    static A A12 = new A("A12");
+    static A A13 = new A("A13");
+    static A A14 = new A("A14");
+    static A A15 = new A("A15");
+    static A A16 = new A("A16");
+    static A A17 = new A("A17");
+    static A A18 = new A("A18");
+    static A A19 = new A("A19");
+    static A A20 = new A("A20");
+    static A A21 = new A("A21");
+    static A A22 = new A("A22");
+    static A A23 = new A("A23");
+    static A A24 = new A("A24");
+    static A A25 = new A("A25");
+    static A A26 = new A("A26");
+    static A A27 = new A("A27");
+    static A A28 = new A("A28");
+    static A A29 = new A("A29");
+    static A A30 = new A("A30");
+    static A A31 = new A("A31");
+    static A A32 = new A("A32");
+    static A A33 = new A("A33");
+    static A A34 = new A("A34");
+    static A A35 = new A("A35");
+    static A A36 = new A("A36");
+    static A A37 = new A("A37");
+    static A A38 = new A("A38");
+    static A A39 = new A("A39");
+    static A A40 = new A("A40");
+    static A A41 = new A("A41");
+    static A A42 = new A("A42");
+    static A A43 = new A("A43");
+    static A A44 = new A("A44");
+    static A A45 = new A("A45");
+    static A A46 = new A("A46");
+    static A A47 = new A("A47");
+    static A A48 = new A("A48");
+    static A A49 = new A("A49");
+    static A A50 = new A("A50");
+    static A A51 = new A("A51");
+    static A A52 = new A("A52");
+    static A A53 = new A("A53");
+    static A A54 = new A("A54");
+    static A A55 = new A("A55");
+    static A A56 = new A("A56");
+    static A A57 = new A("A57");
+    static A A58 = new A("A58");
+    static A A59 = new A("A59");
+    static A A60 = new A("A60");
+    static A A61 = new A("A61");
+    static A A62 = new A("A62");
+    static A A63 = new A("A63");
+    static A A64 = new A("A64");
+    static A A65 = new A("A65");
+    static A A66 = new A("A66");
+    static A A67 = new A("A67");
+    static A A68 = new A("A68");
+    static A A69 = new A("A69");
+    static A A70 = new A("A70");
+    static A A71 = new A("A71");
+    static A A72 = new A("A72");
+    static A A73 = new A("A73");
+    static A A74 = new A("A74");
+    static A A75 = new A("A75");
+    static A A76 = new A("A76");
+    static A A77 = new A("A77");
+    static A A78 = new A("A78");
+    static A A79 = new A("A79");
+    static A A80 = new A("A80");
+    static A A81 = new A("A81");
+    static A A82 = new A("A82");
+    static A A83 = new A("A83");
+    static A A84 = new A("A84");
+    static A A85 = new A("A85");
+    static A A86 = new A("A86");
+    static A A87 = new A("A87");
+    static A A88 = new A("A88");
+    static A A89 = new A("A89");
+    static A A90 = new A("A90");
+    static A A91 = new A("A91");
+    static A A92 = new A("A92");
+    static A A93 = new A("A93");
+    static A A94 = new A("A94");
+    static A A95 = new A("A95");
+    static A A96 = new A("A96");
+    static A A97 = new A("A97");
+    static A A98 = new A("A98");
+    static A A99 = new A("A99");
+  }
+
+  // public static void main(String[] args) {
+  //   for (int i = 0; i < 100; i++) {
+  //     System.out.println("    static A A" + i + " = new A(\"A" + i + "\")");
+  //   }
+  // }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/arrays/StringArrayWithNonUniqueValuesTest.java b/src/test/java/com/android/tools/r8/rewrite/arrays/StringArrayWithNonUniqueValuesTest.java
index 220e5dc..9100e39 100644
--- a/src/test/java/com/android/tools/r8/rewrite/arrays/StringArrayWithNonUniqueValuesTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/arrays/StringArrayWithNonUniqueValuesTest.java
@@ -9,12 +9,15 @@
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestCompilerBuilder;
 import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.InstructionSubject;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
 import java.util.Arrays;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -31,11 +34,15 @@
   @Parameter(1)
   public CompilationMode compilationMode;
 
-  @Parameters(name = "{0}, mode = {1}")
+  @Parameter(2)
+  public Integer maxMaterializingConstants;
+
+  @Parameters(name = "{0}, mode = {1}, maxMaterializingConstants = {2}")
   public static Iterable<?> data() {
     return buildParameters(
         getTestParameters().withDefaultCfRuntime().withDexRuntimesAndAllApiLevels().build(),
-        CompilationMode.values());
+        CompilationMode.values(),
+        ImmutableList.of(Constants.U8BIT_MAX - 16, 2));
   }
 
   private static final String EXPECTED_OUTPUT = StringUtils.lines("100", "104");
@@ -60,9 +67,19 @@
         method.streamInstructions().filter(InstructionSubject::isConstString).count());
   }
 
+  private void configure(TestCompilerBuilder<?, ?, ?, ?, ?> builder) {
+    builder.addOptionsModification(
+        options ->
+            options.rewriteArrayOptions().maxMaterializingConstants = maxMaterializingConstants);
+  }
+
   private void inspect(CodeInspector inspector) {
     inspect(inspector.clazz(TestClass.class).uniqueMethodWithOriginalName("m1"), 1, 100, false);
-    inspect(inspector.clazz(TestClass.class).uniqueMethodWithOriginalName("m2"), 26, 104, false);
+    inspect(
+        inspector.clazz(TestClass.class).uniqueMethodWithOriginalName("m2"),
+        maxMaterializingConstants == 2 ? 32 : 26,
+        104,
+        false);
   }
 
   @Test
@@ -71,6 +88,7 @@
     testForD8(parameters.getBackend())
         .addInnerClasses(getClass())
         .setMinApi(parameters)
+        .apply(this::configure)
         .run(parameters.getRuntime(), TestClass.class)
         .inspect(this::inspect)
         .assertSuccessWithOutput(EXPECTED_OUTPUT);
@@ -84,6 +102,7 @@
         .setMinApi(parameters)
         .enableInliningAnnotations()
         .addDontObfuscate()
+        .apply(this::configure)
         .run(parameters.getRuntime(), TestClass.class)
         .inspect(this::inspect)
         .assertSuccessWithOutput(EXPECTED_OUTPUT);
diff --git a/src/test/java/com/android/tools/r8/rewrite/arrays/StringArrayWithUniqueValuesTest.java b/src/test/java/com/android/tools/r8/rewrite/arrays/StringArrayWithUniqueValuesTest.java
index c3a70be..2613ca5 100644
--- a/src/test/java/com/android/tools/r8/rewrite/arrays/StringArrayWithUniqueValuesTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/arrays/StringArrayWithUniqueValuesTest.java
@@ -10,11 +10,13 @@
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.InstructionSubject;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
 import java.util.Arrays;
 import java.util.Iterator;
 import org.junit.Test;
@@ -32,11 +34,15 @@
   @Parameter(1)
   public CompilationMode compilationMode;
 
-  @Parameters(name = "{0}, mode = {1}")
+  @Parameter(2)
+  public Integer maxMaterializingConstants;
+
+  @Parameters(name = "{0}, mode = {1}, maxMaterializingConstants = {2}")
   public static Iterable<?> data() {
     return buildParameters(
         getTestParameters().withDefaultCfRuntime().withDexRuntimesAndAllApiLevels().build(),
-        CompilationMode.values());
+        CompilationMode.values(),
+        ImmutableList.of(Constants.U8BIT_MAX - 16, 2));
   }
 
   private static final String EXPECTED_OUTPUT =
diff --git a/src/test/java/com/android/tools/r8/rewrite/enums/EnumOptimizationTest.java b/src/test/java/com/android/tools/r8/rewrite/enums/EnumOptimizationTest.java
index 97045ca..7b1c258 100644
--- a/src/test/java/com/android/tools/r8/rewrite/enums/EnumOptimizationTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/enums/EnumOptimizationTest.java
@@ -161,7 +161,6 @@
         .addOptionsModification(this::configure)
         .addOptionsModification(
             o -> {
-              o.testing.enableLir();
               // Not inlining toString depends on simple inlining limit.
               o.inlinerOptions().simpleInliningInstructionLimit = 3;
             })
diff --git a/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java b/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java
index 1dba3c9..1afb1c3 100644
--- a/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java
@@ -52,10 +52,6 @@
     return Paths.get(EXAMPLES_BUILD_DIR, test + ".jar");
   }
 
-  private byte[] getProgramDexFileData(String test) throws IOException {
-    return Files.readAllBytes(Paths.get(EXAMPLES_BUILD_DIR, test, "classes.dex"));
-  }
-
   @Test
   public void testIgnoreWarnings() throws Exception {
     // Generate R8 processed version without library option.
diff --git a/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java b/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
index fb55a41..b573cd5 100644
--- a/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
@@ -156,7 +156,7 @@
       DiagnosticsConsumer diagnosticsConsumer)
       throws Exception {
 
-    String programFile = ToolHelper.TESTS_BUILD_DIR + getName() + ".jar";
+    String programFile = ToolHelper.THIRD_PARTY_DIR + getName() + ".jar";
 
     R8FullTestBuilder testBuilder =
         testForR8(parameters.getBackend())
diff --git a/src/test/java/com/android/tools/r8/shaking/annotations/b137392797/B137392797.java b/src/test/java/com/android/tools/r8/shaking/annotations/b137392797/B137392797.java
index df9580c..c084e8a 100644
--- a/src/test/java/com/android/tools/r8/shaking/annotations/b137392797/B137392797.java
+++ b/src/test/java/com/android/tools/r8/shaking/annotations/b137392797/B137392797.java
@@ -88,7 +88,6 @@
             options -> {
               // The default limit for LIR is 2 at time of writing.
               // The constructor inlining check needs a limit of 4 to trigger.
-              options.testing.enableLir();
               options.inlinerOptions().simpleInliningInstructionLimit = 4;
             })
         .compile()
diff --git a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
index 5c8821d..e6ba0f6 100644
--- a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
+++ b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
@@ -11,6 +11,7 @@
 
 import com.android.tools.r8.TestRuntime.CfVm;
 import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.TestDataSourceSet;
 import com.android.tools.r8.cf.CfVersion;
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.graph.AccessFlags;
@@ -155,6 +156,12 @@
     return create(ToolHelper.getClassAsBytes(clazz), classFromTypeName(clazz.getTypeName()));
   }
 
+  public static ClassFileTransformer create(Class<?> clazz, TestDataSourceSet sourceSet)
+      throws IOException {
+    return create(
+        ToolHelper.getClassAsBytes(clazz, sourceSet), classFromTypeName(clazz.getTypeName()));
+  }
+
   public <E extends Exception> ClassFileTransformer applyIf(
       boolean condition, ThrowingConsumer<ClassFileTransformer, E> consumer) throws E {
     if (condition) {
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 196cc8a..f514289 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 @@
-089140826fdd80b1b74c5cd9b38627383956963e
\ No newline at end of file
+1274833123c17ea92300f41af2c18bb1a20dcc01
\ No newline at end of file
diff --git a/third_party/dependencies.tar.gz.sha1 b/third_party/dependencies.tar.gz.sha1
index 139251d..cea55a6 100644
--- a/third_party/dependencies.tar.gz.sha1
+++ b/third_party/dependencies.tar.gz.sha1
@@ -1 +1 @@
-d004917502f1b50bedada4bc5cca457ceb237c87
\ No newline at end of file
+3bfa0f5611e2f0e4beed4287cb5f9f9d3458c61b
\ No newline at end of file
diff --git a/third_party/dependencies_new.tar.gz.sha1 b/third_party/dependencies_new.tar.gz.sha1
index 2d9e556..621dd6c 100644
--- a/third_party/dependencies_new.tar.gz.sha1
+++ b/third_party/dependencies_new.tar.gz.sha1
@@ -1 +1 @@
-e2dff12bf95f94797da37f77b274fe222aeda83a
\ No newline at end of file
+4fad35bd436967989be01124889a973aee24ffa1
\ No newline at end of file
diff --git a/third_party/examples.tar.gz.sha1 b/third_party/examples.tar.gz.sha1
new file mode 100644
index 0000000..7cc4607
--- /dev/null
+++ b/third_party/examples.tar.gz.sha1
@@ -0,0 +1 @@
+a64a830bad847a8bcb1b1d441b16876882951960
\ No newline at end of file
diff --git a/third_party/examplesAndroidN.tar.gz.sha1 b/third_party/examplesAndroidN.tar.gz.sha1
new file mode 100644
index 0000000..c7c48ab
--- /dev/null
+++ b/third_party/examplesAndroidN.tar.gz.sha1
@@ -0,0 +1 @@
+3b4abfc288b97b2f592de1528162ffdfa4db76c2
\ No newline at end of file
diff --git a/third_party/examplesAndroidO.tar.gz.sha1 b/third_party/examplesAndroidO.tar.gz.sha1
new file mode 100644
index 0000000..7d28b20
--- /dev/null
+++ b/third_party/examplesAndroidO.tar.gz.sha1
@@ -0,0 +1 @@
+05367865efe55fcbf75c782deddab15ae7c40dad
\ No newline at end of file
diff --git a/third_party/examplesAndroidOGenerated.tar.gz.sha1 b/third_party/examplesAndroidOGenerated.tar.gz.sha1
new file mode 100644
index 0000000..832286b
--- /dev/null
+++ b/third_party/examplesAndroidOGenerated.tar.gz.sha1
@@ -0,0 +1 @@
+ea9220ee4f864517540f693ab0fd371a24607bf2
\ No newline at end of file
diff --git a/third_party/examplesAndroidP.tar.gz.sha1 b/third_party/examplesAndroidP.tar.gz.sha1
new file mode 100644
index 0000000..8b2db07
--- /dev/null
+++ b/third_party/examplesAndroidP.tar.gz.sha1
@@ -0,0 +1 @@
+591f8ee9f40e7a433e951908a22367d2001ff80e
\ No newline at end of file
diff --git a/third_party/examplesAndroidPGenerated.tar.gz.sha1 b/third_party/examplesAndroidPGenerated.tar.gz.sha1
new file mode 100644
index 0000000..28762a8
--- /dev/null
+++ b/third_party/examplesAndroidPGenerated.tar.gz.sha1
@@ -0,0 +1 @@
+8c0a039c70b4b335a017a818d286a9ace420a7ef
\ No newline at end of file
diff --git a/third_party/kotlinR8TestResources.tar.gz.sha1 b/third_party/kotlinR8TestResources.tar.gz.sha1
new file mode 100644
index 0000000..44d30c5
--- /dev/null
+++ b/third_party/kotlinR8TestResources.tar.gz.sha1
@@ -0,0 +1 @@
+4d5ea5f132e98018c5e3ef73ce478e0745d7f998
\ No newline at end of file
diff --git a/third_party/retrace/binary_compatibility.tar.gz.sha1 b/third_party/retrace/binary_compatibility.tar.gz.sha1
index 41a5e04..f1b451d 100644
--- a/third_party/retrace/binary_compatibility.tar.gz.sha1
+++ b/third_party/retrace/binary_compatibility.tar.gz.sha1
@@ -1 +1 @@
-c200df1f5305635d3b26f948a15732af5fefcacf
\ No newline at end of file
+cce7a600b543fcbe53dfeb84e834d389ec9c5421
\ No newline at end of file
diff --git a/tools/archive.py b/tools/archive.py
index 3cebc4a..8b136ec 100755
--- a/tools/archive.py
+++ b/tools/archive.py
@@ -74,27 +74,34 @@
 def GetStorageDestination(storage_prefix,
                           version_or_path,
                           file_name,
-                          is_main):
+                          is_main,
+                          new_gradle=False):
   # We archive main commits under raw/main instead of directly under raw
   version_dir = GetVersionDestination(storage_prefix,
                                       version_or_path,
-                                      is_main)
+                                      is_main,
+                                      new_gradle)
   return '%s/%s' % (version_dir, file_name)
 
-def GetVersionDestination(storage_prefix, version_or_path, is_main):
+def GetVersionDestination(storage_prefix, version_or_path, is_main,
+                          new_gradle=False):
   archive_dir = 'raw/main' if is_main else 'raw'
-  return '%s%s/%s/%s' % (storage_prefix, ARCHIVE_BUCKET,
+  bucket = ARCHIVE_BUCKET if not new_gradle else "r8-test-results"
+  return '%s%s/%s/%s' % (storage_prefix, bucket,
                          archive_dir, version_or_path)
 
-def GetUploadDestination(version_or_path, file_name, is_main):
-  return GetStorageDestination('gs://', version_or_path, file_name, is_main)
+def GetUploadDestination(version_or_path, file_name, is_main,
+                         new_gradle=False):
+  return GetStorageDestination('gs://', version_or_path, file_name, is_main,
+                               new_gradle)
 
-def GetUrl(version_or_path, file_name, is_main):
+def GetUrl(version_or_path, file_name, is_main, new_gradle=False):
   return GetStorageDestination('https://storage.googleapis.com/',
-                               version_or_path, file_name, is_main)
+                               version_or_path, file_name, is_main, new_gradle)
 
-def GetMavenUrl(is_main):
-  return GetVersionDestination('https://storage.googleapis.com/', '', is_main)
+def GetMavenUrl(is_main, new_gradle=False):
+  return GetVersionDestination('https://storage.googleapis.com/', '', is_main,
+                               new_gradle)
 
 def SetRLimitToMax():
   (soft, hard) = resource.getrlimit(resource.RLIMIT_NOFILE)
@@ -105,8 +112,15 @@
   print('INFO: Open files soft limit: %s' % soft)
   print('INFO: Open files hard limit: %s' % hard)
 
+
 def Main():
   (options, args) = ParseOptions()
+  Run(options, False)
+  # Clean to ensure that we actually generate all of the artifacts
+  shutil.rmtree(utils.BUILD)
+  Run(options, True)
+
+def Run(options, new_gradle):
   if not utils.is_bot() and not options.dry_run:
     raise Exception('You are not a bot, don\'t archive builds. '
       + 'Use --dry-run to test locally')
@@ -124,7 +138,6 @@
     PrintResourceInfo()
 
   with utils.TempDir() as temp:
-
     version_file = os.path.join(temp, 'r8-version.properties')
     with open(version_file,'w') as version_writer:
       version_writer.write('version.sha=' + GetGitHash() + '\n')
@@ -134,41 +147,56 @@
       releaser = \
           ("<local developer build>" if options.dry_run
             else 'releaser=go/r8bot ('
-                + os.environ.get('SWARMING_BOT_ID') + ')\n')
+                + (os.environ.get('SWARMING_BOT_ID') or 'foo') + ')\n')
       version_writer.write(releaser)
       version_writer.write('version-file.version.code=1\n')
 
-    # Create maven release which uses a build that exclude dependencies.
-    create_maven_release.generate_r8_maven_zip(
-        utils.MAVEN_ZIP,
-        version_file=version_file,
-        skip_gradle_build=options.skip_gradle_build)
     create_maven_release.generate_r8_maven_zip(
         utils.MAVEN_ZIP_LIB,
-        is_r8lib=True,
         version_file=version_file,
-        skip_gradle_build=options.skip_gradle_build)
+        skip_gradle_build=options.skip_gradle_build,
+        new_gradle=new_gradle)
+
 
     # Generate and copy a full build without dependencies.
     if (not options.skip_gradle_build):
-      gradle.RunGradleExcludeDeps([utils.R8, utils.R8_SRC])
-    shutil.copyfile(utils.R8_JAR, utils.R8_FULL_EXCLUDE_DEPS_JAR)
+      if (new_gradle):
+        gradle.RunGradle([':main:swissArmyKnife'], new_gradle=True)
+      else:
+        gradle.RunGradleExcludeDeps([utils.R8, utils.R8_SRC])
+    if (not new_gradle):
+      shutil.copyfile(utils.R8_JAR, utils.R8_FULL_EXCLUDE_DEPS_JAR)
 
     # Ensure all archived artifacts has been built before archiving.
     # The target tasks postfixed by 'lib' depend on the actual target task so
     # building it invokes the original task first.
     # The '-Pno_internal' flag is important because we generate the lib based on uses in tests.
     if (not options.skip_gradle_build):
-      gradle.RunGradle([
-          utils.R8,
-          utils.R8LIB,
-          utils.R8LIB_NO_DEPS,
-          utils.R8RETRACE,
-          utils.R8RETRACE_NO_DEPS,
-          utils.LIBRARY_DESUGAR_CONVERSIONS,
-          utils.KEEPANNO_ANNOTATIONS_TARGET,
-          '-Pno_internal'
-      ])
+      if (new_gradle):
+        gradle.RunGradle([
+            ':keepanno:keepAnnoJar',
+            ':main:consolidatedLicense',
+            ':main:r8WithRelocatedDeps',
+            ':main:swissArmyKnife',
+            ':test:r8LibNoDeps',
+            ':test:r8LibWithRelocatedDeps',
+            ':test:retraceNoDeps',
+            ':test:retraceWithRelocatedDeps',
+            ':test:sourcesJar',
+            ':test:sourcesJar',
+            '-Pno_internal'
+        ], new_gradle=True)
+      else:
+        gradle.RunGradle([
+            utils.R8,
+            utils.R8LIB,
+            utils.R8LIB_NO_DEPS,
+            utils.R8RETRACE,
+            utils.R8RETRACE_NO_DEPS,
+            utils.LIBRARY_DESUGAR_CONVERSIONS,
+            utils.KEEPANNO_ANNOTATIONS_TARGET,
+            '-Pno_internal'
+        ])
 
     # Create maven release of the desuage_jdk_libs configuration. This require
     # an r8.jar with dependencies to have been built.
@@ -206,49 +234,48 @@
       print('On main, using git hash for archiving')
       version = GetGitHash()
 
-    destination = GetVersionDestination('gs://', version, is_main)
+    destination = GetVersionDestination('gs://', version, is_main, new_gradle)
     if utils.cloud_storage_exists(destination) and not options.dry_run:
       raise Exception('Target archive directory %s already exists' % destination)
 
     # Create pom file for our maven repository that we build for testing.
     default_pom_file = os.path.join(temp, 'r8.pom')
     create_maven_release.write_default_r8_pom_file(default_pom_file, version)
-
-    for file in [
-      utils.R8_JAR,
-      utils.R8LIB_JAR,
-      utils.R8LIB_JAR + '.map',
-      utils.R8LIB_JAR + '_map.zip',
-      utils.R8_SRC_JAR,
-      utils.R8_FULL_EXCLUDE_DEPS_JAR,
-      utils.R8LIB_EXCLUDE_DEPS_JAR,
-      utils.R8LIB_EXCLUDE_DEPS_JAR + '.map',
-      utils.R8LIB_EXCLUDE_DEPS_JAR + '_map.zip',
-      utils.R8RETRACE_JAR,
-      utils.R8RETRACE_JAR + '.map',
-      utils.R8RETRACE_JAR + '_map.zip',
-      utils.R8RETRACE_EXCLUDE_DEPS_JAR,
-      utils.R8RETRACE_EXCLUDE_DEPS_JAR + '.map',
-      utils.R8RETRACE_EXCLUDE_DEPS_JAR + '_map.zip',
-      utils.MAVEN_ZIP,
-      utils.MAVEN_ZIP_LIB,
-      utils.DESUGAR_CONFIGURATION,
-      utils.DESUGAR_CONFIGURATION_MAVEN_ZIP,
-      utils.DESUGAR_CONFIGURATION_JDK11_LEGACY,
-      utils.DESUGAR_CONFIGURATION_JDK11_LEGACY_MAVEN_ZIP,
-      utils.DESUGAR_CONFIGURATION_JDK11_MINIMAL_MAVEN_ZIP,
-      utils.DESUGAR_CONFIGURATION_JDK11_MAVEN_ZIP,
-      utils.DESUGAR_CONFIGURATION_JDK11_NIO_MAVEN_ZIP,
-      utils.KEEPANNO_ANNOTATIONS_JAR,
-      utils.GENERATED_LICENSE,
-    ]:
+    for_archiving = [
+        utils.R8_JAR,
+        utils.R8LIB_JAR,
+        utils.R8LIB_JAR + '.map',
+        utils.R8LIB_JAR + '_map.zip',
+        utils.R8_FULL_EXCLUDE_DEPS_JAR,
+        utils.R8LIB_EXCLUDE_DEPS_JAR,
+        utils.R8LIB_EXCLUDE_DEPS_JAR + '.map',
+        utils.R8LIB_EXCLUDE_DEPS_JAR + '_map.zip',
+        utils.MAVEN_ZIP_LIB,
+        utils.DESUGAR_CONFIGURATION,
+        utils.DESUGAR_CONFIGURATION_MAVEN_ZIP,
+        utils.DESUGAR_CONFIGURATION_JDK11_LEGACY,
+        utils.DESUGAR_CONFIGURATION_JDK11_LEGACY_MAVEN_ZIP,
+        utils.DESUGAR_CONFIGURATION_JDK11_MINIMAL_MAVEN_ZIP,
+        utils.DESUGAR_CONFIGURATION_JDK11_MAVEN_ZIP,
+        utils.DESUGAR_CONFIGURATION_JDK11_NIO_MAVEN_ZIP,
+        utils.R8_SRC_JAR,
+        utils.R8RETRACE_JAR,
+        utils.R8RETRACE_JAR + '.map',
+        utils.R8RETRACE_JAR + '_map.zip',
+        utils.R8RETRACE_EXCLUDE_DEPS_JAR,
+        utils.R8RETRACE_EXCLUDE_DEPS_JAR + '.map',
+        utils.R8RETRACE_EXCLUDE_DEPS_JAR + '_map.zip',
+        utils.KEEPANNO_ANNOTATIONS_JAR,
+        utils.GENERATED_LICENSE]
+    for file in for_archiving:
       file_name = os.path.basename(file)
       tagged_jar = os.path.join(temp, file_name)
       shutil.copyfile(file, tagged_jar)
       if file_name.endswith('.jar') and not file_name.endswith('-src.jar'):
         with zipfile.ZipFile(tagged_jar, 'a') as zip:
           zip.write(version_file, os.path.basename(version_file))
-      destination = GetUploadDestination(version, file_name, is_main)
+      destination = GetUploadDestination(version, file_name, is_main,
+                                         new_gradle=new_gradle)
       print('Uploading %s to %s' % (tagged_jar, destination))
       if options.dry_run:
         if options.dry_run_output:
@@ -260,21 +287,24 @@
           print('Dry run, not actually uploading')
       else:
         utils.upload_file_to_cloud_storage(tagged_jar, destination)
-        print('File available at: %s' % GetUrl(version, file_name, is_main))
+        print('File available at: %s' % GetUrl(version, file_name, is_main,
+                                               new_gradle=new_gradle))
 
       # Upload R8 to a maven compatible location.
       if file == utils.R8_JAR:
         maven_dst = GetUploadDestination(utils.get_maven_path('r8', version),
-                                         'r8-%s.jar' % version, is_main)
+                                         'r8-%s.jar' % version, is_main,
+                                         new_gradle=new_gradle)
         maven_pom_dst = GetUploadDestination(
             utils.get_maven_path('r8', version),
-            'r8-%s.pom' % version, is_main)
+            'r8-%s.pom' % version, is_main, new_gradle=new_gradle)
         if options.dry_run:
           print('Dry run, not actually creating maven repo for R8')
         else:
           utils.upload_file_to_cloud_storage(tagged_jar, maven_dst)
           utils.upload_file_to_cloud_storage(default_pom_file, maven_pom_dst)
-          print('Maven repo root available at: %s' % GetMavenUrl(is_main))
+          print('Maven repo root available at: %s' % GetMavenUrl(
+              is_main, new_gradle=new_gradle))
 
       # Upload desugar_jdk_libs configuration to a maven compatible location.
       if file == utils.DESUGAR_CONFIGURATION:
@@ -282,7 +312,8 @@
         jar_version_name = 'desugar_jdk_libs_configuration-%s.jar' % version
         maven_dst = GetUploadDestination(
             utils.get_maven_path('desugar_jdk_libs_configuration', version),
-                                 jar_version_name, is_main)
+                                 jar_version_name, is_main,
+            new_gradle=new_gradle)
 
         with utils.TempDir() as tmp_dir:
           desugar_jdk_libs_configuration_jar = os.path.join(tmp_dir,
@@ -303,10 +334,11 @@
           else:
             utils.upload_file_to_cloud_storage(
                 desugar_jdk_libs_configuration_jar, maven_dst)
-            print('Maven repo root available at: %s' % GetMavenUrl(is_main))
+            print('Maven repo root available at: %s' % GetMavenUrl(is_main,
+                                                                   new_gradle=new_gradle))
             # Also archive the jar as non maven destination for Google3
             jar_destination = GetUploadDestination(
-                version, jar_basename, is_main)
+                version, jar_basename, is_main, new_gradle=new_gradle)
             utils.upload_file_to_cloud_storage(
                 desugar_jdk_libs_configuration_jar, jar_destination)
 
@@ -317,7 +349,8 @@
         jar_version_name = 'desugar_jdk_libs_configuration-%s-jdk11-legacy.jar' % version
         maven_dst = GetUploadDestination(
             utils.get_maven_path('desugar_jdk_libs_configuration', version),
-                                 jar_version_name, is_main)
+            jar_version_name, is_main,
+            new_gradle=new_gradle)
 
         with utils.TempDir() as tmp_dir:
           desugar_jdk_libs_configuration_jar = os.path.join(tmp_dir,
@@ -338,10 +371,11 @@
           else:
             utils.upload_file_to_cloud_storage(
                 desugar_jdk_libs_configuration_jar, maven_dst)
-            print('Maven repo root available at: %s' % GetMavenUrl(is_main))
+            print('Maven repo root available at: %s' % GetMavenUrl(
+                is_main, new_gradle=new_gradle))
             # Also archive the jar as non maven destination for Google3
             jar_destination = GetUploadDestination(
-                version, jar_basename, is_main)
+                version, jar_basename, is_main, new_gradle=new_gradle)
             utils.upload_file_to_cloud_storage(
                 desugar_jdk_libs_configuration_jar, jar_destination)
 
diff --git a/tools/build_sample_apk.py b/tools/build_sample_apk.py
index 67ead8c..2cc95aa 100755
--- a/tools/build_sample_apk.py
+++ b/tools/build_sample_apk.py
@@ -244,10 +244,10 @@
       time = split[1]
       name = split[0].split()[-1]
       overall_total += int(time)
-      print '%s: %s' % (name, time)
+      print('%s: %s' % (name, time))
       results[name] = time
 
-  print 'Total: %s' % overall_total
+  print('Total: %s' % overall_total)
   if not output:
     return overall_total
   results['total'] = str(overall_total)
@@ -260,7 +260,7 @@
     with open(total_file, 'w') as f:
       f.write(time)
 
-  print 'Result stored in: \n%s' % ('\n'.join(written_files))
+  print('Result stored in: \n%s' % ('\n'.join(written_files)))
   return overall_total
 
 def benchmark(app, output_dir):
@@ -331,7 +331,7 @@
     ensure_no_logcat()
     for _ in range(BENCHMARK_ITERATIONS):
       grand_total += benchmark(options.app, options.benchmark_output_dir)
-  print 'Combined average: %s' % (grand_total/BENCHMARK_ITERATIONS)
+  print('Combined average: %s' % (grand_total/BENCHMARK_ITERATIONS))
 
 if __name__ == '__main__':
   sys.exit(Main())
diff --git a/tools/create_local_maven_with_dependencies.py b/tools/create_local_maven_with_dependencies.py
index 500c480..f9a0383 100755
--- a/tools/create_local_maven_with_dependencies.py
+++ b/tools/create_local_maven_with_dependencies.py
@@ -11,10 +11,13 @@
 
 import utils
 
+# The local_maven_repository_generator orderes the repositories by name, so
+# prefix with X- to control the order, as many dependencies are present
+# in several repositories.
 REPOSITORIES = [
-  'Maven Central=https://repo1.maven.org/maven2/',
-  'Google=https://maven.google.com/',
-  "Gradle Plugins=https://plugins.gradle.org/m2/",
+  'A-Google=https://maven.google.com/',
+  'B-Maven Central=https://repo1.maven.org/maven2/',
+  "C-Gradle Plugins=https://plugins.gradle.org/m2/",
 ]
 
 ANDRDID_SUPPORT_VERSION = '25.4.0'
diff --git a/tools/create_maven_release.py b/tools/create_maven_release.py
index 1ca239e..137f584 100755
--- a/tools/create_maven_release.py
+++ b/tools/create_maven_release.py
@@ -18,14 +18,6 @@
 import utils
 import zipfile
 
-DEPENDENCYTEMPLATE = Template(
-"""
-    <dependency>
-        <groupId>$group</groupId>
-        <artifactId>$artifact</artifactId>
-        <version>$version</version>
-    </dependency>""")
-
 LICENSETEMPLATE = Template(
 """
     <license>
@@ -56,7 +48,7 @@
       <distribution>repo</distribution>
     </license>$library_licenses
   </licenses>
-  <dependencies>$dependencies
+  <dependencies>
   </dependencies>
   <developers>
     <developer>
@@ -116,8 +108,6 @@
   result = argparse.ArgumentParser()
   result.add_argument('--out', help='The zip file to output')
   group = result.add_mutually_exclusive_group()
-  group.add_argument('--r8lib', action='store_true',
-                      help='Build r8 with dependencies included shrunken')
   group.add_argument('--desugar-configuration', action='store_true',
                       help='Build desugar library configuration (original JDK-8)')
   group.add_argument('--desugar-configuration-jdk8', action='store_true',
@@ -199,86 +189,19 @@
     result += LICENSETEMPLATE.substitute(name=name, url=url)
   return result
 
-
-# Generate the dependencies block for the pom file.
-#
-# We ask gradle to list all dependencies. In that output
-# we locate the runtimeClasspath block for 'main' which
-# looks something like:
-#
-# runtimeClasspath - Runtime classpath of source set 'main'.
-# +--- net.sf.jopt-simple:jopt-simple:4.6
-# +--- com.googlecode.json-simple:json-simple:1.1
-# +--- com.google.guava:guava:23.0
-# +--- it.unimi.dsi:fastutil:7.2.0
-# +--- org.ow2.asm:asm:6.0
-# +--- org.ow2.asm:asm-commons:6.0
-# |    \--- org.ow2.asm:asm-tree:6.0
-# |         \--- org.ow2.asm:asm:6.0
-# +--- org.ow2.asm:asm-tree:6.0 (*)
-# +--- org.ow2.asm:asm-analysis:6.0
-# |    \--- org.ow2.asm:asm-tree:6.0 (*)
-# \--- org.ow2.asm:asm-util:6.0
-#      \--- org.ow2.asm:asm-tree:6.0 (*)
-#
-# We filter out the repeats that are marked by '(*)'.
-#
-# For each remaining line, we remove the junk at the start
-# in chunks. As an example:
-#
-# '  |    \--- org.ow2.asm:asm-tree:6.0  '  --strip-->
-# '|    \--- org.ow2.asm:asm-tree:6.0'  -->
-# '\--- org.ow2.asm:asm-tree:6.0'  -->
-# 'org.ow2.asm:asm-tree:6.0'
-#
-# The end result is the dependency we are looking for:
-#
-# groupId: org.ow2.asm
-# artifact: asm-tree
-# version: 6.0
-def generate_dependencies():
-  dependencies = gradle.RunGradleGetOutput(['dependencies'])
-  dependency_lines = []
-  collect = False
-  for line in dependencies.splitlines():
-    if line and 'runtimeClasspath' in line and "'main'" in line:
-      collect = True
-      continue
-    if collect:
-      if not len(line) == 0:
-        if not '(*)' in line:
-          trimmed = line.strip()
-          while trimmed.find(' ') != -1:
-            trimmed = trimmed[trimmed.find(' ') + 1:].strip()
-          if not trimmed in dependency_lines:
-            dependency_lines.append(trimmed)
-      else:
-        break
-  result = ''
-  for dep in dependency_lines:
-    components = dep.split(':')
-    assert len(components) == 3
-    group = components[0]
-    artifact = components[1]
-    version = components[2]
-    result += DEPENDENCYTEMPLATE.substitute(
-        group=group, artifact=artifact, version=version)
-  return result
-
 def write_default_r8_pom_file(pom_file, version):
-  write_pom_file(R8_POMTEMPLATE, pom_file, version, dependencies=generate_dependencies())
+  write_pom_file(R8_POMTEMPLATE, pom_file, version)
 
 def write_pom_file(
-    template, pom_file, version, artifact_id=None, dependencies='', library_licenses=''):
+    template, pom_file, version, artifact_id=None, library_licenses=''):
   version_pom = (
       template.substitute(
           artifactId=artifact_id,
           version=version,
-          dependencies=dependencies,
           library_licenses=library_licenses)
     if artifact_id else
       template.substitute(
-          version=version, dependencies=dependencies, library_licenses=library_licenses))
+          version=version, library_licenses=library_licenses))
   with open(pom_file, 'w') as file:
     file.write(version_pom)
 
@@ -323,18 +246,19 @@
     base_no_zip = out[0:len(out)-4]
     make_archive(base_no_zip, 'zip', tmp_dir)
 
-def generate_r8_maven_zip(out, is_r8lib=False, version_file=None, skip_gradle_build=False):
-  # Build the R8 no deps artifact.
+def generate_r8_maven_zip(out, version_file=None, skip_gradle_build=False,
+                          new_gradle=False):
   if not skip_gradle_build:
-    if not is_r8lib:
-      gradle.RunGradleExcludeDeps([utils.R8])
+    if (new_gradle):
+      gradle.RunGradle([':test:r8LibWithRelocatedDeps',
+                        '-Pno_internal'], new_gradle=True)
     else:
       gradle.RunGradle([utils.R8LIB, '-Pno_internal'])
 
   version = determine_version()
   with utils.TempDir() as tmp_dir:
     file_copy = join(tmp_dir, 'copy_of_jar.jar')
-    copyfile(utils.R8LIB_JAR if is_r8lib else utils.R8_JAR, file_copy)
+    copyfile(utils.R8LIB_JAR, file_copy)
 
     if version_file:
       with zipfile.ZipFile(file_copy, 'a') as zip:
@@ -346,8 +270,7 @@
         R8_POMTEMPLATE,
         pom_file,
         version,
-        dependencies='' if is_r8lib else generate_dependencies(),
-        library_licenses=generate_library_licenses() if is_r8lib else '')
+        library_licenses=generate_library_licenses())
     # Write the maven zip file.
     generate_maven_zip(
         'r8',
@@ -473,7 +396,7 @@
       utils.DESUGAR_IMPLEMENTATION_JDK11,
       utils.LIBRARY_DESUGAR_CONVERSIONS_ZIP)
   else:
-    generate_r8_maven_zip(options.out, options.r8lib)
+    generate_r8_maven_zip(options.out)
 
 if __name__ == "__main__":
   exit(main(sys.argv[1:]))
diff --git a/tools/create_r8lib.py b/tools/create_r8lib.py
index a27cf24..a778ab3 100755
--- a/tools/create_r8lib.py
+++ b/tools/create_r8lib.py
@@ -37,6 +37,11 @@
     default=False,
     help='Mark this artifact as an "excldeps" variant of the compiler')
   parser.add_argument(
+    '--debug-variant',
+    action='store_true',
+    default=False,
+    help='Compile with debug flag')
+  parser.add_argument(
     '--lib',
     action='append',
     help='Additional libraries (JDK 1.8 rt.jar already included)')
@@ -49,6 +54,10 @@
     action='append',
     help='Keep configuration')
   parser.add_argument(
+    '--pg-map',
+    default=None,
+    help='Input map for distribution and composition')
+  parser.add_argument(
     '--r8jar',
     required=True,
     help='The R8 jar to compile')
@@ -95,6 +104,8 @@
     cmd.extend(['-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:5005'])
   cmd.extend(['-cp', args.r8compiler, 'com.android.tools.r8.R8'])
   cmd.append(args.r8jar)
+  if args.debug_variant:
+    cmd.append('--debug')
   cmd.append('--classfile')
   cmd.extend(['--map-id-template', map_id_template])
   cmd.extend(['--source-file-template', source_file_template])
@@ -111,6 +122,8 @@
   if args.classpath:
     for cp in args.classpath:
       cmd.extend(['--classpath', cp])
+  if args.pg_map:
+    cmd.extend(['--pg-map', args.pg_map])
   print(' '.join(cmd))
   subprocess.check_call(cmd)
 
diff --git a/tools/linux/art-10.0.0.tar.gz.sha1 b/tools/linux/art-10.0.0.tar.gz.sha1
index d64a0d7..5a5883f 100644
--- a/tools/linux/art-10.0.0.tar.gz.sha1
+++ b/tools/linux/art-10.0.0.tar.gz.sha1
@@ -1 +1 @@
-04316aaf3b05ebfe17a35ea5f47d900ac3c32d69
\ No newline at end of file
+913765ddb848e0dc4b9c125f185fe5bdd5eed28a
\ No newline at end of file
diff --git a/tools/linux/art-5.1.1.tar.gz.sha1 b/tools/linux/art-5.1.1.tar.gz.sha1
index d7f86b0..9b01a78 100644
--- a/tools/linux/art-5.1.1.tar.gz.sha1
+++ b/tools/linux/art-5.1.1.tar.gz.sha1
@@ -1 +1 @@
-7c1b10e333a5a028db7f74a989d9edfe168900bf
\ No newline at end of file
+70045aa5878d2f7f750b040d3d05b3acc2984bff
\ No newline at end of file
diff --git a/tools/linux/art-6.0.1.tar.gz.sha1 b/tools/linux/art-6.0.1.tar.gz.sha1
index 8e9cf7f..4e17ca1 100644
--- a/tools/linux/art-6.0.1.tar.gz.sha1
+++ b/tools/linux/art-6.0.1.tar.gz.sha1
@@ -1 +1 @@
-8d46aad0898be3291dffa3c2656a28ffbc155ad4
\ No newline at end of file
+26e3858dab7662047e6c42977be707265c55c87d
\ No newline at end of file
diff --git a/tools/linux/art-7.0.0.tar.gz.sha1 b/tools/linux/art-7.0.0.tar.gz.sha1
index faae6a3..6133be7 100644
--- a/tools/linux/art-7.0.0.tar.gz.sha1
+++ b/tools/linux/art-7.0.0.tar.gz.sha1
@@ -1 +1 @@
-c044d5c8441d8b67e60febce580c252ac7bdde0f
\ No newline at end of file
+67b78c98dc13891f269f33244eb495c6e6f7e5e7
\ No newline at end of file
diff --git a/tools/linux/art-8.1.0.tar.gz.sha1 b/tools/linux/art-8.1.0.tar.gz.sha1
index 91aea2f..a63bd11 100644
--- a/tools/linux/art-8.1.0.tar.gz.sha1
+++ b/tools/linux/art-8.1.0.tar.gz.sha1
@@ -1 +1 @@
-0394edc4e2dbe9e5b04a81efede7578ecf20b853
\ No newline at end of file
+cb3c600221c9d8f753828851cfc855a76b2d0808
\ No newline at end of file
diff --git a/tools/linux/art-9.0.0.tar.gz.sha1 b/tools/linux/art-9.0.0.tar.gz.sha1
index 788e039..15cdcf3 100644
--- a/tools/linux/art-9.0.0.tar.gz.sha1
+++ b/tools/linux/art-9.0.0.tar.gz.sha1
@@ -1 +1 @@
-bfd3d6f4c7d7245d93eecdcc386b41299266e2d1
\ No newline at end of file
+d78bee0e43c83c93cce0776ced2be521e671394c
\ No newline at end of file
diff --git a/tools/linux/art.tar.gz.sha1 b/tools/linux/art.tar.gz.sha1
index 98bc420..a3fe049 100644
--- a/tools/linux/art.tar.gz.sha1
+++ b/tools/linux/art.tar.gz.sha1
@@ -1 +1 @@
-1b4c4528315324c023e888ee5c53fc57edb3d5bf
\ No newline at end of file
+9be36809c39980b104d045e253014be858cf5819
\ No newline at end of file
diff --git a/tools/linux/host/art-12.0.0-beta4.tar.gz.sha1 b/tools/linux/host/art-12.0.0-beta4.tar.gz.sha1
index 9f80774..bd5eea4 100644
--- a/tools/linux/host/art-12.0.0-beta4.tar.gz.sha1
+++ b/tools/linux/host/art-12.0.0-beta4.tar.gz.sha1
@@ -1 +1 @@
-df7267e9eff9cc1812b5a7b4111e7175d5e186e9
\ No newline at end of file
+969c9fb7571ecf198aacbff529c3d3ba352743ca
\ No newline at end of file
diff --git a/tools/linux/host/art-13.0.0.tar.gz.sha1 b/tools/linux/host/art-13.0.0.tar.gz.sha1
index 8ec439c..008bf5b 100644
--- a/tools/linux/host/art-13.0.0.tar.gz.sha1
+++ b/tools/linux/host/art-13.0.0.tar.gz.sha1
@@ -1 +1 @@
-241ecc532b3bf804ef92bdb568ce7d4d85248434
\ No newline at end of file
+2002103b43d57ad52c1e9101c7b1db8e1ba85038
\ No newline at end of file
diff --git a/tools/linux/host/art-14.0.0-beta3.tar.gz.sha1 b/tools/linux/host/art-14.0.0-beta3.tar.gz.sha1
index 42f9f43..2568d10 100644
--- a/tools/linux/host/art-14.0.0-beta3.tar.gz.sha1
+++ b/tools/linux/host/art-14.0.0-beta3.tar.gz.sha1
@@ -1 +1 @@
-c879ddf6410159a4ac7e673fd66a64abe2140ffa
\ No newline at end of file
+599d516cd106605931791d37b862a7404824d44e
\ No newline at end of file
diff --git a/tools/linux/host/art-master.tar.gz.sha1 b/tools/linux/host/art-master.tar.gz.sha1
index 096aa8c..f79fc13 100644
--- a/tools/linux/host/art-master.tar.gz.sha1
+++ b/tools/linux/host/art-master.tar.gz.sha1
@@ -1 +1 @@
-7c795b752ba1edce6c3e9e11d837d31eb78043a3
\ No newline at end of file
+c8f7b9bc4de8b3f019281b656a3bff344341c21f
\ No newline at end of file
diff --git a/tools/r8.py b/tools/r8.py
index 31abcf6..6789787 100755
--- a/tools/r8.py
+++ b/tools/r8.py
@@ -3,8 +3,83 @@
 # 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 optparse
 import sys
+
 import toolhelper
+import utils
+
+def ParseOptions(argv):
+  parser = optparse.OptionParser(usage='%prog [options] -- [R8 options]')
+  parser.add_option(
+      '-c',
+      '--commit-hash',
+      '--commit_hash',
+      help='Commit hash of R8 to use.',
+      default=None)
+  parser.add_argument(
+      '--debug-agent',
+      help='Enable Java debug agent and suspend compilation (default disabled)',
+      default=False,
+      action='store_true')
+  parser.add_option(
+    '--ea',
+    help='Enable Java assertions when running the compiler (default disabled)',
+    default=False,
+    action='store_true')
+  parser.add_option(
+    '--lib-android',
+    help='Add the android.jar for the given API level',
+    default=None,
+    type=int)
+  parser.add_option(
+    '--lib-rt',
+    help='Add rt.jar from openjdk-1.8',
+    default=False,
+    action='store_true')
+  parser.add_option(
+    '--no-build', '--no_build',
+    help='Do not build R8',
+    default=False,
+    action='store_true')
+  parser.add_option(
+    '--print-runtimeraw', '--print_runtimeraw',
+    metavar='BENCHMARKNAME',
+    help='Print the line \'<BENCHMARKNAME>(RunTimeRaw):' +
+         ' <elapsed> ms\' at the end where <elapsed> is' +
+         ' the elapsed time in milliseconds.')
+  parser.add_option(
+      '--tag',
+      help='Tag of R8 to use.',
+      default=None)
+  parser.add_option(
+      '--version',
+      help='Version of R8 to use.',
+      default=None)
+  return parser.parse_args(argv)
+
+def main(argv):
+  (options, args) = ParseOptions(sys.argv)
+  r8_args = args[1:]
+  if options.lib_android:
+    r8_args.extend(['--lib', utils.get_android_jar(options.lib_android)])
+  if options.lib_rt:
+    r8_args.extend(['--lib', utils.RT_JAR])
+  time_consumer = lambda duration : print_duration(duration, options)
+  return toolhelper.run(
+      'r8',
+      r8_args,
+      build=not options.no_build,
+      debug=options.ea,
+      debug_agent=options.debug_agent,
+      jar=utils.find_r8_jar_from_options(options),
+      main='com.android.tools.r8.R8',
+      time_consumer=time_consumer)
+
+def print_duration(duration, options):
+  benchmark_name = options.print_runtimeraw
+  if benchmark_name:
+    print('%s-Total(RunTimeRaw): %s ms' % (benchmark_name, duration))
 
 if __name__ == '__main__':
-  sys.exit(toolhelper.run('r8', sys.argv[1:]))
+  sys.exit(main(sys.argv[1:]))
diff --git a/tools/r8_release.py b/tools/r8_release.py
index c5c5f51..347b29b 100755
--- a/tools/r8_release.py
+++ b/tools/r8_release.py
@@ -119,7 +119,10 @@
 
         validate_version_change_diff(version_diff_output, "main", version)
 
-        maybe_check_call(args, ['git', 'cl', 'upload', '--no-squash'])
+        cmd = ['git', 'cl', 'upload', '--no-squash']
+        if args.bypass_hooks:
+           cmd.append('--bypass-hooks')
+        maybe_check_call(args, cmd)
 
         if args.dry_run:
           input(
@@ -966,6 +969,10 @@
                       default=False,
                       action='store_true',
                       help='Delete CL in google3')
+  result.add_argument('--bypass-hooks', '--bypass_hooks',
+                      default=False,
+                      action='store_true',
+                      help="Bypass hooks when uploading")
   result.add_argument('--no-upload', '--no_upload',
                       default=False,
                       action='store_true',
diff --git a/tools/startup/generate_startup_descriptors.py b/tools/startup/generate_startup_descriptors.py
index c94a59c..adae239 100755
--- a/tools/startup/generate_startup_descriptors.py
+++ b/tools/startup/generate_startup_descriptors.py
@@ -63,7 +63,7 @@
       # Clear logcat and start capturing logcat.
       adb_utils.clear_logcat(device.device_id)
       logcat_process = adb_utils.start_logcat(
-          device.device_id, format='tag', filter='r8:I ActivityTaskManager:I *:S')
+          device.device_id, format='tag', filter='R8:I ActivityTaskManager:I *:S')
     else:
       # Clear existing profile data.
       adb_utils.clear_profile_data(options.app_id, device.device_id)
@@ -113,11 +113,13 @@
         print('Entering post startup: %s' % message)
         post_startup = True
         continue
-    elif tag == 'r8':
+    elif tag == 'R8':
       if is_startup_descriptor(message):
         startup_descriptors[message] = {
           'conditional_startup': False,
-          'post_startup': post_startup
+          'hot': False,
+          'post_startup': post_startup,
+          'startup': True
         }
         continue
     # Reaching here means we didn't expect this line.
@@ -318,6 +320,11 @@
                            'synthetic contexts',
                       action='store_true',
                       default=False)
+  result.add_argument('--grant-post-notification-permission',
+                      help='Grants the android.permission.POST_NOTIFICATIONS '
+                           'permission before launching the app',
+                      default=False,
+                      action='store_true')
   result.add_argument('--logcat',
                       action='store_true',
                       default=False)
@@ -401,6 +408,12 @@
   elif options.bundle:
     adb_utils.uninstall(options.app_id, device.device_id)
     adb_utils.install_bundle(options.bundle, device.device_id)
+  # Grant notifications.
+  if options.grant_post_notification_permission:
+    adb_utils.grant(
+        options.app_id,
+        'android.permission.POST_NOTIFICATIONS',
+        device.device_id)
   if options.until_stable:
     iteration = 0
     stable_iterations = 0
diff --git a/tools/test.py b/tools/test.py
index 40dc33c..89b4ee5 100755
--- a/tools/test.py
+++ b/tools/test.py
@@ -296,7 +296,7 @@
     gradle_args.append('-Pslow_tests=1')
   if options.tool:
     gradle_args.append('-Ptool=%s' % options.tool)
-  if options.one_line_per_test:
+  if options.one_line_per_test and not options.new_gradle:
     gradle_args.append('-Pone_line_per_test')
   if options.test_namespace:
     gradle_args.append('-Ptest_namespace=%s' % options.test_namespace)
@@ -342,10 +342,14 @@
     exit(1)
   if not options.no_r8lib:
     gradle_args.append('-Pr8lib')
-    # Force gradle to build a version of r8lib without dependencies for
-    # BootstrapCurrentEqualityTest.
-    gradle_args.append('R8LibNoDeps')
-    gradle_args.append('R8Retrace')
+    if options.new_gradle:
+      gradle_args.append(':test:r8LibNoDeps')
+      gradle_args.append(':test:retraceWithRelocatedDeps')
+    else:
+      # Force gradle to build a version of r8lib without dependencies for
+      # BootstrapCurrentEqualityTest.
+      gradle_args.append('R8LibNoDeps')
+      gradle_args.append('R8Retrace')
   if options.r8lib_no_deps:
     gradle_args.append('-Pr8lib_no_deps')
   if options.worktree:
@@ -372,6 +376,8 @@
     gradle_args.append(':main:r8WithRelocatedDeps')
     gradle_args.append(':test:cleanTest')
     gradle_args.append('test:test')
+    gradle_args.append('--stacktrace')
+    gradle_args.append('-Pprint_full_stacktraces')
   else:
     gradle_args.append('r8WithRelocatedDeps')
     gradle_args.append('r8WithRelocatedDeps17')
@@ -391,9 +397,14 @@
       print("No failing tests")
       return 0
   # Test filtering. Must always follow the 'test' task.
+  testFilterProperty = []
   for testFilter in args:
     gradle_args.append('--tests')
     gradle_args.append(testFilter)
+    testFilterProperty.append(testFilter)
+    assert not ("|" in testFilter), "| is used as separating character"
+  if len(testFilterProperty) > 0:
+    gradle_args.append("-Ptestfilter=" + "|".join(testFilterProperty))
   if options.with_code_coverage:
     # Create Jacoco report after tests.
     gradle_args.append('jacocoTestReport')
diff --git a/tools/testing_state.py b/tools/testing_state.py
index 7c51790..6cda6ca 100644
--- a/tools/testing_state.py
+++ b/tools/testing_state.py
@@ -16,6 +16,7 @@
     return
   if not testing_state_path:
     testing_state_path = os.path.join(DEFAULT_REPORTS_ROOT, utils.get_HEAD_branch())
+  testing_state_path = os.path.abspath(testing_state_path)
   gradle_args.append('-Ptesting-state-mode=%s' % testing_state_mode)
   gradle_args.append('-Ptesting-state-path=%s' % testing_state_path)
   prepare_testing_index(testing_state_mode, testing_state_path)
@@ -39,8 +40,7 @@
     parent_report = fresh_testing_index(testing_state_dir)
     os.rename(index_path, parent_report)
   index = open(index_path, "a")
-  relative_state_dir = os.path.relpath(testing_state_dir)
-  title = relative_state_dir
+  title = f"Testing: {os.path.basename(testing_state_dir)}"
   # Print a console link to the test report for easy access.
   print("=" * 70)
   print("Test report written to:")
@@ -59,6 +59,7 @@
   index.write(f"<p><a href=\"file://{testing_state_dir}\">Test directories</a></p>")
   # git branch/hash and diff for future reference
   index.write(f"<p>Run on: {datetime.datetime.now()}</p>")
+  index.write(f"<p>State path: {testing_state_dir}</p>")
   index.write(f"<p>Git branch: {utils.get_HEAD_branch()}")
   index.write(f"</br>Git SHA: {utils.get_HEAD_sha1()}")
   index.write(f'</br>Git diff summary:\n')
diff --git a/tools/toolhelper.py b/tools/toolhelper.py
index c8d8a08..f08fd49 100644
--- a/tools/toolhelper.py
+++ b/tools/toolhelper.py
@@ -16,7 +16,7 @@
         profile=False, track_memory_file=None, extra_args=None,
         stderr=None, stdout=None, return_stdout=False, timeout=0, quiet=False,
         cmd_prefix=None, jar=None, main=None, time_consumer=None,
-        worker_id=None):
+        debug_agent=None, worker_id=None):
   cmd = []
   if cmd_prefix:
     cmd.extend(cmd_prefix)
@@ -29,8 +29,9 @@
   cmd.append(jdk.GetJavaExecutable())
   if extra_args:
     cmd.extend(extra_args)
-  agent, args = extract_debug_agent_from_args(args)
-  if agent:
+  if debug_agent is None:
+    debug_agent, args = extract_debug_agent_from_args(args)
+  if debug_agent:
     cmd.append(
         '-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:5005')
   if debug:
diff --git a/tools/utils.py b/tools/utils.py
index fe8b539..4c671b9 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -71,7 +71,6 @@
 R8LIB_TESTS_JAR = os.path.join(LIBS, 'r8libtestdeps-cf.jar')
 R8_TESTS_DEPS_JAR = os.path.join(LIBS, 'test_deps_all.jar')
 R8LIB_TESTS_DEPS_JAR = R8_TESTS_DEPS_JAR
-MAVEN_ZIP = os.path.join(LIBS, 'r8.zip')
 MAVEN_ZIP_LIB = os.path.join(LIBS, 'r8lib.zip')
 LIBRARY_DESUGAR_CONVERSIONS_LEGACY_ZIP = os.path.join(
     CUSTOM_CONVERSION_DIR, 'library_desugar_conversions_legacy.jar')
@@ -312,7 +311,9 @@
   return defines.IsOsX()
 
 def EnsureDepFromGoogleCloudStorage(dep, tgz, sha1, msg):
-  if not os.path.exists(dep) or os.path.getmtime(tgz) < os.path.getmtime(sha1):
+  if (not os.path.exists(dep)
+     or not os.path.exists(tgz)
+     or os.path.getmtime(tgz) < os.path.getmtime(sha1)):
     DownloadFromGoogleCloudStorage(sha1)
     # Update the mtime of the tar file to make sure we do not run again unless
     # there is an update.