Merge commit '4cfe51ef79f7be4b8095d70294410ab25095c1e6' into dev-release

Change-Id: Ib34bee76fd2af418056373e6c0354160cb1db00f
diff --git a/doc/compilerdump.md b/doc/compilerdump.md
index c85a9ff..87976be 100644
--- a/doc/compilerdump.md
+++ b/doc/compilerdump.md
@@ -63,6 +63,17 @@
 ./gradlew assembleDebug -Dorg.gradle.caching=false -Dcom.android.tools.r8.dumpinputtodirectory=mydumps/ --no-daemon
 ```
 
+### Generating a dump in AOSP
+For R8 compilations you can generate dumps with the R8_DUMP_DIRECTORY environment variable.
+This will put dumps for all R8 compilations leading up to the target that you are building.
+For a specific target you can do:
+
+```
+mkdir /tmp/dumps
+R8_DUMP_DIRECTORY=/tmp/dumps m -j77 SystemUI
+```
+The actual dump file for SystemUI will be the last dump in /tmp/dumps.
+
 
 ## Reproducing using a dump
 
@@ -81,3 +92,4 @@
 
 The flags can also be used to override the setting specified in the dump.
 Doing so allows compiling with other compiler versions, or other settings.
+
diff --git a/infra/config/global/generated/cr-buildbucket.cfg b/infra/config/global/generated/cr-buildbucket.cfg
index 11b270c..ded5fe1 100644
--- a/infra/config/global/generated/cr-buildbucket.cfg
+++ b/infra/config/global/generated/cr-buildbucket.cfg
@@ -27,8 +27,8 @@
       dimensions: "os:Ubuntu-20.04"
       dimensions: "pool:luci.r8.ci"
       exe {
-        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
-        cipd_version: "refs/heads/master"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
         cmd: "luciexe"
       }
       properties:
@@ -56,8 +56,8 @@
       dimensions: "os:Ubuntu-20.04"
       dimensions: "pool:luci.r8.ci"
       exe {
-        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
-        cipd_version: "refs/heads/master"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
         cmd: "luciexe"
       }
       properties:
@@ -85,8 +85,8 @@
       dimensions: "os:Ubuntu-20.04"
       dimensions: "pool:luci.r8.ci"
       exe {
-        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
-        cipd_version: "refs/heads/master"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
         cmd: "luciexe"
       }
       properties:
@@ -114,8 +114,8 @@
       dimensions: "os:Ubuntu-20.04"
       dimensions: "pool:luci.r8.ci"
       exe {
-        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
-        cipd_version: "refs/heads/master"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
         cmd: "luciexe"
       }
       properties:
@@ -152,8 +152,8 @@
       dimensions: "os:Ubuntu-20.04"
       dimensions: "pool:luci.r8.ci"
       exe {
-        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
-        cipd_version: "refs/heads/master"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
         cmd: "luciexe"
       }
       properties:
@@ -186,8 +186,8 @@
       dimensions: "os:Ubuntu-20.04"
       dimensions: "pool:luci.r8.ci"
       exe {
-        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
-        cipd_version: "refs/heads/master"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
         cmd: "luciexe"
       }
       properties:
@@ -218,8 +218,8 @@
       dimensions: "os:Ubuntu-20.04"
       dimensions: "pool:luci.r8.ci"
       exe {
-        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
-        cipd_version: "refs/heads/master"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
         cmd: "luciexe"
       }
       properties:
@@ -250,8 +250,8 @@
       dimensions: "os:Ubuntu-20.04"
       dimensions: "pool:luci.r8.ci"
       exe {
-        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
-        cipd_version: "refs/heads/master"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
         cmd: "luciexe"
       }
       properties:
@@ -288,8 +288,8 @@
       dimensions: "os:Ubuntu-20.04"
       dimensions: "pool:luci.r8.ci"
       exe {
-        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
-        cipd_version: "refs/heads/master"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
         cmd: "luciexe"
       }
       properties:
@@ -326,8 +326,8 @@
       dimensions: "os:Ubuntu-20.04"
       dimensions: "pool:luci.r8.ci"
       exe {
-        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
-        cipd_version: "refs/heads/master"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
         cmd: "luciexe"
       }
       properties:
@@ -364,8 +364,8 @@
       dimensions: "os:Ubuntu-20.04"
       dimensions: "pool:luci.r8.ci"
       exe {
-        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
-        cipd_version: "refs/heads/master"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
         cmd: "luciexe"
       }
       properties:
@@ -402,8 +402,8 @@
       dimensions: "os:Ubuntu-20.04"
       dimensions: "pool:luci.r8.ci"
       exe {
-        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
-        cipd_version: "refs/heads/master"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
         cmd: "luciexe"
       }
       properties:
@@ -440,8 +440,8 @@
       dimensions: "os:Ubuntu-20.04"
       dimensions: "pool:luci.r8.ci"
       exe {
-        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
-        cipd_version: "refs/heads/master"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
         cmd: "luciexe"
       }
       properties:
@@ -478,8 +478,8 @@
       dimensions: "os:Ubuntu-20.04"
       dimensions: "pool:luci.r8.ci"
       exe {
-        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
-        cipd_version: "refs/heads/master"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
         cmd: "luciexe"
       }
       properties:
@@ -516,8 +516,8 @@
       dimensions: "os:Ubuntu-20.04"
       dimensions: "pool:luci.r8.ci"
       exe {
-        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
-        cipd_version: "refs/heads/master"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
         cmd: "luciexe"
       }
       properties:
@@ -554,8 +554,8 @@
       dimensions: "os:Ubuntu-20.04"
       dimensions: "pool:luci.r8.ci"
       exe {
-        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
-        cipd_version: "refs/heads/master"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
         cmd: "luciexe"
       }
       properties:
@@ -592,8 +592,8 @@
       dimensions: "os:Ubuntu-20.04"
       dimensions: "pool:luci.r8.ci"
       exe {
-        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
-        cipd_version: "refs/heads/master"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
         cmd: "luciexe"
       }
       properties:
@@ -630,8 +630,8 @@
       dimensions: "os:Ubuntu-20.04"
       dimensions: "pool:luci.r8.ci"
       exe {
-        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
-        cipd_version: "refs/heads/master"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
         cmd: "luciexe"
       }
       properties:
@@ -668,8 +668,8 @@
       dimensions: "os:Ubuntu-20.04"
       dimensions: "pool:luci.r8.ci"
       exe {
-        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
-        cipd_version: "refs/heads/master"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
         cmd: "luciexe"
       }
       properties:
@@ -706,8 +706,8 @@
       dimensions: "os:Ubuntu-20.04"
       dimensions: "pool:luci.r8.ci"
       exe {
-        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
-        cipd_version: "refs/heads/master"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
         cmd: "luciexe"
       }
       properties:
@@ -744,8 +744,8 @@
       dimensions: "os:Ubuntu-20.04"
       dimensions: "pool:luci.r8.ci"
       exe {
-        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
-        cipd_version: "refs/heads/master"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
         cmd: "luciexe"
       }
       properties:
@@ -782,8 +782,8 @@
       dimensions: "os:Ubuntu-20.04"
       dimensions: "pool:luci.r8.ci"
       exe {
-        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
-        cipd_version: "refs/heads/master"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
         cmd: "luciexe"
       }
       properties:
@@ -820,8 +820,8 @@
       dimensions: "os:Ubuntu-20.04"
       dimensions: "pool:luci.r8.ci"
       exe {
-        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
-        cipd_version: "refs/heads/master"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
         cmd: "luciexe"
       }
       properties:
@@ -858,8 +858,8 @@
       dimensions: "os:Ubuntu-20.04"
       dimensions: "pool:luci.r8.ci"
       exe {
-        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
-        cipd_version: "refs/heads/master"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
         cmd: "luciexe"
       }
       properties:
@@ -896,8 +896,8 @@
       dimensions: "os:Ubuntu-20.04"
       dimensions: "pool:luci.r8.ci"
       exe {
-        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
-        cipd_version: "refs/heads/master"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
         cmd: "luciexe"
       }
       properties:
@@ -934,8 +934,8 @@
       dimensions: "os:Ubuntu-20.04"
       dimensions: "pool:luci.r8.ci"
       exe {
-        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
-        cipd_version: "refs/heads/master"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
         cmd: "luciexe"
       }
       properties:
@@ -972,8 +972,8 @@
       dimensions: "os:Ubuntu-20.04"
       dimensions: "pool:luci.r8.ci"
       exe {
-        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
-        cipd_version: "refs/heads/master"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
         cmd: "luciexe"
       }
       properties:
@@ -1010,8 +1010,8 @@
       dimensions: "os:Ubuntu-20.04"
       dimensions: "pool:luci.r8.ci"
       exe {
-        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
-        cipd_version: "refs/heads/master"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
         cmd: "luciexe"
       }
       properties:
@@ -1048,8 +1048,8 @@
       dimensions: "os:Ubuntu-20.04"
       dimensions: "pool:luci.r8.ci"
       exe {
-        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
-        cipd_version: "refs/heads/master"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
         cmd: "luciexe"
       }
       properties:
@@ -1086,8 +1086,8 @@
       dimensions: "os:Ubuntu-20.04"
       dimensions: "pool:luci.r8.ci"
       exe {
-        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
-        cipd_version: "refs/heads/master"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
         cmd: "luciexe"
       }
       properties:
@@ -1124,8 +1124,8 @@
       dimensions: "os:Ubuntu-20.04"
       dimensions: "pool:luci.r8.ci"
       exe {
-        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
-        cipd_version: "refs/heads/master"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
         cmd: "luciexe"
       }
       properties:
@@ -1162,8 +1162,8 @@
       dimensions: "os:Ubuntu-20.04"
       dimensions: "pool:luci.r8.ci"
       exe {
-        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
-        cipd_version: "refs/heads/master"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
         cmd: "luciexe"
       }
       properties:
@@ -1199,8 +1199,8 @@
       dimensions: "os:Ubuntu-20.04"
       dimensions: "pool:luci.r8.ci"
       exe {
-        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
-        cipd_version: "refs/heads/master"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
         cmd: "luciexe"
       }
       properties:
@@ -1236,8 +1236,8 @@
       dimensions: "os:Ubuntu-20.04"
       dimensions: "pool:luci.r8.ci"
       exe {
-        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
-        cipd_version: "refs/heads/master"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
         cmd: "luciexe"
       }
       properties:
@@ -1273,8 +1273,8 @@
       dimensions: "os:Ubuntu-20.04"
       dimensions: "pool:luci.r8.ci"
       exe {
-        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
-        cipd_version: "refs/heads/master"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
         cmd: "luciexe"
       }
       properties:
@@ -1305,8 +1305,8 @@
       dimensions: "os:Ubuntu-20.04"
       dimensions: "pool:luci.r8.ci"
       exe {
-        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
-        cipd_version: "refs/heads/master"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
         cmd: "luciexe"
       }
       properties:
@@ -1337,8 +1337,8 @@
       dimensions: "os:Ubuntu-20.04"
       dimensions: "pool:luci.r8.ci"
       exe {
-        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
-        cipd_version: "refs/heads/master"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
         cmd: "luciexe"
       }
       properties:
@@ -1374,8 +1374,8 @@
       dimensions: "os:Ubuntu-20.04"
       dimensions: "pool:luci.r8.ci"
       exe {
-        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
-        cipd_version: "refs/heads/master"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
         cmd: "luciexe"
       }
       properties:
@@ -1411,8 +1411,8 @@
       dimensions: "os:Ubuntu-20.04"
       dimensions: "pool:luci.r8.ci"
       exe {
-        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
-        cipd_version: "refs/heads/master"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
         cmd: "luciexe"
       }
       properties:
@@ -1448,8 +1448,8 @@
       dimensions: "os:Ubuntu-20.04"
       dimensions: "pool:luci.r8.ci"
       exe {
-        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
-        cipd_version: "refs/heads/master"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
         cmd: "luciexe"
       }
       properties:
@@ -1485,8 +1485,8 @@
       dimensions: "os:Ubuntu-20.04"
       dimensions: "pool:luci.r8.ci"
       exe {
-        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
-        cipd_version: "refs/heads/master"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
         cmd: "luciexe"
       }
       properties:
@@ -1522,8 +1522,8 @@
       dimensions: "os:Ubuntu-20.04"
       dimensions: "pool:luci.r8.ci"
       exe {
-        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
-        cipd_version: "refs/heads/master"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
         cmd: "luciexe"
       }
       properties:
@@ -1559,8 +1559,8 @@
       dimensions: "os:Ubuntu-20.04"
       dimensions: "pool:luci.r8.ci"
       exe {
-        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
-        cipd_version: "refs/heads/master"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
         cmd: "luciexe"
       }
       properties:
@@ -1596,8 +1596,8 @@
       dimensions: "os:Ubuntu-20.04"
       dimensions: "pool:luci.r8.ci"
       exe {
-        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
-        cipd_version: "refs/heads/master"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
         cmd: "luciexe"
       }
       properties:
@@ -1633,8 +1633,8 @@
       dimensions: "os:Ubuntu-20.04"
       dimensions: "pool:luci.r8.ci"
       exe {
-        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
-        cipd_version: "refs/heads/master"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
         cmd: "luciexe"
       }
       properties:
@@ -1670,8 +1670,8 @@
       dimensions: "os:Ubuntu-20.04"
       dimensions: "pool:luci.r8.ci"
       exe {
-        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
-        cipd_version: "refs/heads/master"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
         cmd: "luciexe"
       }
       properties:
@@ -1707,8 +1707,8 @@
       dimensions: "os:Ubuntu-20.04"
       dimensions: "pool:luci.r8.ci"
       exe {
-        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
-        cipd_version: "refs/heads/master"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
         cmd: "luciexe"
       }
       properties:
@@ -1744,8 +1744,8 @@
       dimensions: "os:Ubuntu-20.04"
       dimensions: "pool:luci.r8.ci"
       exe {
-        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
-        cipd_version: "refs/heads/master"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
         cmd: "luciexe"
       }
       properties:
@@ -1781,8 +1781,8 @@
       dimensions: "os:Ubuntu-20.04"
       dimensions: "pool:luci.r8.ci"
       exe {
-        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
-        cipd_version: "refs/heads/master"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
         cmd: "luciexe"
       }
       properties:
@@ -1818,8 +1818,8 @@
       dimensions: "os:Ubuntu-20.04"
       dimensions: "pool:luci.r8.ci"
       exe {
-        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
-        cipd_version: "refs/heads/master"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
         cmd: "luciexe"
       }
       properties:
@@ -1852,8 +1852,8 @@
       dimensions: "os:Ubuntu-20.04"
       dimensions: "pool:luci.r8.ci"
       exe {
-        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
-        cipd_version: "refs/heads/master"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
         cmd: "luciexe"
       }
       properties:
@@ -1886,8 +1886,8 @@
       dimensions: "os:Ubuntu-20.04"
       dimensions: "pool:luci.r8.ci"
       exe {
-        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
-        cipd_version: "refs/heads/master"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
         cmd: "luciexe"
       }
       properties:
@@ -1915,8 +1915,8 @@
       dimensions: "os:Ubuntu-20.04"
       dimensions: "pool:luci.r8.ci"
       exe {
-        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
-        cipd_version: "refs/heads/master"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
         cmd: "luciexe"
       }
       properties:
@@ -1944,8 +1944,8 @@
       dimensions: "os:Windows-10"
       dimensions: "pool:luci.r8.ci"
       exe {
-        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
-        cipd_version: "refs/heads/master"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
         cmd: "luciexe"
       }
       properties:
@@ -1980,8 +1980,8 @@
       dimensions: "os:Windows-10"
       dimensions: "pool:luci.r8.ci"
       exe {
-        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
-        cipd_version: "refs/heads/master"
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
         cmd: "luciexe"
       }
       properties:
diff --git a/infra/config/global/main.star b/infra/config/global/main.star
index f223381..3e642c0 100755
--- a/infra/config/global/main.star
+++ b/infra/config/global/main.star
@@ -155,9 +155,8 @@
 luci.recipe(
       name="rex",
       cipd_package = "infra_internal/recipe_bundles/" +
-          "chrome-internal.googlesource.com/chrome/" +
-          "tools/build_limited/scripts/slave",
-      cipd_version = "refs/heads/master",
+          "chrome-internal.googlesource.com/chrome/tools/build",
+      cipd_version = "refs/heads/main",
       use_bbagent = True
 )
 
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 24d8545..93926dd 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -247,7 +247,7 @@
 
   static void runInternal(AndroidApp app, InternalOptions options, ExecutorService executor)
       throws IOException {
-    if (options.r8PartialCompilationOptions.enabled) {
+    if (options.partialCompilationConfiguration.isEnabled()) {
       try {
         new R8Partial(options).runInternal(app, executor);
       } catch (ResourceException e) {
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 1077122..6a7475f 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -51,6 +51,7 @@
 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.R8PartialCompilationConfiguration;
 import com.android.tools.r8.utils.Reporter;
 import com.android.tools.r8.utils.SemanticVersion;
 import com.android.tools.r8.utils.SetUtils;
@@ -149,6 +150,7 @@
     private AndroidResourceConsumer androidResourceConsumer = null;
     private ResourceShrinkerConfiguration resourceShrinkerConfiguration =
         ResourceShrinkerConfiguration.DEFAULT_CONFIGURATION;
+    private R8PartialCompilationConfiguration partialCompilationConfiguration = null;
 
     private final ProguardConfigurationParserOptions.Builder parserOptionsBuilder =
         ProguardConfigurationParserOptions.builder().readEnvironment();
@@ -554,6 +556,43 @@
       this.readEmbeddedRulesFromClasspathAndLibrary = enable;
     }
 
+    /**
+     * Configure partial shrinking in R8, where R8 is only applied to a part of the input.
+     *
+     * <p>The patterns in {@code includePatterns} and {@code excludePatterns} are comma separated
+     * lists of string patterns of fully qualified names of packages/classes. The patterns support
+     * the wildcards {@code *} and {@code **}. The wildcards are only supported at the end of the
+     * pattern, so only prefix matching. If the character just before the wildcard is a {@code .}
+     * (package separator) then the difference between {@code *} and {@code **} is that {@code *}
+     * only includes classes in the same package, whereas {@code **} includes classes in subpackages
+     * as well. If the character before the wildcard is not a {@code .} (package separator) then
+     * {@code *} and {@code **} will both match all classes with that prefix.
+     *
+     * <p>If {@code includePatterns} is not specified ({@code null} or an empty string), the default
+     * is {@code "androidx.**,kotlin.**,kotlinx.**"}.
+     *
+     * <p>The include patterns are processed first collecting all possible include classes. Then the
+     * exclude patterns are applied removing all matching classes from the collected include
+     * classes.
+     *
+     * @param includePatterns patterns for classes to include in R8 shrinking (see above for
+     *     semantics)
+     * @param excludePatterns patterns for classes to exclude from R8 shrinking (see above for
+     *     semantics)
+     * @return
+     */
+    @Deprecated
+    public Builder enableExperimentalPartialShrinking(
+        String includePatterns, String excludePatterns) {
+      if (includePatterns == null || includePatterns.isEmpty()) {
+        includePatterns = "androidx.**,kotlin.**,kotlinx.**";
+      }
+      partialCompilationConfiguration =
+          R8PartialCompilationConfiguration.fromIncludeExcludePatterns(
+              includePatterns, excludePatterns);
+      return self();
+    }
+
     @Override
     protected InternalProgramOutputPathConsumer createProgramOutputConsumer(
         Path path,
@@ -799,7 +838,8 @@
               androidResourceConsumer,
               resourceShrinkerConfiguration,
               keepSpecifications,
-              buildMetadataConsumer);
+              buildMetadataConsumer,
+              partialCompilationConfiguration);
 
       if (inputDependencyGraphConsumer != null) {
         inputDependencyGraphConsumer.finished();
@@ -1016,6 +1056,7 @@
   private final AndroidResourceConsumer androidResourceConsumer;
   private final ResourceShrinkerConfiguration resourceShrinkerConfiguration;
   private final Consumer<? super R8BuildMetadata> buildMetadataConsumer;
+  private final R8PartialCompilationConfiguration partialCompilationConfiguration;
 
   /** Get a new {@link R8Command.Builder}. */
   public static Builder builder() {
@@ -1115,7 +1156,8 @@
       AndroidResourceConsumer androidResourceConsumer,
       ResourceShrinkerConfiguration resourceShrinkerConfiguration,
       List<KeepSpecificationSource> keepSpecifications,
-      Consumer<? super R8BuildMetadata> buildMetadataConsumer) {
+      Consumer<? super R8BuildMetadata> buildMetadataConsumer,
+      R8PartialCompilationConfiguration partialCompilationConfiguration) {
     super(
         inputApp,
         mode,
@@ -1165,6 +1207,7 @@
     this.androidResourceConsumer = androidResourceConsumer;
     this.resourceShrinkerConfiguration = resourceShrinkerConfiguration;
     this.buildMetadataConsumer = buildMetadataConsumer;
+    this.partialCompilationConfiguration = partialCompilationConfiguration;
   }
 
   private R8Command(boolean printHelp, boolean printVersion) {
@@ -1194,6 +1237,7 @@
     androidResourceConsumer = null;
     resourceShrinkerConfiguration = null;
     buildMetadataConsumer = null;
+    partialCompilationConfiguration = null;
   }
 
   public DexItemFactory getDexItemFactory() {
@@ -1343,6 +1387,10 @@
     }
 
     // EXPERIMENTAL flags.
+    if (partialCompilationConfiguration != null) {
+      internal.partialCompilationConfiguration = partialCompilationConfiguration;
+    }
+
     assert !internal.forceProguardCompatibility;
     internal.forceProguardCompatibility = forceProguardCompatibility;
 
diff --git a/src/main/java/com/android/tools/r8/R8Partial.java b/src/main/java/com/android/tools/r8/R8Partial.java
index e9cb9b8..42b5b5b 100644
--- a/src/main/java/com/android/tools/r8/R8Partial.java
+++ b/src/main/java/com/android/tools/r8/R8Partial.java
@@ -21,9 +21,12 @@
 import com.android.tools.r8.tracereferences.TraceReferencesKeepRules;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.AndroidAppConsumers;
 import com.android.tools.r8.utils.DumpInputFlags;
+import com.android.tools.r8.utils.ExceptionUtils;
 import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Timing;
 import com.android.tools.r8.utils.ZipUtils;
 import com.android.tools.r8.utils.ZipUtils.ZipBuilder;
@@ -34,15 +37,37 @@
 import java.util.HashSet;
 import java.util.Set;
 import java.util.concurrent.ExecutorService;
-import java.util.function.Predicate;
+import java.util.function.Consumer;
 import java.util.stream.Collectors;
 
 class R8Partial {
 
   private final InternalOptions options;
+  private final Consumer<AndroidApp> r8InputAppConsumer;
+  private final Consumer<AndroidApp> d8InputAppConsumer;
+  private final Consumer<AndroidApp> r8OutputAppConsumer;
+  private final Consumer<AndroidApp> d8OutputAppConsumer;
 
   R8Partial(InternalOptions options) {
     this.options = options;
+    this.r8InputAppConsumer = options.partialCompilationConfiguration.r8InputAppConsumer;
+    this.d8InputAppConsumer = options.partialCompilationConfiguration.d8InputAppConsumer;
+    this.r8OutputAppConsumer = options.partialCompilationConfiguration.r8OutputAppConsumer;
+    this.d8OutputAppConsumer = options.partialCompilationConfiguration.d8OutputAppConsumer;
+  }
+
+  static void runForTesting(AndroidApp app, InternalOptions options)
+      throws CompilationFailedException {
+    ExecutorService executor = ThreadUtils.getExecutorService(options);
+    ExceptionUtils.withR8CompilationHandler(
+        options.reporter,
+        () -> {
+          try {
+            new R8Partial(options).runInternal(app, executor);
+          } finally {
+            executor.shutdown();
+          }
+        });
   }
 
   void runInternal(AndroidApp app, ExecutorService executor) throws IOException, ResourceException {
@@ -51,8 +76,8 @@
     ProgramConsumer originalProgramConsumer = options.programConsumer;
     MapConsumer originalMapConsumer = options.mapConsumer;
 
-    Path tmp = options.r8PartialCompilationOptions.getTemp();
-    Path dumpFile = options.r8PartialCompilationOptions.getDumpFile();
+    Path tmp = options.partialCompilationConfiguration.getTempDir();
+    Path dumpFile = options.partialCompilationConfiguration.getDumpFile();
 
     // Create a dump of the compiler input.
     // TODO(b/309743298): Do not use compiler dump to handle splitting the compilation. This should
@@ -94,15 +119,15 @@
         AppInfoWithClassHierarchy.createForDesugaring(
             AppInfo.createInitialAppInfo(dapp, GlobalSyntheticsStrategy.forNonSynthesizing()));
 
-    Predicate<String> isR8 = options.r8PartialCompilationOptions.isR8;
-    Set<String> d8classes = new HashSet<>();
+    Set<DexProgramClass> d8classes = new HashSet<>();
     appInfo
         .classes()
         .forEach(
             clazz -> {
-              String key = clazz.toSourceString();
-              if (!d8classes.contains(key) && !isR8.test(key)) {
-                d8classes.add(key);
+              if (!d8classes.contains(clazz)
+                  && !options.partialCompilationConfiguration.test(
+                      clazz.getType().getDescriptor())) {
+                d8classes.add(clazz);
                 // TODO(b/309743298): Improve this to only visit each class once and stop at
                 //  library boundary.
                 appInfo.forEachSuperType(
@@ -111,7 +136,7 @@
                       DexProgramClass superClass =
                           asProgramClassOrNull(appInfo.definitionFor(superType));
                       if (superClass != null) {
-                        d8classes.add(superClass.toSourceString());
+                        d8classes.add(superClass);
                       }
                     });
               }
@@ -120,7 +145,7 @@
     // Filter the program input into the D8 and R8 parts.
     Set<String> d8ZipEntries =
         d8classes.stream()
-            .map(name -> name.replace('.', '/') + ".class")
+            .map(clazz -> ZipUtils.zipEntryNameForClass(clazz.getClassReference()))
             .collect(Collectors.toSet());
     ZipBuilder d8ProgramBuilder = ZipBuilder.builder(tmp.resolve("d8-program.jar"));
     ZipBuilder r8ProgramBuilder = ZipBuilder.builder(tmp.resolve("r8-program.jar"));
@@ -151,13 +176,23 @@
       d8Builder.addDesugaredLibraryConfiguration(
           Files.readString(dump.getDesugaredLibraryFile(), UTF_8));
     }
+    AndroidAppConsumers d8OutputAppSink = null;
+    if (d8OutputAppConsumer != null) {
+      d8OutputAppSink = new AndroidAppConsumers(d8Builder);
+    }
     d8Builder.validate();
     D8Command d8command = d8Builder.makeCommand();
     AndroidApp d8App = d8command.getInputApp();
+    if (d8InputAppConsumer != null) {
+      d8InputAppConsumer.accept(d8App);
+    }
     InternalOptions d8Options = d8command.getInternalOptions();
     assert d8Options.getMinApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N)
         : "Default interface methods not yet supported";
     D8.runInternal(d8App, d8Options, executor);
+    if (d8OutputAppConsumer != null) {
+      d8OutputAppConsumer.accept(d8OutputAppSink.build());
+    }
 
     // Run trace references to produce keep rules for the D8 compiled part.
     // TODO(b/309743298): Do not emit keep rules into a file.
@@ -192,12 +227,23 @@
       r8Builder.addDesugaredLibraryConfiguration(
           Files.readString(dump.getDesugaredLibraryFile(), UTF_8));
     }
+    AndroidAppConsumers r8OutputAppSink = null;
+    if (r8Builder != null) {
+      r8OutputAppSink = new AndroidAppConsumers(r8Builder);
+    }
+    r8Builder.validate();
     R8Command r8Command = r8Builder.makeCommand();
     AndroidApp r8App = r8Command.getInputApp();
+    if (r8InputAppConsumer != null) {
+      r8InputAppConsumer.accept(r8App);
+    }
     InternalOptions r8Options = r8Command.getInternalOptions();
     r8Options.mapConsumer = originalMapConsumer;
     r8Options.quiet = true; // Don't write the R8 version.
     R8.runInternal(r8App, r8Options, executor);
+    if (r8OutputAppConsumer != null) {
+      r8OutputAppConsumer.accept(r8OutputAppSink.build());
+    }
 
     // Emit resources and merged DEX to the output consumer.
     // TODO(b/309743298): Consider passing the DataResourceConsumer to the R8 invocation above.
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 7d9439b..763198e 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -114,14 +114,11 @@
 import com.android.tools.r8.verticalclassmerging.VerticallyMergedClasses;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Predicates;
-import com.google.common.base.Splitter;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -1022,9 +1019,10 @@
   private final ArtProfileOptions artProfileOptions = new ArtProfileOptions(this);
   private final StartupOptions startupOptions = new StartupOptions();
   private final InstrumentationOptions instrumentationOptions;
-  public final R8PartialCompilationOptions r8PartialCompilationOptions =
-      new R8PartialCompilationOptions(
-          System.getProperty("com.android.tools.r8.r8PartialCompilation"));
+  public R8PartialCompilationConfiguration partialCompilationConfiguration =
+      R8PartialCompilationConfiguration.fromIncludeExcludePatterns(
+          System.getProperty("com.android.tools.r8.experimentalPartialShrinkingIncludePatterns"),
+          System.getProperty("com.android.tools.r8.experimentalPartialShrinkingExcludePatterns"));
   public final TestingOptions testing = new TestingOptions();
 
   public List<ProguardConfigurationRule> mainDexKeepRules = ImmutableList.of();
@@ -2282,39 +2280,6 @@
     }
   }
 
-  public static class R8PartialCompilationOptions {
-    public boolean enabled;
-    public Path tempDir = null;
-    public Predicate<String> isR8 = null;
-
-    R8PartialCompilationOptions(String partialR8) {
-      this.enabled = partialR8 != null;
-      if (this.enabled) {
-        final List<String> prefixes = Splitter.on(",").splitToList(partialR8);
-        this.isR8 =
-            name -> {
-              for (int i = 0; i < prefixes.size(); i++) {
-                if (name.startsWith(prefixes.get(i))) {
-                  return true;
-                }
-              }
-              return false;
-            };
-      }
-    }
-
-    public synchronized Path getTemp() throws IOException {
-      if (tempDir == null) {
-        tempDir = Files.createTempDirectory("r8PartialCompilation");
-      }
-      return tempDir;
-    }
-
-    public Path getDumpFile() throws IOException {
-      return getTemp().resolve("dump.zip");
-    }
-  }
-
   public static class TestingOptions {
 
     public boolean enableEmbeddedKeepAnnotations =
diff --git a/src/main/java/com/android/tools/r8/utils/R8PartialCompilationConfiguration.java b/src/main/java/com/android/tools/r8/utils/R8PartialCompilationConfiguration.java
new file mode 100644
index 0000000..a1c3b2d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/R8PartialCompilationConfiguration.java
@@ -0,0 +1,283 @@
+// Copyright (c) 2024, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+import com.android.tools.r8.graph.DexString;
+import com.google.common.base.Predicates;
+import com.google.common.base.Splitter;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+
+public class R8PartialCompilationConfiguration implements Predicate<DexString> {
+
+  private final boolean enabled;
+  private Path tempDir = null;
+  private final List<Predicate<DexString>> includePredicates;
+  private final List<Predicate<DexString>> excludePredicates;
+
+  public Consumer<AndroidApp> r8InputAppConsumer;
+  public Consumer<AndroidApp> d8InputAppConsumer;
+  public Consumer<AndroidApp> r8OutputAppConsumer;
+  public Consumer<AndroidApp> d8OutputAppConsumer;
+
+  private static final R8PartialCompilationConfiguration disabledConfiguration =
+      new R8PartialCompilationConfiguration(false, null, null);
+
+  private R8PartialCompilationConfiguration(
+      boolean enabled,
+      List<Predicate<DexString>> includePredicates,
+      List<Predicate<DexString>> excludePredicates) {
+    assert !enabled || !includePredicates.isEmpty();
+    assert !enabled || excludePredicates != null;
+    this.enabled = enabled;
+    this.includePredicates = includePredicates;
+    this.excludePredicates = excludePredicates;
+  }
+
+  @Override
+  public boolean test(DexString name) {
+    for (Predicate<DexString> isR8ClassPredicate : includePredicates) {
+      if (isR8ClassPredicate.test(name)) {
+        for (Predicate<DexString> isD8ClassPredicate : excludePredicates) {
+          if (isD8ClassPredicate.test(name)) {
+            return false;
+          }
+        }
+        return true;
+      }
+    }
+    return false;
+  }
+
+  public static R8PartialCompilationConfiguration disabledConfiguration() {
+    return disabledConfiguration;
+  }
+
+  public static R8PartialCompilationConfiguration fromIncludeExcludePatterns(
+      String includePatterns, String excludePatterns) {
+    boolean enabled = includePatterns != null || excludePatterns != null;
+    if (!enabled) {
+      return disabledConfiguration();
+    }
+    Builder builder = builder();
+    if (includePatterns != null) {
+      Splitter.on(",").splitToList(includePatterns).forEach(builder::addJavaTypeIncludePattern);
+    }
+    if (excludePatterns != null) {
+      Splitter.on(",").splitToList(excludePatterns).forEach(builder::addJavaTypeExcludePattern);
+    }
+    return builder.build();
+  }
+
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  public boolean isEnabled() {
+    return enabled;
+  }
+
+  public synchronized Path getTempDir() throws IOException {
+    if (tempDir == null) {
+      setTempDir(Files.createTempDirectory("r8PartialCompilation"));
+    }
+    return tempDir;
+  }
+
+  public void setTempDir(Path tempDir) {
+    this.tempDir = tempDir;
+  }
+
+  public Path getDumpFile() throws IOException {
+    return getTempDir().resolve("dump.zip");
+  }
+
+  public static class Builder {
+    private final List<Predicate<DexString>> includePredicates = new ArrayList<>();
+    private final List<Predicate<DexString>> excludePredicates = new ArrayList<>();
+
+    private Builder() {}
+
+    public R8PartialCompilationConfiguration build() {
+      return new R8PartialCompilationConfiguration(
+          !includePredicates.isEmpty(), includePredicates, excludePredicates);
+    }
+
+    public Builder includeAll() {
+      includePredicates.add(Predicates.alwaysTrue());
+      return this;
+    }
+
+    public Builder addJavaTypeIncludePattern(String pattern) {
+      includePredicates.add(
+          createMatcher("L" + DescriptorUtils.getBinaryNameFromJavaType(pattern)));
+      return this;
+    }
+
+    public Builder addJavaTypeExcludePattern(String pattern) {
+      excludePredicates.add(
+          createMatcher("L" + DescriptorUtils.getBinaryNameFromJavaType(pattern)));
+      return this;
+    }
+
+    public Builder addDescriptorIncludePattern(String pattern) {
+      includePredicates.add(createMatcher(pattern));
+      return this;
+    }
+
+    public Builder addDescriptorExcludePattern(String pattern) {
+      excludePredicates.add(createMatcher(pattern));
+      return this;
+    }
+
+    private Predicate<DexString> createMatcher(String descriptorPrefix) {
+      assert descriptorPrefix.startsWith("L");
+      assert descriptorPrefix.indexOf('.') == -1;
+
+      if (descriptorPrefix.equals("L**")) {
+        return new AllClassesMatcher();
+      } else if (descriptorPrefix.equals("L*")) {
+        return new UnnamedPackageMatcher();
+      } else if (descriptorPrefix.endsWith("/**")) {
+        return new PackageAndSubpackagePrefixMatcher(
+            descriptorPrefix.substring(0, descriptorPrefix.length() - 2));
+      } else if (descriptorPrefix.endsWith("/*")) {
+        return new PackagePrefixMatcher(
+            descriptorPrefix.substring(0, descriptorPrefix.length() - 1));
+      }
+      if (descriptorPrefix.endsWith("*")) {
+        return new ClassPrefixMatcher(descriptorPrefix.substring(0, descriptorPrefix.length() - 1));
+      } else {
+        return new ClassNameMatcher(descriptorPrefix + ';');
+      }
+    }
+
+    public Builder includeClasses(Class<?>... classes) {
+      return includeClasses(Arrays.asList(classes));
+    }
+
+    public Builder includeClasses(Collection<Class<?>> classes) {
+      classes.forEach(
+          clazz ->
+              includePredicates.add(
+                  descriptor ->
+                      descriptor.toString().equals(DescriptorUtils.javaClassToDescriptor(clazz))));
+      return this;
+    }
+
+    public Builder includeJavaType(Predicate<String> include) {
+      includePredicates.add(
+          descriptor -> include.test(DescriptorUtils.descriptorToJavaType(descriptor.toString())));
+      return this;
+    }
+
+    public Builder excludeClasses(Class<?>... classes) {
+      return excludeClasses(Arrays.asList(classes));
+    }
+
+    public Builder excludeClasses(Collection<Class<?>> classes) {
+      classes.forEach(
+          clazz ->
+              excludePredicates.add(
+                  descriptor ->
+                      descriptor.toString().equals(DescriptorUtils.javaClassToDescriptor(clazz))));
+      return this;
+    }
+
+    public Builder excludeJavaType(Predicate<String> exclude) {
+      excludePredicates.add(
+          descriptor -> exclude.test(DescriptorUtils.descriptorToJavaType(descriptor.toString())));
+      return this;
+    }
+  }
+
+  private static class AllClassesMatcher implements Predicate<DexString> {
+
+    AllClassesMatcher() {}
+
+    @Override
+    public boolean test(DexString descriptor) {
+      return true;
+    }
+  }
+
+  private static class UnnamedPackageMatcher implements Predicate<DexString> {
+
+    UnnamedPackageMatcher() {}
+
+    @Override
+    public boolean test(DexString descriptor) {
+      return descriptor.indexOf('/') == -1;
+    }
+  }
+
+  private static class PackageAndSubpackagePrefixMatcher implements Predicate<DexString> {
+
+    private final byte[] descriptorPrefix;
+
+    PackageAndSubpackagePrefixMatcher(String descriptorPrefix) {
+      this.descriptorPrefix = DexString.encodeToMutf8(descriptorPrefix);
+    }
+
+    @Override
+    public boolean test(DexString descriptor) {
+      return descriptor.startsWith(descriptorPrefix);
+    }
+  }
+
+  private static class PackagePrefixMatcher implements Predicate<DexString> {
+
+    private final byte[] descriptorPrefix;
+    private final int descriptorPrefixLength;
+
+    PackagePrefixMatcher(String descriptorPrefix) {
+      this.descriptorPrefix = DexString.encodeToMutf8(descriptorPrefix);
+      this.descriptorPrefixLength = descriptorPrefix.length();
+    }
+
+    @Override
+    public boolean test(DexString descriptor) {
+      return descriptor.startsWith(descriptorPrefix)
+          && descriptor.lastIndexOf('/') == descriptorPrefixLength - 1;
+    }
+  }
+
+  private static class ClassPrefixMatcher implements Predicate<DexString> {
+
+    private final byte[] descriptorPrefix;
+    private final int descriptorPrefixLength;
+
+    ClassPrefixMatcher(String descriptorPrefix) {
+      this.descriptorPrefix = DexString.encodeToMutf8(descriptorPrefix);
+      this.descriptorPrefixLength = descriptorPrefix.length();
+    }
+
+    @Override
+    public boolean test(DexString descriptor) {
+      return descriptor.startsWith(descriptorPrefix)
+          && descriptor.lastIndexOf('/') < descriptorPrefixLength - 1;
+    }
+  }
+
+  private static class ClassNameMatcher implements Predicate<DexString> {
+
+    private final String descriptor;
+
+    ClassNameMatcher(String descriptor) {
+      this.descriptor = descriptor;
+    }
+
+    @Override
+    public boolean test(DexString descriptor) {
+      return descriptor.toString().equals(this.descriptor);
+    }
+  }
+}
diff --git a/src/test/examplesJava9/twraddsuppressed/TestClass.java b/src/test/examplesJava9/twraddsuppressed/TestClass.java
index 61285ce..fcf0d6f 100644
--- a/src/test/examplesJava9/twraddsuppressed/TestClass.java
+++ b/src/test/examplesJava9/twraddsuppressed/TestClass.java
@@ -20,6 +20,8 @@
   }
 
   public static void bar() {
+    // NOTE: The $closeResource helper is _only_ generated by the JDK-9 compiler.
+    //
     // Use twr twice to have javac generate a shared $closeResource helper.
     try (MyClosable closable = new MyClosable()) {
       foo();
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 5d349fc..bd1aace 100644
--- a/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java
+++ b/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java
@@ -36,6 +36,7 @@
 import com.android.tools.r8.compilerapi.testsetup.ApiTestingSetUpTest;
 import com.android.tools.r8.compilerapi.wrappers.CommandLineParserTest;
 import com.android.tools.r8.compilerapi.wrappers.EnableMissingLibraryApiModelingTest;
+import com.android.tools.r8.partial.PartialShrinkingPreviewApiTest;
 import com.google.common.collect.ImmutableList;
 import java.nio.file.Path;
 import java.nio.file.Paths;
@@ -79,7 +80,7 @@
           MainDexRulesTest.ApiTest.class);
 
   private static final List<Class<? extends CompilerApiTest>> CLASSES_PENDING_BINARY_COMPATIBILITY =
-      ImmutableList.of();
+      ImmutableList.of(PartialShrinkingPreviewApiTest.ApiTest.class);
 
   private final TemporaryFolder temp;
 
diff --git a/src/test/java/com/android/tools/r8/desugar/suppressedexceptions/TwrSuppressedExceptionsTest.java b/src/test/java/com/android/tools/r8/desugar/suppressedexceptions/TwrSuppressedExceptionsTest.java
index ce7e1a7..6c03e85 100644
--- a/src/test/java/com/android/tools/r8/desugar/suppressedexceptions/TwrSuppressedExceptionsTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/suppressedexceptions/TwrSuppressedExceptionsTest.java
@@ -84,6 +84,7 @@
               ClassSubject clazz = inspector.clazz(MAIN.typeName());
               hasInvokesTo(
                   clazz.uniqueMethodWithOriginalName("bar"),
+                  // NOTE: The $closeResource helper is _only_ generated by the JDK-9 compiler.
                   "$closeResource",
                   apiLevelHasTwrCloseResourceSupport(true) ? 4 : 0);
               if (apiLevelHasSuppressedExceptionsSupport(true)) {
@@ -102,6 +103,7 @@
             DesugarTestConfiguration::isNotDesugared,
             inspector -> {
               ClassSubject clazz = inspector.clazz(MAIN.typeName());
+              // NOTE: The $closeResource helper is _only_ generated by the JDK-9 compiler.
               hasInvokesTo(clazz.uniqueMethodWithOriginalName("bar"), "$closeResource", 4);
               hasInvokesTo(clazz.mainMethod(), "getSuppressed", 1);
             });
diff --git a/src/test/java/com/android/tools/r8/desugar/twr/TwrCloseResourceDuplicationTest.java b/src/test/java/com/android/tools/r8/desugar/twr/TwrCloseResourceDuplicationTest.java
index b5b8444..dbea1507 100644
--- a/src/test/java/com/android/tools/r8/desugar/twr/TwrCloseResourceDuplicationTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/twr/TwrCloseResourceDuplicationTest.java
@@ -110,6 +110,8 @@
         .assertSuccessWithOutput(EXPECTED)
         .inspect(
             inspector -> {
+              // NOTE: The $closeResource helper is _only_ generated by the JDK-9 compiler.
+              //
               // There should be two synthetic classes besides the three program classes.
               // One for the desugar version of TWR $closeResource and one for the
               // Throwable.addSuppressed that is still present in the original $closeResource.
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/backports/CloseResourceMethod.java b/src/test/java/com/android/tools/r8/ir/desugar/backports/CloseResourceMethod.java
index 2059e86..7e18bdb 100644
--- a/src/test/java/com/android/tools/r8/ir/desugar/backports/CloseResourceMethod.java
+++ b/src/test/java/com/android/tools/r8/ir/desugar/backports/CloseResourceMethod.java
@@ -8,6 +8,9 @@
 
 public class CloseResourceMethod {
 
+  // NOTE: The $closeResource helper is _only_ generated by the JDK-9 compiler. The test
+  // CheckTwrOutputForJavaCompilersTest will fail if it is generated by other compilers.
+  //
   // The following method defines the code of
   //
   //     public static void $closeResource(Throwable throwable, Object resource)
diff --git a/src/test/java/com/android/tools/r8/partial/ClassHierarchyInterleavedD8AndR8Test.java b/src/test/java/com/android/tools/r8/partial/ClassHierarchyInterleavedD8AndR8Test.java
index db45859..32bfa65 100644
--- a/src/test/java/com/android/tools/r8/partial/ClassHierarchyInterleavedD8AndR8Test.java
+++ b/src/test/java/com/android/tools/r8/partial/ClassHierarchyInterleavedD8AndR8Test.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8.partial;
 
 import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
 import static org.hamcrest.MatcherAssert.assertThat;
@@ -13,9 +14,10 @@
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.ToolHelper.DexVm;
 import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.R8PartialCompilationConfiguration;
 import com.android.tools.r8.utils.ThrowingConsumer;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import java.util.function.Predicate;
+import java.util.function.Consumer;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -40,15 +42,18 @@
   }
 
   private void runTest(
-      Predicate<String> isR8, ThrowingConsumer<CodeInspector, RuntimeException> inspector)
+      Consumer<R8PartialCompilationConfiguration.Builder> partialConfigurationBuilderConsumer,
+      ThrowingConsumer<CodeInspector, RuntimeException> d8Inspector,
+      ThrowingConsumer<CodeInspector, RuntimeException> inspector)
       throws Exception {
     // Path tempDir = temp.newFolder().toPath();
     testForR8Partial(parameters.getBackend())
         .setMinApi(parameters)
         .addProgramClasses(A.class, B.class, C.class, Main.class)
         .addKeepMainRule(Main.class)
-        .setR8PartialConfigurationPredicate(isR8)
+        .setR8PartialConfiguration(partialConfigurationBuilderConsumer)
         .compile()
+        .inspectD8Input(d8Inspector)
         .inspect(inspector)
         .run(parameters.getRuntime(), Main.class)
         .assertSuccessWithEmptyOutput();
@@ -57,7 +62,13 @@
   @Test
   public void testD8Top() throws Exception {
     runTest(
-        name -> !name.equals(A.class.getTypeName()),
+        partialConfigurationBuilder ->
+            partialConfigurationBuilder.includeAll().excludeClasses(A.class),
+        inspector -> {
+          assertThat(inspector.programClass(A.class), isPresent());
+          assertThat(inspector.programClass(B.class), isAbsent());
+          assertThat(inspector.programClass(C.class), isAbsent());
+        },
         inspector -> {
           assertThat(inspector.clazz(A.class), isPresentAndNotRenamed());
           assertThat(inspector.clazz(B.class), isAbsent()); // Merged into C.
@@ -68,7 +79,13 @@
   @Test
   public void testD8Middle() throws Exception {
     runTest(
-        name -> !name.equals(B.class.getTypeName()),
+        partialConfigurationBuilder ->
+            partialConfigurationBuilder.includeAll().excludeClasses(B.class),
+        inspector -> {
+          assertThat(inspector.programClass(A.class), isPresent());
+          assertThat(inspector.programClass(B.class), isPresent());
+          assertThat(inspector.programClass(C.class), isAbsent());
+        },
         inspector -> {
           assertThat(inspector.clazz(A.class), isPresentAndNotRenamed());
           assertThat(inspector.clazz(B.class), isPresentAndNotRenamed());
@@ -79,7 +96,13 @@
   @Test
   public void testD8Bottom() throws Exception {
     runTest(
-        name -> !name.equals(C.class.getTypeName()),
+        partialConfigurationBuilder ->
+            partialConfigurationBuilder.includeAll().excludeClasses(C.class),
+        inspector -> {
+          assertThat(inspector.programClass(A.class), isPresent());
+          assertThat(inspector.programClass(B.class), isPresent());
+          assertThat(inspector.programClass(C.class), isPresent());
+        },
         inspector -> {
           assertThat(inspector.clazz(A.class), isPresentAndNotRenamed());
           assertThat(inspector.clazz(B.class), isPresentAndNotRenamed());
diff --git a/src/test/java/com/android/tools/r8/partial/PartialCompilationBasicPreviewPatternsTest.java b/src/test/java/com/android/tools/r8/partial/PartialCompilationBasicPreviewPatternsTest.java
new file mode 100644
index 0000000..b6c55e9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/partial/PartialCompilationBasicPreviewPatternsTest.java
@@ -0,0 +1,286 @@
+// Copyright (c) 2024, 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.partial;
+
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.partial.pkg1.A1;
+import com.android.tools.r8.partial.pkg1.A2;
+import com.android.tools.r8.partial.pkg1.subpkg.B;
+import com.android.tools.r8.partial.pkg2.C1;
+import com.android.tools.r8.partial.pkg2.C2;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class PartialCompilationBasicPreviewPatternsTest extends TestBase {
+
+  private static String PKG1 = getPackageName(A1.class);
+  private static String SUBPKG = getPackageName(B.class);
+  private static String PKG2 = getPackageName(C2.class);
+
+  private static String getPackageName(Class<?> clazz) {
+    return clazz.getTypeName().substring(0, clazz.getTypeName().lastIndexOf('.'));
+  }
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  // Test with min API level 24.
+  public static TestParametersCollection data() {
+    return getTestParameters()
+        .withDexRuntime(DexVm.Version.V7_0_0)
+        .withApiLevel(AndroidApiLevel.N)
+        .build();
+  }
+
+  private static final List<Class<?>> ALL_CLASSES =
+      ImmutableList.of(A1.class, A2.class, B.class, C1.class, C2.class, Main.class);
+  private static final String[] ALL_TYPE_NAMES =
+      new String[] {
+        A1.class.getTypeName(),
+        A2.class.getTypeName(),
+        B.class.getTypeName(),
+        C1.class.getTypeName(),
+        C2.class.getTypeName()
+      };
+
+  @Test
+  public void pkg1AndSubpackagesCompiledWithR8() throws Exception {
+    testForR8Partial(parameters.getBackend())
+        .setMinApi(parameters)
+        .addProgramClasses(ALL_CLASSES)
+        .setR8PartialConfiguration(builder -> builder.addJavaTypeIncludePattern(PKG1 + ".**"))
+        .compile()
+        .inspectD8Input(
+            inspector ->
+                assertTrue(inspector.hasExactlyProgramClasses(C1.class, C2.class, Main.class)))
+        .inspectR8Input(
+            inspector ->
+                assertTrue(inspector.hasExactlyProgramClasses(A1.class, A2.class, B.class)))
+        .inspect(
+            inspector ->
+                assertTrue(inspector.hasExactlyProgramClasses(C1.class, C2.class, Main.class)))
+        .run(parameters.getRuntime(), Main.class, ALL_TYPE_NAMES)
+        .assertSuccessWithOutputLines(
+            "Not instantiated",
+            "Not instantiated",
+            "Not instantiated",
+            "Instantiated",
+            "Instantiated");
+  }
+
+  @Test
+  public void pkg1CompiledWithR8() throws Exception {
+    testForR8Partial(parameters.getBackend())
+        .setMinApi(parameters)
+        .addProgramClasses(ALL_CLASSES)
+        .setR8PartialConfiguration(builder -> builder.addJavaTypeIncludePattern(PKG1 + ".*"))
+        .compile()
+        .inspectD8Input(
+            inspector ->
+                assertTrue(
+                    inspector.hasExactlyProgramClasses(B.class, C1.class, C2.class, Main.class)))
+        .inspectR8Input(
+            inspector -> assertTrue(inspector.hasExactlyProgramClasses(A1.class, A2.class)))
+        .inspect(
+            inspector ->
+                assertTrue(
+                    inspector.hasExactlyProgramClasses(B.class, C1.class, C2.class, Main.class)))
+        .run(parameters.getRuntime(), Main.class, ALL_TYPE_NAMES)
+        .assertSuccessWithOutputLines(
+            "Not instantiated", "Not instantiated", "Instantiated", "Instantiated", "Instantiated");
+  }
+
+  @Test
+  public void pkg1AndSubpackagesExcludeSubPkgCompiledWithR8() throws Exception {
+    testForR8Partial(parameters.getBackend())
+        .setMinApi(parameters)
+        .addProgramClasses(ALL_CLASSES)
+        .setR8PartialConfiguration(
+            builder ->
+                builder
+                    .addJavaTypeIncludePattern(PKG1 + ".**")
+                    .addJavaTypeExcludePattern(SUBPKG + ".*"))
+        .compile()
+        .inspectD8Input(
+            inspector ->
+                assertTrue(
+                    inspector.hasExactlyProgramClasses(C1.class, C2.class, B.class, Main.class)))
+        .inspectR8Input(
+            inspector -> assertTrue(inspector.hasExactlyProgramClasses(A1.class, A2.class)))
+        .inspect(
+            inspector ->
+                assertTrue(
+                    inspector.hasExactlyProgramClasses(B.class, C1.class, C2.class, Main.class)))
+        .run(parameters.getRuntime(), Main.class, ALL_TYPE_NAMES)
+        .assertSuccessWithOutputLines(
+            "Not instantiated", "Not instantiated", "Instantiated", "Instantiated", "Instantiated");
+  }
+
+  @Test
+  public void pkg1AndSubpackagesExcludeAPrefixCompiledWithR8() throws Exception {
+    testForR8Partial(parameters.getBackend())
+        .setMinApi(parameters)
+        .addProgramClasses(ALL_CLASSES)
+        .setR8PartialConfiguration(
+            builder ->
+                builder
+                    .addJavaTypeIncludePattern(PKG1 + ".**")
+                    .addJavaTypeExcludePattern(PKG1 + ".A*"))
+        .compile()
+        .inspectD8Input(
+            inspector ->
+                assertTrue(
+                    inspector.hasExactlyProgramClasses(
+                        A1.class, A2.class, C1.class, C2.class, Main.class)))
+        .inspectR8Input(inspector -> assertTrue(inspector.hasExactlyProgramClasses(B.class)))
+        .inspect(
+            inspector ->
+                assertTrue(
+                    inspector.hasExactlyProgramClasses(
+                        A1.class, A2.class, C1.class, C2.class, Main.class)))
+        .run(parameters.getRuntime(), Main.class, ALL_TYPE_NAMES)
+        .assertSuccessWithOutputLines(
+            "Instantiated", "Instantiated", "Not instantiated", "Instantiated", "Instantiated");
+  }
+
+  @Test
+  public void pkg1AndSubpackagesExcludeA1AndA2CompiledWithR8() throws Exception {
+    testForR8Partial(parameters.getBackend())
+        .setMinApi(parameters)
+        .addProgramClasses(ALL_CLASSES)
+        .setR8PartialConfiguration(
+            builder ->
+                builder
+                    .addJavaTypeIncludePattern(PKG1 + ".**")
+                    .addJavaTypeExcludePattern(PKG1 + ".A1")
+                    .addJavaTypeExcludePattern(PKG1 + ".A2"))
+        .compile()
+        .inspectD8Input(
+            inspector ->
+                assertTrue(
+                    inspector.hasExactlyProgramClasses(
+                        A1.class, A2.class, C1.class, C2.class, Main.class)))
+        .inspectR8Input(inspector -> assertTrue(inspector.hasExactlyProgramClasses(B.class)))
+        .inspect(
+            inspector ->
+                assertTrue(
+                    inspector.hasExactlyProgramClasses(
+                        A1.class, A2.class, C1.class, C2.class, Main.class)))
+        .run(parameters.getRuntime(), Main.class, ALL_TYPE_NAMES)
+        .assertSuccessWithOutputLines(
+            "Instantiated", "Instantiated", "Not instantiated", "Instantiated", "Instantiated");
+  }
+
+  @Test
+  public void allExeptC1CompiledWithR8() throws Exception {
+    testForR8Partial(parameters.getBackend())
+        .setMinApi(parameters)
+        .addProgramClasses(ALL_CLASSES)
+        .setR8PartialConfiguration(
+            builder ->
+                builder
+                    .addJavaTypeIncludePattern(PKG1 + ".**")
+                    .addJavaTypeIncludePattern(PKG2 + ".**")
+                    .addJavaTypeExcludePattern(PKG2 + ".C1"))
+        .compile()
+        .inspectD8Input(
+            inspector -> assertTrue(inspector.hasExactlyProgramClasses(C1.class, Main.class)))
+        .inspectR8Input(
+            inspector ->
+                assertTrue(
+                    inspector.hasExactlyProgramClasses(A1.class, A2.class, B.class, C2.class)))
+        .inspect(inspector -> assertTrue(inspector.hasExactlyProgramClasses(C1.class, Main.class)))
+        .run(parameters.getRuntime(), Main.class, ALL_TYPE_NAMES)
+        .assertSuccessWithOutputLines(
+            "Not instantiated",
+            "Not instantiated",
+            "Not instantiated",
+            "Instantiated",
+            "Not instantiated");
+  }
+
+  @Test
+  public void allExeptA1CompiledWithR8() throws Exception {
+    testForR8Partial(parameters.getBackend())
+        .setMinApi(parameters)
+        .addProgramClasses(ALL_CLASSES)
+        .setR8PartialConfiguration(
+            builder ->
+                builder
+                    .addJavaTypeIncludePattern(PKG1 + ".*")
+                    .addJavaTypeIncludePattern(SUBPKG + ".*")
+                    .addJavaTypeIncludePattern(PKG2 + ".*")
+                    .addJavaTypeExcludePattern(PKG1 + ".A1"))
+        .compile()
+        .inspectD8Input(
+            inspector -> assertTrue(inspector.hasExactlyProgramClasses(A1.class, Main.class)))
+        .inspectR8Input(
+            inspector ->
+                assertTrue(
+                    inspector.hasExactlyProgramClasses(A2.class, B.class, C1.class, C2.class)))
+        .inspect(inspector -> assertTrue(inspector.hasExactlyProgramClasses(A1.class, Main.class)))
+        .run(parameters.getRuntime(), Main.class, ALL_TYPE_NAMES)
+        .assertSuccessWithOutputLines(
+            "Instantiated",
+            "Not instantiated",
+            "Not instantiated",
+            "Not instantiated",
+            "Not instantiated");
+  }
+
+  @Test
+  public void allExeptBCompiledWithR8() throws Exception {
+    testForR8Partial(parameters.getBackend())
+        .setMinApi(parameters)
+        .addProgramClasses(ALL_CLASSES)
+        .setR8PartialConfiguration(
+            builder ->
+                builder
+                    .addJavaTypeIncludePattern(PKG1 + ".**")
+                    .addJavaTypeIncludePattern(PKG2 + ".**")
+                    .addJavaTypeExcludePattern(SUBPKG + ".*"))
+        .compile()
+        .inspectD8Input(
+            inspector -> assertTrue(inspector.hasExactlyProgramClasses(B.class, Main.class)))
+        .inspectR8Input(
+            inspector ->
+                assertTrue(
+                    inspector.hasExactlyProgramClasses(A1.class, A2.class, C1.class, C2.class)))
+        .inspect(inspector -> assertTrue(inspector.hasExactlyProgramClasses(B.class, Main.class)))
+        .run(parameters.getRuntime(), Main.class, ALL_TYPE_NAMES)
+        .assertSuccessWithOutputLines(
+            "Not instantiated",
+            "Not instantiated",
+            "Instantiated",
+            "Not instantiated",
+            "Not instantiated");
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) throws Exception {
+      for (String arg : args) {
+        try {
+          Class.forName(arg);
+          System.out.println("Instantiated");
+        } catch (ClassNotFoundException e) {
+          System.out.println("Not instantiated");
+        }
+      }
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/partial/PartialCompilationBasicTest.java b/src/test/java/com/android/tools/r8/partial/PartialCompilationBasicTest.java
index 662aa21..e55a8d5 100644
--- a/src/test/java/com/android/tools/r8/partial/PartialCompilationBasicTest.java
+++ b/src/test/java/com/android/tools/r8/partial/PartialCompilationBasicTest.java
@@ -39,12 +39,37 @@
         .setMinApi(parameters)
         .addProgramClasses(A.class, B.class, Main.class)
         .addKeepMainRule(Main.class)
-        .setR8PartialConfiguration(builder -> builder.includeAll().excludeClasses(A.class).build())
+        .setR8PartialConfiguration(builder -> builder.includeAll().excludeClasses(A.class))
         .compile()
+        .inspectR8Input(
+            inspector -> {
+              assertThat(inspector.programClass(A.class), isAbsent());
+              assertThat(inspector.programClass(B.class), isPresent());
+              assertThat(inspector.programClass(Main.class), isPresent());
+            })
+        .inspectD8Input(
+            inspector -> {
+              assertThat(inspector.programClass(A.class), isPresent());
+              assertThat(inspector.programClass(B.class), isAbsent());
+              assertThat(inspector.programClass(Main.class), isAbsent());
+            })
+        .inspectR8Output(
+            inspector -> {
+              assertThat(inspector.clazz(A.class), isAbsent());
+              assertThat(inspector.clazz(B.class), isAbsent());
+              assertThat(inspector.clazz(Main.class), isPresent());
+            })
+        .inspectD8Output(
+            inspector -> {
+              assertThat(inspector.clazz(A.class), isPresent());
+              assertThat(inspector.clazz(B.class), isAbsent());
+              assertThat(inspector.clazz(Main.class), isAbsent());
+            })
         .inspect(
             inspector -> {
               assertThat(inspector.clazz(A.class), isPresent());
               assertThat(inspector.clazz(B.class), isAbsent());
+              assertThat(inspector.clazz(Main.class), isPresent());
             })
         .run(parameters.getRuntime(), Main.class, getClass().getTypeName())
         .assertSuccessWithOutputLines("Instantiated", "Not instantiated");
@@ -56,12 +81,41 @@
         .setMinApi(parameters)
         .addProgramClasses(A.class, B.class, Main.class)
         .addKeepMainRule(Main.class)
-        .setR8PartialConfiguration(builder -> builder.includeAll().excludeClasses(B.class).build())
+        .setR8PartialConfiguration(builder -> builder.includeAll().excludeClasses(B.class))
         .compile()
+        .inspectR8Input(
+            inspector -> {
+              // TODO(b/309743298): These are all present as inspection currently also look at
+              //  classpath.
+              assertThat(inspector.programClass(A.class), isPresent());
+              assertThat(inspector.programClass(B.class), isAbsent());
+              assertThat(inspector.programClass(Main.class), isPresent());
+            })
+        .inspectD8Input(
+            inspector -> {
+              // TODO(b/309743298): These are all present as inspection currently also look at
+              //  classpath.
+              assertThat(inspector.programClass(A.class), isAbsent());
+              assertThat(inspector.programClass(B.class), isPresent());
+              assertThat(inspector.programClass(Main.class), isAbsent());
+            })
+        .inspectR8Output(
+            inspector -> {
+              assertThat(inspector.clazz(A.class), isAbsent());
+              assertThat(inspector.clazz(B.class), isAbsent());
+              assertThat(inspector.clazz(Main.class), isPresent());
+            })
+        .inspectD8Output(
+            inspector -> {
+              assertThat(inspector.clazz(A.class), isAbsent());
+              assertThat(inspector.clazz(B.class), isPresent());
+              assertThat(inspector.clazz(Main.class), isAbsent());
+            })
         .inspect(
             inspector -> {
               assertThat(inspector.clazz(A.class), isAbsent());
               assertThat(inspector.clazz(B.class), isPresent());
+              assertThat(inspector.clazz(Main.class), isPresent());
             })
         .run(parameters.getRuntime(), Main.class, getClass().getTypeName())
         .assertSuccessWithOutputLines("Not instantiated", "Instantiated");
diff --git a/src/test/java/com/android/tools/r8/partial/PartialCompilationDemoTest.java b/src/test/java/com/android/tools/r8/partial/PartialCompilationDemoTest.java
index 0ccd8ce..43ad334 100644
--- a/src/test/java/com/android/tools/r8/partial/PartialCompilationDemoTest.java
+++ b/src/test/java/com/android/tools/r8/partial/PartialCompilationDemoTest.java
@@ -236,10 +236,10 @@
   private void runR8Partial(Path tempDir, CompilerDump dump, Path output, Predicate<String> isR8)
       throws IOException, CompilationFailedException {
     testForR8Partial(parameters.getBackend())
-        .setR8PartialConfigurationPredicate(isR8)
+        .setR8PartialConfigurationJavaTypePredicate(isR8)
         .addOptionsModification(
             options -> {
-              options.r8PartialCompilationOptions.tempDir = tempDir;
+              options.partialCompilationConfiguration.setTempDir(tempDir);
 
               // For compiling nowonandroid.
               options.testing.allowUnnecessaryDontWarnWildcards = true;
diff --git a/src/test/java/com/android/tools/r8/partial/PartialShrinkingPreviewApiTest.java b/src/test/java/com/android/tools/r8/partial/PartialShrinkingPreviewApiTest.java
new file mode 100644
index 0000000..f5784c5
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/partial/PartialShrinkingPreviewApiTest.java
@@ -0,0 +1,78 @@
+// Copyright (c) 2024, 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.partial;
+
+import static com.android.tools.r8.MarkerMatcher.markerMinApi;
+import static com.android.tools.r8.MarkerMatcher.markerR8Mode;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+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.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.AndroidApiLevel;
+import com.android.tools.r8.utils.ThrowingConsumer;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.nio.file.Path;
+import org.hamcrest.CoreMatchers;
+import org.junit.Test;
+
+public class PartialShrinkingPreviewApiTest extends CompilerApiTestRunner {
+
+  public static final int MIN_API_LEVEL = 31;
+
+  public PartialShrinkingPreviewApiTest(TestParameters parameters) {
+    super(parameters);
+  }
+
+  @Override
+  public Class<? extends CompilerApiTest> binaryTestClass() {
+    return ApiTest.class;
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    ApiTest test = new ApiTest(ApiTest.PARAMETERS);
+    runTest(test::runR8);
+  }
+
+  private void runTest(ThrowingConsumer<ProgramConsumer, Exception> test) throws Exception {
+    Path output = temp.newFolder().toPath().resolve("out.jar");
+    test.accept(new DexIndexedConsumer.ArchiveConsumer(output));
+    assertThat(
+        new CodeInspector(output).getMarkers(),
+        CoreMatchers.everyItem(
+            CoreMatchers.allOf(
+                markerMinApi(AndroidApiLevel.getAndroidApiLevel(MIN_API_LEVEL)),
+                markerR8Mode("full"))));
+  }
+
+  public static class ApiTest extends CompilerApiTest {
+
+    public ApiTest(Object parameters) {
+      super(parameters);
+    }
+
+    public void runR8(ProgramConsumer programConsumer) throws Exception {
+      R8.run(
+          R8Command.builder()
+              .addClassProgramData(getBytesForClass(getMockClass()), Origin.unknown())
+              .addProguardConfiguration(getKeepMainRules(getMockClass()), Origin.unknown())
+              .addLibraryFiles(getJava8RuntimeJar())
+              .setProgramConsumer(programConsumer)
+              .enableExperimentalPartialShrinking("**", null)
+              .setMinApiLevel(MIN_API_LEVEL)
+              .build());
+    }
+
+    @Test
+    public void testR8() throws Exception {
+      runR8(DexIndexedConsumer.emptyConsumer());
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/partial/pkg1/A1.java b/src/test/java/com/android/tools/r8/partial/pkg1/A1.java
new file mode 100644
index 0000000..e435721
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/partial/pkg1/A1.java
@@ -0,0 +1,6 @@
+// Copyright (c) 2024, 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.partial.pkg1;
+
+public class A1 {}
diff --git a/src/test/java/com/android/tools/r8/partial/pkg1/A2.java b/src/test/java/com/android/tools/r8/partial/pkg1/A2.java
new file mode 100644
index 0000000..7cac5c2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/partial/pkg1/A2.java
@@ -0,0 +1,6 @@
+// Copyright (c) 2024, 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.partial.pkg1;
+
+public class A2 {}
diff --git a/src/test/java/com/android/tools/r8/partial/pkg1/subpkg/B.java b/src/test/java/com/android/tools/r8/partial/pkg1/subpkg/B.java
new file mode 100644
index 0000000..ee4cb46
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/partial/pkg1/subpkg/B.java
@@ -0,0 +1,6 @@
+// Copyright (c) 2024, 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.partial.pkg1.subpkg;
+
+public class B {}
diff --git a/src/test/java/com/android/tools/r8/partial/pkg2/C1.java b/src/test/java/com/android/tools/r8/partial/pkg2/C1.java
new file mode 100644
index 0000000..a6225c1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/partial/pkg2/C1.java
@@ -0,0 +1,6 @@
+// Copyright (c) 2024, 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.partial.pkg2;
+
+public class C1 {}
diff --git a/src/test/java/com/android/tools/r8/partial/pkg2/C2.java b/src/test/java/com/android/tools/r8/partial/pkg2/C2.java
new file mode 100644
index 0000000..139666a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/partial/pkg2/C2.java
@@ -0,0 +1,6 @@
+// Copyright (c) 2024, 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.partial.pkg2;
+
+public class C2 {}
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/TwrCloseResourceDuplicationProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/TwrCloseResourceDuplicationProfileRewritingTest.java
index f840c33f..3bb5e4c 100644
--- a/src/test/java/com/android/tools/r8/profile/art/completeness/TwrCloseResourceDuplicationProfileRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/TwrCloseResourceDuplicationProfileRewritingTest.java
@@ -98,6 +98,7 @@
                 ImmutableList.of(Reference.classFromClass(String.class)),
                 null))
         .addMethodRule(
+            // NOTE: The $closeResource helper is _only_ generated by the JDK-9 compiler.
             Reference.method(
                 FOO.getClassReference(), "$closeResource", closeResourceFormalParameters, null))
         .addMethodRule(
@@ -107,6 +108,7 @@
                 ImmutableList.of(Reference.classFromClass(String.class)),
                 null))
         .addMethodRule(
+            // NOTE: The $closeResource helper is _only_ generated by the JDK-9 compiler.
             Reference.method(
                 BAR.getClassReference(), "$closeResource", closeResourceFormalParameters, null))
         .build();
diff --git a/src/test/testbase/java/com/android/tools/r8/R8PartialTestBuilder.java b/src/test/testbase/java/com/android/tools/r8/R8PartialTestBuilder.java
index 319b633..3dacb92 100644
--- a/src/test/testbase/java/com/android/tools/r8/R8PartialTestBuilder.java
+++ b/src/test/testbase/java/com/android/tools/r8/R8PartialTestBuilder.java
@@ -10,23 +10,17 @@
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.Box;
 import com.android.tools.r8.utils.InternalOptions;
-import com.google.common.base.Predicates;
-import com.google.common.collect.ImmutableList;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
+import com.android.tools.r8.utils.R8PartialCompilationConfiguration;
 import java.util.List;
 import java.util.function.Consumer;
-import java.util.function.Function;
 import java.util.function.Predicate;
 import java.util.function.Supplier;
-import java.util.stream.Collectors;
 
 public class R8PartialTestBuilder
     extends R8TestBuilder<R8PartialTestCompileResult, R8TestRunResult, R8PartialTestBuilder> {
 
-  private R8PartialConfiguration r8PartialConfiguration =
-      R8PartialConfiguration.defaultConfiguration();
+  private R8PartialCompilationConfiguration r8PartialConfiguration =
+      R8PartialCompilationConfiguration.disabledConfiguration();
 
   private R8PartialTestBuilder(TestState state, Builder builder, Backend backend) {
     super(state, builder, backend);
@@ -52,106 +46,30 @@
     return this;
   }
 
-  public static class R8PartialConfiguration implements Predicate<String> {
-    private static final R8PartialConfiguration defaultConfiguration =
-        new R8PartialConfiguration(ImmutableList.of(), ImmutableList.of());
-    private final List<Predicate<String>> includePredicates;
-    private final List<Predicate<String>> excludePredicates;
-
-    public R8PartialConfiguration(
-        List<Predicate<String>> includePredicates, List<Predicate<String>> excludePredicates) {
-      this.includePredicates = includePredicates;
-      this.excludePredicates = excludePredicates;
-    }
-
-    private static R8PartialConfiguration defaultConfiguration() {
-      return defaultConfiguration;
-    }
-
-    public static Builder builder() {
-      return new Builder();
-    }
-
-    public boolean test(String name) {
-      for (Predicate<String> isR8ClassPredicate : includePredicates) {
-        if (isR8ClassPredicate.test(name)) {
-          for (Predicate<String> isD8ClassPredicate : excludePredicates) {
-            if (isD8ClassPredicate.test(name)) {
-              return false;
-            }
-          }
-          return true;
-        }
-      }
-      return false;
-    }
-
-    public static class Builder {
-      private final List<Predicate<String>> includePredicates = new ArrayList<>();
-      private final List<Predicate<String>> excludePredicates = new ArrayList<>();
-
-      public R8PartialConfiguration build() {
-        return new R8PartialConfiguration(includePredicates, excludePredicates);
-      }
-
-      public Builder includeAll() {
-        includePredicates.add(Predicates.alwaysTrue());
-        return this;
-      }
-
-      public Builder includeClasses(Class<?>... classes) {
-        return includeClasses(Arrays.asList(classes));
-      }
-
-      public Builder includeClasses(Collection<Class<?>> classes) {
-        Collection<String> typeNames =
-            classes.stream().map(Class::getTypeName).collect(Collectors.toList());
-        includePredicates.add(typeNames::contains);
-        return this;
-      }
-
-      public Builder include(Predicate<String> include) {
-        includePredicates.add(include);
-        return this;
-      }
-
-      public Builder excludeClasses(Class<?>... classes) {
-        return excludeClasses(Arrays.asList(classes));
-      }
-
-      public Builder excludeClasses(Collection<Class<?>> classes) {
-        Collection<String> typeNames =
-            classes.stream().map(Class::getTypeName).collect(Collectors.toList());
-        excludePredicates.add(typeNames::contains);
-        return this;
-      }
-
-      public Builder exclude(Predicate<String> exclude) {
-        excludePredicates.add(exclude);
-        return this;
-      }
-    }
-  }
-
-  public R8PartialTestBuilder setR8PartialConfigurationPredicate(Predicate<String> include) {
-    assert r8PartialConfiguration == R8PartialConfiguration.defaultConfiguration()
+  public R8PartialTestBuilder setR8PartialConfigurationJavaTypePredicate(
+      Predicate<String> include) {
+    assert r8PartialConfiguration.equals(R8PartialCompilationConfiguration.disabledConfiguration())
         : "Overwriting configuration...?";
-    r8PartialConfiguration = R8PartialConfiguration.builder().include(include).build();
+    r8PartialConfiguration =
+        R8PartialCompilationConfiguration.builder().includeJavaType(include).build();
     return self();
   }
 
-  public R8PartialTestBuilder setR8PartialConfiguration(R8PartialConfiguration configuration) {
-    assert r8PartialConfiguration == R8PartialConfiguration.defaultConfiguration()
+  public R8PartialTestBuilder setR8PartialConfiguration(
+      R8PartialCompilationConfiguration configuration) {
+    assert r8PartialConfiguration.equals(R8PartialCompilationConfiguration.disabledConfiguration())
         : "Overwriting configuration...?";
     r8PartialConfiguration = configuration;
     return self();
   }
 
   public R8PartialTestBuilder setR8PartialConfiguration(
-      Function<R8PartialConfiguration.Builder, R8PartialConfiguration> fn) {
-    assert r8PartialConfiguration == R8PartialConfiguration.defaultConfiguration()
+      Consumer<R8PartialCompilationConfiguration.Builder> consumer) {
+    assert r8PartialConfiguration.equals(R8PartialCompilationConfiguration.disabledConfiguration())
         : "Overwriting configuration...?";
-    r8PartialConfiguration = fn.apply(R8PartialConfiguration.builder());
+    R8PartialCompilationConfiguration.Builder builder = R8PartialCompilationConfiguration.builder();
+    consumer.accept(builder);
+    r8PartialConfiguration = builder.build();
     return self();
   }
 
@@ -165,12 +83,19 @@
       Box<List<ProguardConfigurationRule>> syntheticProguardRulesConsumer,
       StringBuilder proguardMapBuilder)
       throws CompilationFailedException {
+    Box<AndroidApp> r8InputAppBox = new Box<>();
+    Box<AndroidApp> d8InputAppBox = new Box<>();
+    Box<AndroidApp> r8OutputAppBox = new Box<>();
+    Box<AndroidApp> d8OutputAppBox = new Box<>();
     Consumer<InternalOptions> configureR8PartialCompilation =
         options -> {
-          options.r8PartialCompilationOptions.enabled = true;
-          options.r8PartialCompilationOptions.isR8 = r8PartialConfiguration;
+          options.partialCompilationConfiguration = r8PartialConfiguration;
+          options.partialCompilationConfiguration.r8InputAppConsumer = r8InputAppBox::set;
+          options.partialCompilationConfiguration.d8InputAppConsumer = d8InputAppBox::set;
+          options.partialCompilationConfiguration.r8OutputAppConsumer = r8OutputAppBox::set;
+          options.partialCompilationConfiguration.d8OutputAppConsumer = d8OutputAppBox::set;
         };
-    ToolHelper.runAndBenchmarkR8WithoutResult(
+    ToolHelper.runAndBenchmarkR8PartialWithoutResult(
         builder, configureR8PartialCompilation.andThen(optionsConsumer), benchmarkResults);
     return new R8PartialTestCompileResult(
         getState(),
@@ -186,6 +111,10 @@
         residualArtProfiles,
         resourceShrinkerOutput,
         resourceShrinkerOutputForFeatures,
-        buildMetadata != null ? buildMetadata.get() : null);
+        buildMetadata != null ? buildMetadata.get() : null,
+        r8InputAppBox.get(),
+        d8InputAppBox.get(),
+        r8OutputAppBox.get(),
+        d8OutputAppBox.get());
   }
 }
diff --git a/src/test/testbase/java/com/android/tools/r8/R8PartialTestCompileResult.java b/src/test/testbase/java/com/android/tools/r8/R8PartialTestCompileResult.java
index 18f75fb..2ff5569 100644
--- a/src/test/testbase/java/com/android/tools/r8/R8PartialTestCompileResult.java
+++ b/src/test/testbase/java/com/android/tools/r8/R8PartialTestCompileResult.java
@@ -8,13 +8,23 @@
 import com.android.tools.r8.shaking.CollectingGraphConsumer;
 import com.android.tools.r8.shaking.ProguardConfigurationRule;
 import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.ThrowingConsumer;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.io.IOException;
 import java.nio.file.Path;
 import java.util.HashMap;
 import java.util.List;
+import java.util.function.Consumer;
 
 public class R8PartialTestCompileResult
     extends R8TestCompileResultBase<R8PartialTestCompileResult> {
 
+  private final AndroidApp r8InputApp;
+  private final AndroidApp d8InputApp;
+  private final AndroidApp r8OutputApp;
+  private final AndroidApp d8OutputApp;
+
   R8PartialTestCompileResult(
       TestState state,
       OutputMode outputMode,
@@ -29,7 +39,11 @@
       List<ExternalArtProfile> residualArtProfiles,
       Path resourceShrinkerOutput,
       HashMap<String, Path> resourceShrinkerOutputForFeatures,
-      R8BuildMetadata buildMetadata) {
+      R8BuildMetadata buildMetadata,
+      AndroidApp r8InputApp,
+      AndroidApp d8InputApp,
+      AndroidApp r8OutputApp,
+      AndroidApp d8OutputApp) {
     super(
         state,
         outputMode,
@@ -45,10 +59,74 @@
         resourceShrinkerOutput,
         resourceShrinkerOutputForFeatures,
         buildMetadata);
+    this.r8InputApp = r8InputApp;
+    this.d8InputApp = d8InputApp;
+    this.r8OutputApp = r8OutputApp;
+    this.d8OutputApp = d8OutputApp;
   }
 
   @Override
   public R8PartialTestCompileResult self() {
     return this;
   }
+
+  public CodeInspector inspectorR8Input() throws IOException {
+    return new CodeInspector(r8InputApp);
+  }
+
+  public CodeInspector inspectorR8Input(Consumer<InternalOptions> debugOptionsConsumer)
+      throws IOException {
+    return new CodeInspector(r8InputApp, debugOptionsConsumer);
+  }
+
+  public <E extends Throwable> R8PartialTestCompileResult inspectR8Input(
+      ThrowingConsumer<CodeInspector, E> consumer) throws IOException, E {
+    consumer.accept(inspectorR8Input());
+    return self();
+  }
+
+  public CodeInspector inspectorD8Input() throws IOException {
+    return new CodeInspector(d8InputApp);
+  }
+
+  public CodeInspector inspectorD8Input(Consumer<InternalOptions> debugOptionsConsumer)
+      throws IOException {
+    return new CodeInspector(d8InputApp, debugOptionsConsumer);
+  }
+
+  public <E extends Throwable> R8PartialTestCompileResult inspectD8Input(
+      ThrowingConsumer<CodeInspector, E> consumer) throws IOException, E {
+    consumer.accept(inspectorD8Input());
+    return self();
+  }
+
+  public CodeInspector inspectorR8Output() throws IOException {
+    return new CodeInspector(r8OutputApp);
+  }
+
+  public CodeInspector inspectorR8Output(Consumer<InternalOptions> debugOptionsConsumer)
+      throws IOException {
+    return new CodeInspector(r8OutputApp, debugOptionsConsumer);
+  }
+
+  public <E extends Throwable> R8PartialTestCompileResult inspectR8Output(
+      ThrowingConsumer<CodeInspector, E> consumer) throws IOException, E {
+    consumer.accept(inspectorR8Output());
+    return self();
+  }
+
+  public CodeInspector inspectorD8Output() throws IOException {
+    return new CodeInspector(d8OutputApp);
+  }
+
+  public CodeInspector inspectorD8Output(Consumer<InternalOptions> debugOptionsConsumer)
+      throws IOException {
+    return new CodeInspector(d8OutputApp, debugOptionsConsumer);
+  }
+
+  public <E extends Throwable> R8PartialTestCompileResult inspectD8Output(
+      ThrowingConsumer<CodeInspector, E> consumer) throws IOException, E {
+    consumer.accept(inspectorD8Output());
+    return self();
+  }
 }
diff --git a/src/test/testbase/java/com/android/tools/r8/ToolHelper.java b/src/test/testbase/java/com/android/tools/r8/ToolHelper.java
index 1a8a049..0608acb 100644
--- a/src/test/testbase/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/testbase/java/com/android/tools/r8/ToolHelper.java
@@ -1807,6 +1807,28 @@
     }
   }
 
+  public static void runAndBenchmarkR8PartialWithoutResult(
+      R8Command.Builder commandBuilder,
+      Consumer<InternalOptions> optionsConsumer,
+      BenchmarkResults benchmarkResults)
+      throws CompilationFailedException {
+    long start = 0;
+    if (benchmarkResults != null) {
+      start = System.nanoTime();
+    }
+    R8Command command = commandBuilder.build();
+    InternalOptions internalOptions = command.getInternalOptions();
+    optionsConsumer.accept(internalOptions);
+    try {
+      R8Partial.runForTesting(command.getInputApp(), internalOptions);
+    } finally {
+      if (benchmarkResults != null) {
+        long end = System.nanoTime();
+        benchmarkResults.addRuntimeResult(end - start);
+      }
+    }
+  }
+
   public static AndroidApp runR8WithFullResult(
       R8Command command, Consumer<InternalOptions> optionsConsumer)
       throws CompilationFailedException {
diff --git a/src/test/testbase/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java b/src/test/testbase/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
index 7c06d42..d9af1e0 100644
--- a/src/test/testbase/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
+++ b/src/test/testbase/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
@@ -4,6 +4,8 @@
 package com.android.tools.r8.utils.codeinspector;
 
 import static com.android.tools.r8.utils.ConsumerUtils.emptyConsumer;
+import static com.android.tools.r8.utils.codeinspector.CodeInspector.ClassType.ANY;
+import static com.android.tools.r8.utils.codeinspector.CodeInspector.ClassType.PROGRAM;
 
 import com.android.tools.r8.DexIndexedConsumer;
 import com.android.tools.r8.StringResource;
@@ -45,6 +47,7 @@
 import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.BiMapContainer;
+import com.android.tools.r8.utils.BooleanBox;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.Timing;
@@ -61,6 +64,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.function.BiConsumer;
 import java.util.function.BiFunction;
 import java.util.function.Consumer;
@@ -78,6 +82,11 @@
   final Map<String, String> obfuscatedToOriginalMapping;
   private Retracer lazyRetracer = null;
 
+  public enum ClassType {
+    PROGRAM,
+    ANY
+  }
+
   public static MethodSignature MAIN =
       new MethodSignature("main", "void", new String[] {"java.lang.String[]"});
 
@@ -293,6 +302,10 @@
     return rewriter.getSignature();
   }
 
+  public ClassSubject clazz(Class<?> clazz, ClassType classType) {
+    return clazz(Reference.classFromClass(clazz), classType);
+  }
+
   public ClassSubject clazz(Class<?> clazz) {
     return clazz(Reference.classFromClass(clazz));
   }
@@ -302,6 +315,10 @@
     return clazz(Reference.classFromTypeName(name));
   }
 
+  public ClassSubject programClass(Class<?> clazz) {
+    return clazz(Reference.classFromClass(clazz), PROGRAM);
+  }
+
   public ClassNameMapper getMapping() {
     return mapping;
   }
@@ -355,7 +372,7 @@
     }
   }
 
-  public ClassSubject clazz(ClassReference reference) {
+  public ClassSubject clazz(ClassReference reference, ClassType classType) {
     String descriptor = reference.getDescriptor();
     String name = DescriptorUtils.descriptorToJavaType(descriptor);
     ClassNamingForNameMapper naming = null;
@@ -374,13 +391,20 @@
         }
       }
     }
-    DexClass clazz = application.definitionFor(toDexTypeIgnorePrimitives(name));
+    DexClass clazz =
+        classType == ANY
+            ? application.definitionFor(toDexTypeIgnorePrimitives(name))
+            : application.programDefinitionFor(toDexTypeIgnorePrimitives(name));
     if (clazz == null) {
       return new AbsentClassSubject(this, reference);
     }
     return new FoundClassSubject(this, clazz, MappingWrapper.create(mapping, naming), reference);
   }
 
+  public ClassSubject clazz(ClassReference reference) {
+    return clazz(reference, ANY);
+  }
+
   public ClassSubject companionClassFor(Class<?> clazz) {
     return clazz(SyntheticItemsTestUtils.syntheticCompanionClass(clazz));
   }
@@ -402,6 +426,18 @@
     return builder.build();
   }
 
+  public boolean hasExactlyProgramClasses(Class<?>... classes) {
+    return hasExactlyProgramClasses(Arrays.asList(classes));
+  }
+
+  public boolean hasExactlyProgramClasses(Collection<Class<?>> classes) {
+    Set<ClassReference> descriptors =
+        classes.stream().map(Reference::classFromClass).collect(Collectors.toSet());
+    BooleanBox allFound = new BooleanBox(true);
+    forAllClasses(clazz -> allFound.and(descriptors.remove(clazz.reference)));
+    return descriptors.isEmpty() && allFound.get();
+  }
+
   public Stream<InstructionSubject> streamInstructions() {
     return allClasses().stream()
         .flatMap(cls -> cls.allMethods(MethodSubject::hasCode).stream())