Merge commit '2696206a222cb22370975bd0eae821fd7fc0c257' into dev-release

Change-Id: Id737bba21e7f2d283247cf656a0812e54831acd8
diff --git a/infra/config/global/generated/cr-buildbucket.cfg b/infra/config/global/generated/cr-buildbucket.cfg
index 7f15036..d84a61b 100644
--- a/infra/config/global/generated/cr-buildbucket.cfg
+++ b/infra/config/global/generated/cr-buildbucket.cfg
@@ -964,6 +964,82 @@
       }
     }
     builders {
+      name: "linux-android-7-noble"
+      swarming_host: "chrome-swarming.appspot.com"
+      swarming_tags: "vpython:native-python-wrapper"
+      dimensions: "cpu:x86-64"
+      dimensions: "normal:true"
+      dimensions: "os:Ubuntu-24.04"
+      dimensions: "pool:luci.r8.ci"
+      exe {
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
+        cmd: "luciexe"
+      }
+      properties:
+        '{'
+        '  "builder_group": "internal.client.r8",'
+        '  "recipe": "rex",'
+        '  "test_options": ['
+        '    "--dex_vm=7.0.0",'
+        '    "--all_tests",'
+        '    "--command_cache_dir=/tmp/ccache",'
+        '    "--tool=r8",'
+        '    "--print-times",'
+        '    "--no_internal",'
+        '    "--one_line_per_test",'
+        '    "--archive_failures"'
+        '  ]'
+        '}'
+      priority: 26
+      execution_timeout_secs: 21600
+      expiration_secs: 126000
+      build_numbers: YES
+      service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
+      experiments {
+        key: "luci.recipes.use_python3"
+        value: 100
+      }
+    }
+    builders {
+      name: "linux-android-7-noble_release"
+      swarming_host: "chrome-swarming.appspot.com"
+      swarming_tags: "vpython:native-python-wrapper"
+      dimensions: "cpu:x86-64"
+      dimensions: "normal:true"
+      dimensions: "os:Ubuntu-24.04"
+      dimensions: "pool:luci.r8.ci"
+      exe {
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
+        cmd: "luciexe"
+      }
+      properties:
+        '{'
+        '  "builder_group": "internal.client.r8",'
+        '  "recipe": "rex",'
+        '  "test_options": ['
+        '    "--dex_vm=7.0.0",'
+        '    "--all_tests",'
+        '    "--command_cache_dir=/tmp/ccache",'
+        '    "--tool=r8",'
+        '    "--print-times",'
+        '    "--no_internal",'
+        '    "--one_line_per_test",'
+        '    "--archive_failures"'
+        '  ]'
+        '}'
+      priority: 26
+      execution_timeout_secs: 21600
+      expiration_secs: 126000
+      build_numbers: YES
+      service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
+      experiments {
+        key: "luci.recipes.use_python3"
+        value: 100
+      }
+    }
+    builders {
       name: "linux-android-7_release"
       swarming_host: "chrome-swarming.appspot.com"
       swarming_tags: "vpython:native-python-wrapper"
@@ -1191,6 +1267,80 @@
       }
     }
     builders {
+      name: "linux-default-noble"
+      swarming_host: "chrome-swarming.appspot.com"
+      swarming_tags: "vpython:native-python-wrapper"
+      dimensions: "cpu:x86-64"
+      dimensions: "normal:true"
+      dimensions: "os:Ubuntu-24.04"
+      dimensions: "pool:luci.r8.ci"
+      exe {
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
+        cmd: "luciexe"
+      }
+      properties:
+        '{'
+        '  "builder_group": "internal.client.r8",'
+        '  "recipe": "rex",'
+        '  "test_options": ['
+        '    "--runtimes=dex-default",'
+        '    "--command_cache_dir=/tmp/ccache",'
+        '    "--tool=r8",'
+        '    "--print-times",'
+        '    "--no_internal",'
+        '    "--one_line_per_test",'
+        '    "--archive_failures"'
+        '  ]'
+        '}'
+      priority: 26
+      execution_timeout_secs: 21600
+      expiration_secs: 126000
+      build_numbers: YES
+      service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
+      experiments {
+        key: "luci.recipes.use_python3"
+        value: 100
+      }
+    }
+    builders {
+      name: "linux-default-noble_release"
+      swarming_host: "chrome-swarming.appspot.com"
+      swarming_tags: "vpython:native-python-wrapper"
+      dimensions: "cpu:x86-64"
+      dimensions: "normal:true"
+      dimensions: "os:Ubuntu-24.04"
+      dimensions: "pool:luci.r8.ci"
+      exe {
+        cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+        cipd_version: "refs/heads/main"
+        cmd: "luciexe"
+      }
+      properties:
+        '{'
+        '  "builder_group": "internal.client.r8",'
+        '  "recipe": "rex",'
+        '  "test_options": ['
+        '    "--runtimes=dex-default",'
+        '    "--command_cache_dir=/tmp/ccache",'
+        '    "--tool=r8",'
+        '    "--print-times",'
+        '    "--no_internal",'
+        '    "--one_line_per_test",'
+        '    "--archive_failures"'
+        '  ]'
+        '}'
+      priority: 26
+      execution_timeout_secs: 21600
+      expiration_secs: 126000
+      build_numbers: YES
+      service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
+      experiments {
+        key: "luci.recipes.use_python3"
+        value: 100
+      }
+    }
+    builders {
       name: "linux-default_release"
       swarming_host: "chrome-swarming.appspot.com"
       swarming_tags: "vpython:native-python-wrapper"
diff --git a/infra/config/global/generated/luci-milo.cfg b/infra/config/global/generated/luci-milo.cfg
index 7ee27d7..1376a59 100644
--- a/infra/config/global/generated/luci-milo.cfg
+++ b/infra/config/global/generated/luci-milo.cfg
@@ -26,6 +26,11 @@
     short_name: "default"
   }
   builders {
+    name: "buildbucket/luci.r8.ci/linux-default-noble"
+    category: "R8"
+    short_name: "noble"
+  }
+  builders {
     name: "buildbucket/luci.r8.ci/linux-none"
     category: "R8"
     short_name: "none"
@@ -81,6 +86,11 @@
     short_name: "7"
   }
   builders {
+    name: "buildbucket/luci.r8.ci/linux-android-7-noble"
+    category: "R8"
+    short_name: "noble"
+  }
+  builders {
     name: "buildbucket/luci.r8.ci/linux-android-8"
     category: "R8"
     short_name: "8"
@@ -181,6 +191,11 @@
     short_name: "default"
   }
   builders {
+    name: "buildbucket/luci.r8.ci/linux-default-noble_release"
+    category: "Release|R8"
+    short_name: "noble"
+  }
+  builders {
     name: "buildbucket/luci.r8.ci/linux-none_release"
     category: "Release|R8"
     short_name: "none"
@@ -236,6 +251,11 @@
     short_name: "7"
   }
   builders {
+    name: "buildbucket/luci.r8.ci/linux-android-7-noble_release"
+    category: "Release|R8"
+    short_name: "noble"
+  }
+  builders {
     name: "buildbucket/luci.r8.ci/linux-android-8_release"
     category: "Release|R8"
     short_name: "8"
diff --git a/infra/config/global/generated/luci-notify.cfg b/infra/config/global/generated/luci-notify.cfg
index f072a67..e868b2c 100644
--- a/infra/config/global/generated/luci-notify.cfg
+++ b/infra/config/global/generated/luci-notify.cfg
@@ -288,6 +288,30 @@
   }
   builders {
     bucket: "ci"
+    name: "linux-android-7-noble"
+    repository: "https://r8.googlesource.com/r8"
+  }
+}
+notifiers {
+  notifications {
+    on_failure: true
+    on_new_failure: true
+    notify_blamelist {}
+  }
+  builders {
+    bucket: "ci"
+    name: "linux-android-7-noble_release"
+    repository: "https://r8.googlesource.com/r8"
+  }
+}
+notifiers {
+  notifications {
+    on_failure: true
+    on_new_failure: true
+    notify_blamelist {}
+  }
+  builders {
+    bucket: "ci"
     name: "linux-android-7_release"
     repository: "https://r8.googlesource.com/r8"
   }
@@ -360,6 +384,30 @@
   }
   builders {
     bucket: "ci"
+    name: "linux-default-noble"
+    repository: "https://r8.googlesource.com/r8"
+  }
+}
+notifiers {
+  notifications {
+    on_failure: true
+    on_new_failure: true
+    notify_blamelist {}
+  }
+  builders {
+    bucket: "ci"
+    name: "linux-default-noble_release"
+    repository: "https://r8.googlesource.com/r8"
+  }
+}
+notifiers {
+  notifications {
+    on_failure: true
+    on_new_failure: true
+    notify_blamelist {}
+  }
+  builders {
+    bucket: "ci"
     name: "linux-default_release"
     repository: "https://r8.googlesource.com/r8"
   }
diff --git a/infra/config/global/generated/luci-scheduler.cfg b/infra/config/global/generated/luci-scheduler.cfg
index 04e6f0f..d7f8189 100644
--- a/infra/config/global/generated/luci-scheduler.cfg
+++ b/infra/config/global/generated/luci-scheduler.cfg
@@ -384,6 +384,35 @@
   }
 }
 job {
+  id: "linux-android-7-noble"
+  realm: "ci"
+  acl_sets: "ci"
+  triggering_policy {
+    kind: GREEDY_BATCHING
+    max_concurrent_invocations: 1
+  }
+  buildbucket {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "ci"
+    builder: "linux-android-7-noble"
+  }
+}
+job {
+  id: "linux-android-7-noble_release"
+  realm: "ci"
+  acl_sets: "ci"
+  triggering_policy {
+    kind: GREEDY_BATCHING
+    max_concurrent_invocations: 1
+    max_batch_size: 1
+  }
+  buildbucket {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "ci"
+    builder: "linux-android-7-noble_release"
+  }
+}
+job {
   id: "linux-android-7_release"
   realm: "ci"
   acl_sets: "ci"
@@ -471,6 +500,35 @@
   }
 }
 job {
+  id: "linux-default-noble"
+  realm: "ci"
+  acl_sets: "ci"
+  triggering_policy {
+    kind: GREEDY_BATCHING
+    max_concurrent_invocations: 1
+  }
+  buildbucket {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "ci"
+    builder: "linux-default-noble"
+  }
+}
+job {
+  id: "linux-default-noble_release"
+  realm: "ci"
+  acl_sets: "ci"
+  triggering_policy {
+    kind: GREEDY_BATCHING
+    max_concurrent_invocations: 1
+    max_batch_size: 1
+  }
+  buildbucket {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "ci"
+    builder: "linux-default-noble_release"
+  }
+}
+job {
   id: "linux-default_release"
   realm: "ci"
   acl_sets: "ci"
@@ -866,9 +924,11 @@
   triggers: "linux-android-4.4_release"
   triggers: "linux-android-5_release"
   triggers: "linux-android-6_release"
+  triggers: "linux-android-7-noble_release"
   triggers: "linux-android-7_release"
   triggers: "linux-android-8_release"
   triggers: "linux-android-9_release"
+  triggers: "linux-default-noble_release"
   triggers: "linux-default_release"
   triggers: "linux-internal_release"
   triggers: "linux-jdk11_release"
@@ -901,9 +961,11 @@
   triggers: "linux-android-5"
   triggers: "linux-android-6"
   triggers: "linux-android-7"
+  triggers: "linux-android-7-noble"
   triggers: "linux-android-8"
   triggers: "linux-android-9"
   triggers: "linux-default"
+  triggers: "linux-default-noble"
   triggers: "linux-dev"
   triggers: "linux-internal"
   triggers: "linux-jdk11"
diff --git a/infra/config/global/generated/project.cfg b/infra/config/global/generated/project.cfg
index ade4005..ede4677 100644
--- a/infra/config/global/generated/project.cfg
+++ b/infra/config/global/generated/project.cfg
@@ -7,7 +7,7 @@
 name: "r8"
 access: "group:all"
 lucicfg {
-  version: "1.44.1"
+  version: "1.45.0"
   package_dir: ".."
   config_dir: "generated"
   entry_point: "main.star"
diff --git a/infra/config/global/main.star b/infra/config/global/main.star
index 41a31c4..e0e07e5 100755
--- a/infra/config/global/main.star
+++ b/infra/config/global/main.star
@@ -168,7 +168,7 @@
 
 default_timeout = time.hour * 6
 
-def get_dimensions(windows = False, internal = False, archive = False):
+def get_dimensions(windows = False, internal = False, archive = False, noble=False):
     # We use the following setup:
     #   windows -> always windows machine
     #   internal -> always internal, single small, machine
@@ -181,7 +181,10 @@
     if windows:
         dimensions["os"] = "Windows-10"
     else:
-        dimensions["os"] = "Ubuntu-20.04"
+        if noble:
+            dimensions["os"] = "Ubuntu-24.04"
+        else:
+            dimensions["os"] = "Ubuntu-20.04"
     if internal:
         dimensions["internal"] = "true"
     elif archive:
@@ -362,6 +365,11 @@
     max_concurrent_invocations = 2,
 )
 r8_tester_with_default(
+    "linux-default-noble",
+    ["--runtimes=dex-default", "--command_cache_dir=/tmp/ccache"],
+    dimensions = get_dimensions(noble=True),
+)
+r8_tester_with_default(
     "linux-none",
     ["--runtimes=none", "--command_cache_dir=/tmp/ccache"],
     max_concurrent_invocations = 2,
@@ -409,6 +417,13 @@
     "linux-android-7",
     ["--dex_vm=7.0.0", "--all_tests", "--command_cache_dir=/tmp/ccache"],
 )
+
+r8_tester_with_default(
+    "linux-android-7-noble",
+    ["--dex_vm=7.0.0", "--all_tests", "--command_cache_dir=/tmp/ccache"],
+    dimensions = get_dimensions(noble=True),
+)
+
 r8_tester_with_default(
     "linux-android-8",
     ["--dex_vm=8.1.0", "--all_tests", "--command_cache_dir=/tmp/ccache"],
diff --git a/src/main/java/com/android/tools/r8/GenerateMainDexList.java b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
index a3b18e5..fbaa1ec 100644
--- a/src/main/java/com/android/tools/r8/GenerateMainDexList.java
+++ b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
@@ -84,7 +84,7 @@
     MainDexRootSet mainDexRootSet =
         MainDexRootSet.builder(
                 appView, profileCollectionAdditions, subtypingInfo, options.mainDexKeepRules)
-            .build(executor);
+            .evaluateRulesAndBuild(executor);
     appView.setMainDexRootSet(mainDexRootSet);
 
     GraphConsumer graphConsumer = options.mainDexKeptGraphConsumer;
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index b148ad9..57d52fa 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -372,8 +372,9 @@
                     Iterables.concat(
                         options.getProguardConfiguration().getRules(), synthesizedProguardRules))
                 .setAssumeInfoCollectionBuilder(assumeInfoCollectionBuilder)
+                .evaluateRules(executorService)
                 .tracePartialCompilationDexingOutputClasses(executorService)
-                .build(executorService));
+                .build());
         appView.setAssumeInfoCollection(assumeInfoCollectionBuilder.build());
 
         // Compute the main dex rootset that will be the base of first and final main dex tracing
@@ -384,7 +385,7 @@
           MainDexRootSet mainDexRootSet =
               MainDexRootSet.builder(
                       appView, profileCollectionAdditions, subtypingInfo, options.mainDexKeepRules)
-                  .build(executorService);
+                  .evaluateRulesAndBuild(executorService);
           appView.setMainDexRootSet(mainDexRootSet);
           appView.appInfo().unsetObsolete();
         }
diff --git a/src/main/java/com/android/tools/r8/R8Partial.java b/src/main/java/com/android/tools/r8/R8Partial.java
index 2b104b9..e412af4 100644
--- a/src/main/java/com/android/tools/r8/R8Partial.java
+++ b/src/main/java/com/android/tools/r8/R8Partial.java
@@ -178,6 +178,7 @@
             .addLibraryResourceProvider(
                 new InternalClasspathOrLibraryClassProvider<>(d8Result.getOutputLibraryClasses()))
             .enableLegacyFullModeForKeepRules(true)
+            .setBuildMetadataConsumer(options.r8BuildMetadataConsumer)
             .setEnableExperimentalMissingLibraryApiModeling(
                 options.apiModelingOptions().isApiModelingEnabled())
             .setMinApiLevel(options.getMinApiLevel().getLevel())
diff --git a/src/main/java/com/android/tools/r8/graph/ClassDefinition.java b/src/main/java/com/android/tools/r8/graph/ClassDefinition.java
index a021545..9fd1f91 100644
--- a/src/main/java/com/android/tools/r8/graph/ClassDefinition.java
+++ b/src/main/java/com/android/tools/r8/graph/ClassDefinition.java
@@ -34,12 +34,4 @@
   default boolean isClass() {
     return true;
   }
-
-  boolean isClasspathClass();
-
-  DexClasspathClass asClasspathClass();
-
-  boolean isLibraryClass();
-
-  DexLibraryClass asLibraryClass();
 }
diff --git a/src/main/java/com/android/tools/r8/graph/ClasspathDefinition.java b/src/main/java/com/android/tools/r8/graph/ClasspathDefinition.java
index 43a4e70..8516fbd 100644
--- a/src/main/java/com/android/tools/r8/graph/ClasspathDefinition.java
+++ b/src/main/java/com/android/tools/r8/graph/ClasspathDefinition.java
@@ -21,4 +21,14 @@
   default ProgramDerivedContext asProgramDerivedContext(ProgramDerivedContext witness) {
     return ClasspathOrLibraryContext.create(this, witness);
   }
+
+  @Override
+  default boolean isClasspathDefinition() {
+    return true;
+  }
+
+  @Override
+  default ClasspathDefinition asClasspathDefinition() {
+    return this;
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/Definition.java b/src/main/java/com/android/tools/r8/graph/Definition.java
index e03b8e8..835d298 100644
--- a/src/main/java/com/android/tools/r8/graph/Definition.java
+++ b/src/main/java/com/android/tools/r8/graph/Definition.java
@@ -76,6 +76,22 @@
     return null;
   }
 
+  default boolean isClasspathClass() {
+    return false;
+  }
+
+  default DexClasspathClass asClasspathClass() {
+    return null;
+  }
+
+  default boolean isClasspathDefinition() {
+    return false;
+  }
+
+  default ClasspathDefinition asClasspathDefinition() {
+    return null;
+  }
+
   default boolean isClasspathField() {
     return false;
   }
@@ -96,6 +112,18 @@
     return null;
   }
 
+  default boolean isLibraryClass() {
+    return false;
+  }
+
+  default DexLibraryClass asLibraryClass() {
+    return null;
+  }
+
+  default boolean isLibraryDefinition() {
+    return false;
+  }
+
   default boolean isLibraryField() {
     return false;
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index e8bc0b7..db3a696 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -675,28 +675,8 @@
     return this;
   }
 
-  @Override
-  public boolean isClasspathClass() {
-    return false;
-  }
-
-  @Override
-  public DexClasspathClass asClasspathClass() {
-    return null;
-  }
-
   public abstract boolean isNotProgramClass();
 
-  @Override
-  public boolean isLibraryClass() {
-    return false;
-  }
-
-  @Override
-  public DexLibraryClass asLibraryClass() {
-    return null;
-  }
-
   public boolean isPrivate() {
     return accessFlags.isPrivate();
   }
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignatureContextBuilder.java b/src/main/java/com/android/tools/r8/graph/GenericSignatureContextBuilder.java
index 84bd5a1..0b901df 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignatureContextBuilder.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignatureContextBuilder.java
@@ -142,7 +142,8 @@
     }
     Map<DexReference, TypeParameterSubstitutions> formalsInfo = new IdentityHashMap<>();
     Map<DexReference, DexReference> enclosingInfo = new IdentityHashMap<>();
-    programClasses.forEach(
+    WorkList<DexClass> worklist = WorkList.newIdentityWorkList(programClasses);
+    worklist.process(
         clazz -> {
           // Build up a map of type variables to bounds for every reference such that we can
           // lookup the information even after we prune the generic signatures.
@@ -150,7 +151,7 @@
             formalsInfo.put(
                 clazz.getReference(),
                 TypeParameterSubstitutions.create(clazz.classSignature.getFormalTypeParameters()));
-            clazz.forEachProgramMethod(
+            clazz.forEachClassMethod(
                 method -> {
                   MethodTypeSignature methodSignature =
                       method.getDefinition().getGenericSignature();
@@ -165,8 +166,15 @@
           // Build up an enclosing class context such that the enclosing class can be looked up
           // even after inner class and enclosing method attribute attributes are removed.
           InnerClassAttribute innerClassAttribute = clazz.getInnerClassAttributeForThisClass();
-          if (innerClassAttribute != null) {
+          if (innerClassAttribute != null && innerClassAttribute.getOuter() != null) {
             enclosingInfo.put(clazz.getType(), innerClassAttribute.getOuter());
+            DexClass outerClass =
+                appView
+                    .appInfo()
+                    .definitionForWithoutExistenceAssert(innerClassAttribute.getOuter());
+            if (outerClass != null) {
+              worklist.addIfNotSeen(outerClass);
+            }
           }
           EnclosingMethodAttribute enclosingMethodAttribute = clazz.getEnclosingMethodAttribute();
           if (enclosingMethodAttribute != null) {
diff --git a/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java b/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java
index 5328880..88205c7 100644
--- a/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java
@@ -19,6 +19,7 @@
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Sets;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.IdentityHashMap;
 import java.util.List;
 import java.util.Map;
@@ -260,7 +261,7 @@
     }
     String joined =
         javaLibraryOverride.stream()
-            .sorted()
+            .sorted(Comparator.comparing(DexClass::getType))
             .map(clazz -> clazz.toSourceString() + " (origin: " + clazz.getOrigin() + ")")
             .collect(Collectors.joining(", "));
     String message =
diff --git a/src/main/java/com/android/tools/r8/graph/LibraryDefinition.java b/src/main/java/com/android/tools/r8/graph/LibraryDefinition.java
index 6767199..0dee6e9 100644
--- a/src/main/java/com/android/tools/r8/graph/LibraryDefinition.java
+++ b/src/main/java/com/android/tools/r8/graph/LibraryDefinition.java
@@ -20,4 +20,9 @@
   default ProgramDerivedContext asProgramDerivedContext(ProgramDerivedContext witness) {
     return ClasspathOrLibraryContext.create(this, witness);
   }
+
+  @Override
+  default boolean isLibraryDefinition() {
+    return true;
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstantValueUtils.java b/src/main/java/com/android/tools/r8/ir/code/ConstantValueUtils.java
index b75e839..d94abb1 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstantValueUtils.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstantValueUtils.java
@@ -4,8 +4,9 @@
 
 package com.android.tools.r8.ir.code;
 
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.naming.IdentifierNameStringUtils;
@@ -17,7 +18,7 @@
    * Otherwise returns null. This should only be used for tracing.
    */
   public static DexType getDexTypeRepresentedByValueForTracing(
-      Value value, DexDefinitionSupplier definitions) {
+      Value value, AppView<? extends AppInfoWithClassHierarchy> appView) {
     Value alias =
         value.getAliasedValue(IgnoreDebugLocalWriteAliasedValueConfiguration.getInstance());
     if (alias.isPhi()) {
@@ -30,9 +31,11 @@
 
     if (alias.definition.isInvokeStatic()) {
       InvokeStatic invoke = alias.definition.asInvokeStatic();
-      if (definitions.dexItemFactory().classMethods
+      if (appView
+          .dexItemFactory()
+          .classMethods
           .isReflectiveClassLookup(invoke.getInvokedMethod())) {
-        return getDexTypeFromClassForName(invoke, definitions);
+        return getDexTypeFromClassForName(invoke, appView);
       }
     }
 
@@ -40,24 +43,23 @@
   }
 
   public static DexClass getClassFromClassForName(
-      InvokeStatic invoke, DexDefinitionSupplier definitions) {
-    DexType type = getDexTypeFromClassForName(invoke, definitions);
+      InvokeStatic invoke, AppView<? extends AppInfoWithClassHierarchy> appView) {
+    DexType type = getDexTypeFromClassForName(invoke, appView);
     if (type != null && type.isClassType()) {
-      return definitions.definitionFor(type);
+      return appView.definitionFor(type);
     }
     return null;
   }
 
   public static DexType getDexTypeFromClassForName(
-      InvokeStatic invoke, DexDefinitionSupplier definitions) {
-    assert definitions.dexItemFactory().classMethods
-        .isReflectiveClassLookup(invoke.getInvokedMethod());
+      InvokeStatic invoke, AppView<? extends AppInfoWithClassHierarchy> appView) {
+    assert appView.dexItemFactory().classMethods.isReflectiveClassLookup(invoke.getInvokedMethod());
     if (invoke.arguments().size() == 1 || invoke.arguments().size() == 3) {
       Value argument = invoke.arguments().get(0);
       if (argument.isConstString()) {
         ConstString constStringInstruction = argument.getConstInstruction().asConstString();
-        return IdentifierNameStringUtils.inferTypeFromNameString(
-            definitions, constStringInstruction.getValue());
+        return IdentifierNameStringUtils.inferClassTypeFromNameString(
+            appView, constStringInstruction.getValue());
       }
       if (argument.isDexItemBasedConstString()) {
         DexItemBasedConstString constStringInstruction =
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/AutoCloseableRetargeterHelper.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/AutoCloseableRetargeterHelper.java
index a986672..3e41d2f 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/AutoCloseableRetargeterHelper.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/AutoCloseableRetargeterHelper.java
@@ -199,8 +199,15 @@
                                 factory.voidType, factory.javaUtilConcurrentExecutorServiceType))
                         .setCode(
                             methodSig ->
-                                BackportedMethods.ExecutorServiceMethods_closeExecutorService(
-                                    factory, methodSig)));
+                                appView
+                                        .options()
+                                        .getMinApiLevel()
+                                        .isGreaterThanOrEqualTo(AndroidApiLevel.N)
+                                    ? BackportedMethods
+                                        .ExecutorServiceMethods_closeExecutorServiceNPlus(
+                                            factory, methodSig)
+                                    : BackportedMethods.ExecutorServiceMethods_closeExecutorService(
+                                        factory, methodSig)));
     eventConsumer.acceptAutoCloseableForwardingMethod(method, context);
     return method.getReference();
   }
diff --git a/src/main/java/com/android/tools/r8/metadata/R8BuildMetadata.java b/src/main/java/com/android/tools/r8/metadata/R8BuildMetadata.java
index 0c81115..256e9d2 100644
--- a/src/main/java/com/android/tools/r8/metadata/R8BuildMetadata.java
+++ b/src/main/java/com/android/tools/r8/metadata/R8BuildMetadata.java
@@ -14,6 +14,7 @@
 import com.android.tools.r8.metadata.impl.R8KeepAttributesMetadataImpl;
 import com.android.tools.r8.metadata.impl.R8LibraryDesugaringMetadataImpl;
 import com.android.tools.r8.metadata.impl.R8OptionsMetadataImpl;
+import com.android.tools.r8.metadata.impl.R8PartialCompilationMetadataImpl;
 import com.android.tools.r8.metadata.impl.R8ResourceOptimizationMetadataImpl;
 import com.android.tools.r8.metadata.impl.R8StartupOptimizationMetadataImpl;
 import com.android.tools.r8.metadata.impl.R8StatsMetadataImpl;
@@ -46,6 +47,9 @@
         .registerTypeAdapter(
             R8LibraryDesugaringMetadata.class, deserializeTo(R8LibraryDesugaringMetadataImpl.class))
         .registerTypeAdapter(
+            R8PartialCompilationMetadata.class,
+            deserializeTo(R8PartialCompilationMetadataImpl.class))
+        .registerTypeAdapter(
             R8ResourceOptimizationMetadata.class,
             deserializeTo(R8ResourceOptimizationMetadataImpl.class))
         .registerTypeAdapter(
@@ -80,6 +84,11 @@
   R8FeatureSplitsMetadata getFeatureSplitsMetadata();
 
   /**
+   * @return null if not using partial compilation.
+   */
+  R8PartialCompilationMetadata getPartialCompilationMetadata();
+
+  /**
    * @return null if resource optimization is disabled.
    */
   R8ResourceOptimizationMetadata getResourceOptimizationMetadata();
diff --git a/src/main/java/com/android/tools/r8/metadata/R8PartialCompilationMetadata.java b/src/main/java/com/android/tools/r8/metadata/R8PartialCompilationMetadata.java
new file mode 100644
index 0000000..99cf1a3
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/metadata/R8PartialCompilationMetadata.java
@@ -0,0 +1,6 @@
+// Copyright (c) 2025, 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.metadata;
+
+public interface R8PartialCompilationMetadata {}
diff --git a/src/main/java/com/android/tools/r8/metadata/impl/BuildMetadataFactory.java b/src/main/java/com/android/tools/r8/metadata/impl/BuildMetadataFactory.java
index 870e566..ecead83 100644
--- a/src/main/java/com/android/tools/r8/metadata/impl/BuildMetadataFactory.java
+++ b/src/main/java/com/android/tools/r8/metadata/impl/BuildMetadataFactory.java
@@ -57,6 +57,7 @@
             builder ->
                 builder.setFeatureSplitsMetadata(
                     R8FeatureSplitsMetadataImpl.create(appView, virtualFilesForFeatureSplit)))
+        .setPartialCompilationMetadata(R8PartialCompilationMetadataImpl.create(options))
         .setResourceOptimizationOptions(R8ResourceOptimizationMetadataImpl.create(options))
         .setStartupOptimizationOptions(R8StartupOptimizationMetadataImpl.create(options))
         .setStatsMetadata(R8StatsMetadataImpl.create(appView))
diff --git a/src/main/java/com/android/tools/r8/metadata/impl/R8BuildMetadataImpl.java b/src/main/java/com/android/tools/r8/metadata/impl/R8BuildMetadataImpl.java
index 2abfbdc..ea6cec6 100644
--- a/src/main/java/com/android/tools/r8/metadata/impl/R8BuildMetadataImpl.java
+++ b/src/main/java/com/android/tools/r8/metadata/impl/R8BuildMetadataImpl.java
@@ -16,6 +16,7 @@
 import com.android.tools.r8.metadata.R8DexFileMetadata;
 import com.android.tools.r8.metadata.R8FeatureSplitsMetadata;
 import com.android.tools.r8.metadata.R8OptionsMetadata;
+import com.android.tools.r8.metadata.R8PartialCompilationMetadata;
 import com.android.tools.r8.metadata.R8ResourceOptimizationMetadata;
 import com.android.tools.r8.metadata.R8StartupOptimizationMetadata;
 import com.android.tools.r8.metadata.R8StatsMetadata;
@@ -60,6 +61,10 @@
   private final R8FeatureSplitsMetadata featureSplitsMetadata;
 
   @Expose
+  @SerializedName("partialCompilation")
+  private final R8PartialCompilationMetadata partialCompilationMetadata;
+
+  @Expose
   @SerializedName("resourceOptimization")
   private final R8ResourceOptimizationMetadata resourceOptimizationMetadata;
 
@@ -78,6 +83,7 @@
       List<R8DexFileMetadata> dexFilesMetadata,
       R8StatsMetadata statsMetadata,
       R8FeatureSplitsMetadata featureSplitsMetadata,
+      R8PartialCompilationMetadata partialCompilationMetadata,
       R8ResourceOptimizationMetadata resourceOptimizationMetadata,
       R8StartupOptimizationMetadata startupOptimizationMetadata,
       String version) {
@@ -87,6 +93,7 @@
     this.dexFilesMetadata = dexFilesMetadata;
     this.statsMetadata = statsMetadata;
     this.featureSplitsMetadata = featureSplitsMetadata;
+    this.partialCompilationMetadata = partialCompilationMetadata;
     this.resourceOptimizationMetadata = resourceOptimizationMetadata;
     this.startupOptimizationMetadata = startupOptimizationMetadata;
     this.version = version;
@@ -122,6 +129,11 @@
   }
 
   @Override
+  public R8PartialCompilationMetadata getPartialCompilationMetadata() {
+    return partialCompilationMetadata;
+  }
+
+  @Override
   public R8ResourceOptimizationMetadata getResourceOptimizationMetadata() {
     return resourceOptimizationMetadata;
   }
@@ -154,6 +166,7 @@
     private List<R8DexFileMetadata> dexFilesMetadata;
     private R8StatsMetadata statsMetadata;
     private R8FeatureSplitsMetadata featureSplitsMetadata;
+    private R8PartialCompilationMetadata partialCompilationMetadata;
     private R8ResourceOptimizationMetadata resourceOptimizationOptions;
     private R8StartupOptimizationMetadata startupOptimizationOptions;
     private String version;
@@ -196,6 +209,12 @@
       return this;
     }
 
+    public Builder setPartialCompilationMetadata(
+        R8PartialCompilationMetadata partialCompilationMetadata) {
+      this.partialCompilationMetadata = partialCompilationMetadata;
+      return this;
+    }
+
     public Builder setResourceOptimizationOptions(
         R8ResourceOptimizationMetadata resourceOptimizationOptions) {
       this.resourceOptimizationOptions = resourceOptimizationOptions;
@@ -221,6 +240,7 @@
           dexFilesMetadata,
           statsMetadata,
           featureSplitsMetadata,
+          partialCompilationMetadata,
           resourceOptimizationOptions,
           startupOptimizationOptions,
           version);
diff --git a/src/main/java/com/android/tools/r8/metadata/impl/R8PartialCompilationMetadataImpl.java b/src/main/java/com/android/tools/r8/metadata/impl/R8PartialCompilationMetadataImpl.java
new file mode 100644
index 0000000..efbc67a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/metadata/impl/R8PartialCompilationMetadataImpl.java
@@ -0,0 +1,32 @@
+// Copyright (c) 2025, 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.metadata.impl;
+
+import com.android.tools.r8.keepanno.annotations.AnnotationPattern;
+import com.android.tools.r8.keepanno.annotations.FieldAccessFlags;
+import com.android.tools.r8.keepanno.annotations.KeepConstraint;
+import com.android.tools.r8.keepanno.annotations.KeepItemKind;
+import com.android.tools.r8.keepanno.annotations.UsedByReflection;
+import com.android.tools.r8.metadata.R8PartialCompilationMetadata;
+import com.android.tools.r8.utils.InternalOptions;
+import com.google.gson.annotations.SerializedName;
+
+@UsedByReflection(
+    description = "Keep and preserve @SerializedName for correct (de)serialization",
+    constraints = {KeepConstraint.LOOKUP},
+    constrainAnnotations = @AnnotationPattern(constant = SerializedName.class),
+    kind = KeepItemKind.CLASS_AND_FIELDS,
+    fieldAccess = {FieldAccessFlags.PRIVATE},
+    fieldAnnotatedByClassConstant = SerializedName.class)
+public class R8PartialCompilationMetadataImpl implements R8PartialCompilationMetadata {
+
+  private R8PartialCompilationMetadataImpl() {}
+
+  public static R8PartialCompilationMetadataImpl create(InternalOptions options) {
+    if (options.partialSubCompilationConfiguration == null) {
+      return null;
+    }
+    return new R8PartialCompilationMetadataImpl();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/metadata/impl/R8StartupOptimizationMetadataImpl.java b/src/main/java/com/android/tools/r8/metadata/impl/R8StartupOptimizationMetadataImpl.java
index 9e513b4..10a564f 100644
--- a/src/main/java/com/android/tools/r8/metadata/impl/R8StartupOptimizationMetadataImpl.java
+++ b/src/main/java/com/android/tools/r8/metadata/impl/R8StartupOptimizationMetadataImpl.java
@@ -39,7 +39,7 @@
 
   public static R8StartupOptimizationMetadataImpl create(InternalOptions options) {
     StartupOptions startupOptions = options.getStartupOptions();
-    if (startupOptions.getStartupProfileProviders().isEmpty()) {
+    if (!startupOptions.hasStartupProfileProviders()) {
       return null;
     }
     return new R8StartupOptimizationMetadataImpl(startupOptions);
diff --git a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
index ac6868b..0b20716 100644
--- a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
+++ b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
@@ -11,6 +11,7 @@
 
 import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.Definition;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexMember;
@@ -104,10 +105,11 @@
       return;
     }
     DexString original = staticValue.getValue();
-    DexReference itemBasedString = inferMemberOrTypeFromNameString(appView(), original);
+    Definition itemBasedString = inferMemberOrTypeFromNameString(appView(), original);
     if (itemBasedString != null) {
       encodedField.setStaticValue(
-          new DexItemBasedValueString(itemBasedString, ClassNameComputationInfo.none()));
+          new DexItemBasedValueString(
+              itemBasedString.getReference(), ClassNameComputationInfo.none()));
     }
   }
 
@@ -176,13 +178,13 @@
       return null;
     }
     DexString original = in.getConstInstruction().asConstString().getValue();
-    DexReference itemBasedString = inferMemberOrTypeFromNameString(appView(), original);
+    Definition itemBasedString = inferMemberOrTypeFromNameString(appView(), original);
     if (itemBasedString == null) {
       warnUndeterminedIdentifierIfNecessary(
           field, code.context(), fieldPut.asFieldInstruction(), original);
       return null;
     }
-    return itemBasedString;
+    return itemBasedString.getReference();
   }
 
   private InstructionListIterator decoupleIdentifierNameStringForFieldPutInstruction(
@@ -245,7 +247,7 @@
     Value[] changes = new Value[ins.size()];
     if (isReflectionMethod(appView.dexItemFactory(), invokedMethod) || isClassNameComparison) {
       IdentifierNameStringLookupResult<?> identifierLookupResult =
-          identifyIdentifier(invoke, appView, code.context());
+          identifyIdentifier(invoke, appView(), code.context());
       if (identifierLookupResult == null) {
         warnUndeterminedIdentifierIfNecessary(invokedMethod, code.context(), invoke, null);
         return iterator;
@@ -312,12 +314,15 @@
       // For general invoke. Multiple arguments can be string literals to be renamed.
       for (int i = 0; i < ins.size(); i++) {
         Value in = ins.get(i);
+        if (in.getType().isNullType()) {
+          continue;
+        }
         if (!in.isConstString()) {
           warnUndeterminedIdentifierIfNecessary(invokedMethod, code.context(), invoke, null);
           continue;
         }
         DexString original = in.getConstInstruction().asConstString().getValue();
-        DexReference itemBasedString = inferMemberOrTypeFromNameString(appView(), original);
+        Definition itemBasedString = inferMemberOrTypeFromNameString(appView(), original);
         if (itemBasedString == null) {
           warnUndeterminedIdentifierIfNecessary(invokedMethod, code.context(), invoke, original);
           continue;
@@ -328,7 +333,8 @@
         // Prepare $decoupled just before $invoke
         Value newIn = code.createValue(in.getType(), in.getLocalInfo());
         DexItemBasedConstString decoupled =
-            new DexItemBasedConstString(newIn, itemBasedString, ClassNameComputationInfo.none());
+            new DexItemBasedConstString(
+                newIn, itemBasedString.getReference(), ClassNameComputationInfo.none());
         decoupled.setPosition(invoke.getPosition());
         changes[i] = newIn;
         // If the current block has catch handler, split into two blocks.
diff --git a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringUtils.java b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringUtils.java
index f312d7d..9e232c0 100644
--- a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringUtils.java
+++ b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringUtils.java
@@ -5,14 +5,14 @@
 
 import static com.android.tools.r8.utils.DescriptorUtils.javaTypeToDescriptorIfValidJavaType;
 
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.Definition;
 import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexDefinitionSupplier;
-import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexClassAndField;
+import com.android.tools.r8.graph.DexClassAndMember;
+import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexMember;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexString;
@@ -33,7 +33,6 @@
 import com.android.tools.r8.ir.code.NewArrayFilled;
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.naming.identifiernamestring.IdentifierNameStringLookupResult;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.StringUtils;
 import com.google.common.collect.Sets;
 import java.util.ArrayList;
@@ -199,15 +198,17 @@
    * Returns a {@link DexReference} if one of the arguments to the invoke instruction is a constant
    * string that corresponds to either a class or member name (i.e., an identifier).
    *
-   * @param definitions {@link DexDefinitionSupplier} that gives access to {@link DexItemFactory}.
+   * @param appView {@link AppView} that gives access to {@link DexItemFactory}.
    * @param invoke {@link InvokeMethod} that is expected to have an identifier in its arguments.
    * @return {@link DexReference} corresponding to the first constant string argument that matches a
    *     class or member name, or {@code null} if no such constant was found.
    */
   @SuppressWarnings("ReferenceEquality")
   public static IdentifierNameStringLookupResult<?> identifyIdentifier(
-      InvokeMethod invoke, DexDefinitionSupplier definitions, ProgramMethod context) {
-    DexItemFactory dexItemFactory = definitions.dexItemFactory();
+      InvokeMethod invoke,
+      AppView<? extends AppInfoWithClassHierarchy> appView,
+      ProgramMethod context) {
+    DexItemFactory dexItemFactory = appView.dexItemFactory();
     List<Value> ins = invoke.arguments();
     // The only static calls: Class#forName,
     //   which receive either (String) or (String, boolean, ClassLoader) as ins.
@@ -215,7 +216,7 @@
       InvokeStatic invokeStatic = invoke.asInvokeStatic();
       if (dexItemFactory.classMethods.isReflectiveClassLookup(invokeStatic.getInvokedMethod())) {
         return IdentifierNameStringLookupResult.fromClassForName(
-            ConstantValueUtils.getDexTypeFromClassForName(invokeStatic, definitions));
+            ConstantValueUtils.getDexTypeFromClassForName(invokeStatic, appView));
       }
     }
 
@@ -225,8 +226,7 @@
         int argumentIndex = getPositionOfFirstConstString(invokeVirtual);
         if (argumentIndex >= 0) {
           return IdentifierNameStringLookupResult.fromClassNameComparison(
-              inferTypeFromConstStringValue(
-                  definitions, invokeVirtual.inValues().get(argumentIndex)));
+              inferTypeFromConstStringValue(appView, invokeVirtual.inValues().get(argumentIndex)));
         }
       }
     }
@@ -251,7 +251,7 @@
         // declared in the library. Hence there is no need to handle this case.
         return null;
       }
-      DexClass holder = definitions.definitionFor(holderType, context);
+      DexClass holder = appView.definitionFor(holderType, context);
       if (holder == null) {
         return null;
       }
@@ -301,34 +301,42 @@
     return -1;
   }
 
-  static DexReference inferMemberOrTypeFromNameString(
-      AppView<AppInfoWithLiveness> appView, DexString dexString) {
+  public static Definition inferMemberOrTypeFromNameString(
+      AppView<? extends AppInfoWithClassHierarchy> appView, DexString dexString) {
     // "fully.qualified.ClassName.fieldOrMethodName"
     // "fully.qualified.ClassName#fieldOrMethodName"
-    DexMember<?, ?> itemBasedString = inferMemberFromNameString(appView, dexString);
-    if (itemBasedString == null) {
-      // "fully.qualified.ClassName"
-      return inferTypeFromNameString(appView, dexString);
+    DexClassAndMember<?, ?> member = inferMemberFromNameString(appView, dexString);
+    if (member != null) {
+      return member;
     }
-    return itemBasedString;
+    // "fully.qualified.ClassName"
+    DexType type = inferClassTypeFromNameString(appView, dexString);
+    if (type != null) {
+      return appView.appInfo().definitionForWithoutExistenceAssert(type);
+    }
+    return null;
   }
 
-  public static DexType inferTypeFromNameString(
-      DexDefinitionSupplier definitions, DexString dexString) {
+  public static DexType inferClassTypeFromNameString(
+      AppView<? extends AppInfoWithClassHierarchy> appView, DexString dexString) {
     String maybeDescriptor = javaTypeToDescriptorIfValidJavaType(dexString.toString());
-    if (maybeDescriptor != null) {
-      return definitions.dexItemFactory().createType(maybeDescriptor);
+    if (maybeDescriptor == null) {
+      return null;
+    }
+    DexType type = appView.dexItemFactory().createType(maybeDescriptor);
+    if (type.isClassType()) {
+      return type;
     }
     return null;
   }
 
   public static DexType inferTypeFromConstStringValue(
-      DexDefinitionSupplier definitions, Value value) {
+      AppView<? extends AppInfoWithClassHierarchy> appView, Value value) {
     Value root = value.getAliasedValue();
     assert !root.isPhi();
     assert root.isConstString() || root.isDexItemBasedConstString();
     if (root.isConstString()) {
-      return inferTypeFromNameString(definitions, root.definition.asConstString().getValue());
+      return inferClassTypeFromNameString(appView, root.definition.asConstString().getValue());
     }
     if (root.isDexItemBasedConstString()) {
       DexReference reference = root.definition.asDexItemBasedConstString().getItem();
@@ -339,8 +347,8 @@
     return null;
   }
 
-  private static DexMember<?, ?> inferMemberFromNameString(
-      AppView<AppInfoWithLiveness> appView, DexString dexString) {
+  private static DexClassAndMember<?, ?> inferMemberFromNameString(
+      AppView<? extends AppInfoWithClassHierarchy> appView, DexString dexString) {
     String identifier = dexString.toString();
     String typeIdentifier = null;
     String memberIdentifier = null;
@@ -374,40 +382,39 @@
     if (holder == null) {
       return null;
     }
-    DexMember<?, ?> itemBasedString = inferFieldInHolder(holder, memberIdentifier, null);
-    if (itemBasedString == null) {
-      itemBasedString = inferMethodNameInHolder(holder, memberIdentifier);
+    DexClassAndField itemBasedString = inferFieldInHolder(holder, memberIdentifier, null);
+    if (itemBasedString != null) {
+      return itemBasedString;
     }
-    return itemBasedString;
+    return inferMethodNameInHolder(holder, memberIdentifier);
   }
 
-  @SuppressWarnings("ReferenceEquality")
-  private static DexField inferFieldInHolder(DexClass holder, String name, DexType fieldType) {
-    for (DexEncodedField encodedField : holder.fields()) {
-      if (encodedField.getReference().name.toString().equals(name)
-          && (fieldType == null || encodedField.getReference().type == fieldType)) {
-        return encodedField.getReference();
+  private static DexClassAndField inferFieldInHolder(
+      DexClass holder, String name, DexType fieldType) {
+    for (DexClassAndField field : holder.classFields()) {
+      if (field.getName().isEqualTo(name)
+          && (fieldType == null || field.getType().isIdenticalTo(fieldType))) {
+        return field;
       }
     }
     return null;
   }
 
-  private static DexMethod inferMethodNameInHolder(DexClass holder, String name) {
-    for (DexEncodedMethod encodedMethod : holder.methods()) {
-      if (encodedMethod.getReference().name.toString().equals(name)) {
-        return encodedMethod.getReference();
+  private static DexClassAndMethod inferMethodNameInHolder(DexClass holder, String name) {
+    for (DexClassAndMethod method : holder.classMethods()) {
+      if (method.getName().isEqualTo(name)) {
+        return method;
       }
     }
     return null;
   }
 
-  private static DexMethod inferMethodInHolder(
+  private static DexClassAndMethod inferMethodInHolder(
       DexClass holder, String name, DexTypeList arguments) {
     assert arguments != null;
-    for (DexEncodedMethod encodedMethod : holder.methods()) {
-      if (encodedMethod.getReference().name.toString().equals(name)
-          && encodedMethod.getReference().proto.parameters.equals(arguments)) {
-        return encodedMethod.getReference();
+    for (DexClassAndMethod method : holder.classMethods()) {
+      if (method.getName().isEqualTo(name) && method.getParameters().equals(arguments)) {
+        return method;
       }
     }
     return null;
diff --git a/src/main/java/com/android/tools/r8/naming/identifiernamestring/DexMemberBasedConstStringIdentifierNameStringLookupResult.java b/src/main/java/com/android/tools/r8/naming/identifiernamestring/DexMemberBasedConstStringIdentifierNameStringLookupResult.java
index 5072d35..4292714 100644
--- a/src/main/java/com/android/tools/r8/naming/identifiernamestring/DexMemberBasedConstStringIdentifierNameStringLookupResult.java
+++ b/src/main/java/com/android/tools/r8/naming/identifiernamestring/DexMemberBasedConstStringIdentifierNameStringLookupResult.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.naming.identifiernamestring;
 
+import com.android.tools.r8.graph.DexClassAndMember;
 import com.android.tools.r8.graph.DexMember;
 
 public class DexMemberBasedConstStringIdentifierNameStringLookupResult
@@ -12,4 +13,8 @@
   DexMemberBasedConstStringIdentifierNameStringLookupResult(DexMember<?, ?> member) {
     super(member);
   }
+
+  DexMemberBasedConstStringIdentifierNameStringLookupResult(DexClassAndMember<?, ?> member) {
+    super(member.getReference());
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/naming/identifiernamestring/IdentifierNameStringLookupResult.java b/src/main/java/com/android/tools/r8/naming/identifiernamestring/IdentifierNameStringLookupResult.java
index 2c11771..7c4eb6e 100644
--- a/src/main/java/com/android/tools/r8/naming/identifiernamestring/IdentifierNameStringLookupResult.java
+++ b/src/main/java/com/android/tools/r8/naming/identifiernamestring/IdentifierNameStringLookupResult.java
@@ -6,6 +6,7 @@
 
 import static com.android.tools.r8.utils.FunctionUtils.applyOrElse;
 
+import com.android.tools.r8.graph.DexClassAndMember;
 import com.android.tools.r8.graph.DexMember;
 import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexType;
@@ -40,7 +41,7 @@
   }
 
   public static UncategorizedMemberIdentifierNameStringLookupResult fromUncategorized(
-      DexMember<?, ?> member) {
+      DexClassAndMember<?, ?> member) {
     return applyOrElse(member, UncategorizedMemberIdentifierNameStringLookupResult::new, null);
   }
 
diff --git a/src/main/java/com/android/tools/r8/naming/identifiernamestring/UncategorizedMemberIdentifierNameStringLookupResult.java b/src/main/java/com/android/tools/r8/naming/identifiernamestring/UncategorizedMemberIdentifierNameStringLookupResult.java
index 5d29fc1..7226ca7 100644
--- a/src/main/java/com/android/tools/r8/naming/identifiernamestring/UncategorizedMemberIdentifierNameStringLookupResult.java
+++ b/src/main/java/com/android/tools/r8/naming/identifiernamestring/UncategorizedMemberIdentifierNameStringLookupResult.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.naming.identifiernamestring;
 
+import com.android.tools.r8.graph.DexClassAndMember;
 import com.android.tools.r8.graph.DexMember;
 
 public class UncategorizedMemberIdentifierNameStringLookupResult
@@ -12,4 +13,8 @@
   UncategorizedMemberIdentifierNameStringLookupResult(DexMember<?, ?> member) {
     super(member);
   }
+
+  UncategorizedMemberIdentifierNameStringLookupResult(DexClassAndMember<?, ?> member) {
+    super(member.getReference());
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/partial/R8PartialSubCompilationConfiguration.java b/src/main/java/com/android/tools/r8/partial/R8PartialSubCompilationConfiguration.java
index e6203ea..1e01437 100644
--- a/src/main/java/com/android/tools/r8/partial/R8PartialSubCompilationConfiguration.java
+++ b/src/main/java/com/android/tools/r8/partial/R8PartialSubCompilationConfiguration.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.Definition;
 import com.android.tools.r8.graph.DexApplicationReadFlags;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexClasspathClass;
@@ -303,7 +304,7 @@
       }
     }
 
-    public boolean isD8Definition(ProgramDefinition definition) {
+    public boolean isD8Definition(Definition definition) {
       return hasD8DefinitionFor(definition.getReference());
     }
 
diff --git a/src/main/java/com/android/tools/r8/partial/R8PartialUseCollector.java b/src/main/java/com/android/tools/r8/partial/R8PartialUseCollector.java
index 6e30e4ee..5bdfdb9 100644
--- a/src/main/java/com/android/tools/r8/partial/R8PartialUseCollector.java
+++ b/src/main/java/com/android/tools/r8/partial/R8PartialUseCollector.java
@@ -12,10 +12,14 @@
 import com.android.tools.r8.graph.DexClassAndField;
 import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMember;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.graph.ProgramField;
 import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.naming.IdentifierNameStringUtils;
 import com.android.tools.r8.partial.R8PartialSubCompilationConfiguration.R8PartialR8SubCompilationConfiguration;
 import com.android.tools.r8.references.PackageReference;
 import com.android.tools.r8.shaking.ProguardClassFilter;
@@ -40,21 +44,30 @@
 
 public abstract class R8PartialUseCollector extends UseCollector {
 
+  private final Set<DexMember<?, ?>> identifierNameStrings;
   private final ReflectiveIdentification reflectiveIdentification;
 
   private final Set<DexReference> seenAllowObfuscation = ConcurrentHashMap.newKeySet();
   private final Set<DexReference> seenDisallowObfuscation = ConcurrentHashMap.newKeySet();
   private final Set<String> packagesToKeep = ConcurrentHashMap.newKeySet();
 
-  public R8PartialUseCollector(AppView<? extends AppInfoWithClassHierarchy> appView) {
+  public R8PartialUseCollector(
+      AppView<? extends AppInfoWithClassHierarchy> appView,
+      Set<DexMember<?, ?>> identifierNameStrings) {
     super(
         appView,
         new MissingReferencesConsumer(),
         new NopDiagnosticsHandler(),
         getTargetPredicate(appView));
+    this.identifierNameStrings = identifierNameStrings;
     this.reflectiveIdentification =
         new ReflectiveIdentification(
-            appView, new KeepAllReflectiveIdentificationEventConsumer(this));
+            appView, new KeepAllReflectiveIdentificationEventConsumer(this), identifierNameStrings);
+  }
+
+  private static Predicate<DexType> getTargetPredicate(
+      AppView<? extends AppInfoWithClassHierarchy> appView) {
+    return type -> appView.definitionFor(type) != null;
   }
 
   @Override
@@ -62,9 +75,21 @@
     return new KeepNativeMethodSignatureEventConsumer();
   }
 
-  public static Predicate<DexType> getTargetPredicate(
-      AppView<? extends AppInfoWithClassHierarchy> appView) {
-    return type -> appView.definitionFor(type) != null;
+  @Override
+  protected void traceFieldValue(ProgramField field) {
+    if (field.getAccessFlags().isStatic()
+        && field.getDefinition().hasExplicitStaticValue()
+        && identifierNameStrings.contains(field.getReference())) {
+      DexValue fieldValue = field.getDefinition().getStaticValue();
+      if (fieldValue.isDexValueString()) {
+        Definition definition =
+            IdentifierNameStringUtils.inferMemberOrTypeFromNameString(
+                appView, fieldValue.asDexValueString().getValue());
+        if (definition != null && isTargetType(definition.getContextType())) {
+          reflectiveIdentification.enqueue(field, definition);
+        }
+      }
+    }
   }
 
   public void run(ExecutorService executorService) throws ExecutionException {
diff --git a/src/main/java/com/android/tools/r8/shaking/ConvertCheckNotNullRule.java b/src/main/java/com/android/tools/r8/shaking/ConvertCheckNotNullRule.java
index 8dd9b27..479542a 100644
--- a/src/main/java/com/android/tools/r8/shaking/ConvertCheckNotNullRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ConvertCheckNotNullRule.java
@@ -80,7 +80,12 @@
   }
 
   @Override
-  public boolean applyToNonProgramClasses() {
+  public boolean isApplicableToClasspathClasses() {
+    return true;
+  }
+
+  @Override
+  public boolean isApplicableToLibraryClasses() {
     return true;
   }
 
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 9788f61..f020f5c 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -2362,14 +2362,16 @@
           unusedInterfaceTypes.computeIfAbsent(current, ignore -> Sets.newIdentityHashSet());
       if (implementors.add(implementer)) {
         for (DexType iface : current.getInterfaces()) {
-          DexProgramClass definition = getProgramClassOrNull(iface, current);
-          if (definition != null) {
-            if (definition.isPublic()
-                || implementer.getType().isSamePackage(definition.getType())) {
-              worklist.addIfNotSeen(definition);
-            } else {
-              markTypeAsLive(current, implementer);
+          DexClass definition = definitionFor(iface, current);
+          if (definition == null) {
+            continue;
+          }
+          if (definition.isPublic() || implementer.getType().isSamePackage(definition.getType())) {
+            if (definition.isProgramClass()) {
+              worklist.addIfNotSeen(definition.asProgramClass());
             }
+          } else {
+            markTypeAsLive(current, implementer);
           }
         }
       }
@@ -4598,7 +4600,8 @@
             amendWithCompanionMethods(rootSet.reprocess),
             rootSet.alwaysClassInline,
             joinIdentifierNameStrings(
-                rootSet.identifierNameStrings, reflectiveIdentification.getIdentifierNameStrings()),
+                rootSet.identifierNameStrings,
+                reflectiveIdentification.getIdentifierNameStringAdditions()),
             emptySet(),
             prunedClasspathTypesBuilder.build(),
             Collections.emptyMap(),
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardAssumeMayHaveSideEffectsRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardAssumeMayHaveSideEffectsRule.java
index 803ace4..5f2d2e9 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardAssumeMayHaveSideEffectsRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardAssumeMayHaveSideEffectsRule.java
@@ -77,7 +77,12 @@
   }
 
   @Override
-  public boolean applyToNonProgramClasses() {
+  public boolean isApplicableToClasspathClasses() {
+    return true;
+  }
+
+  @Override
+  public boolean isApplicableToLibraryClasses() {
     return true;
   }
 
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardAssumeNoSideEffectRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardAssumeNoSideEffectRule.java
index a224bed..57ce724 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardAssumeNoSideEffectRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardAssumeNoSideEffectRule.java
@@ -79,7 +79,12 @@
   }
 
   @Override
-  public boolean applyToNonProgramClasses() {
+  public boolean isApplicableToClasspathClasses() {
+    return true;
+  }
+
+  @Override
+  public boolean isApplicableToLibraryClasses() {
     return true;
   }
 
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java
index 2adc592..828ca04 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java
@@ -193,7 +193,11 @@
     return null;
   }
 
-  public boolean applyToNonProgramClasses() {
+  public boolean isApplicableToClasspathClasses() {
+    return false;
+  }
+
+  public boolean isApplicableToLibraryClasses() {
     return false;
   }
 
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardIdentifierNameStringRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardIdentifierNameStringRule.java
index 56f431d..db4e0f6 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardIdentifierNameStringRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardIdentifierNameStringRule.java
@@ -75,8 +75,12 @@
   }
 
   @Override
+  public boolean isApplicableToClasspathClasses() {
+    return true;
+  }
+
+  @Override
   String typeString() {
     return "identifiernamestring";
   }
-
 }
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java
index 85a96c2..fe4d871 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java
@@ -156,6 +156,12 @@
   }
 
   @Override
+  public boolean isApplicableToClasspathClasses() {
+    assert !super.isApplicableToClasspathClasses();
+    return getIncludeDescriptorClasses();
+  }
+
+  @Override
   public boolean isProguardKeepRule() {
     return true;
   }
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleBase.java b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleBase.java
index fb02899..93cbfd5 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleBase.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleBase.java
@@ -74,6 +74,10 @@
     this.modifiers = modifiers;
   }
 
+  public boolean getIncludeDescriptorClasses() {
+    return modifiers.includeDescriptorClasses;
+  }
+
   public ProguardKeepRuleType getType() {
     return type;
   }
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
index 39b35aa..09adf3d 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
@@ -19,6 +19,7 @@
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.BottomUpClassHierarchyTraversal;
+import com.android.tools.r8.graph.ClasspathDefinition;
 import com.android.tools.r8.graph.Definition;
 import com.android.tools.r8.graph.DexAnnotation;
 import com.android.tools.r8.graph.DexAnnotation.AnnotatedKind;
@@ -27,6 +28,7 @@
 import com.android.tools.r8.graph.DexClassAndField;
 import com.android.tools.r8.graph.DexClassAndMember;
 import com.android.tools.r8.graph.DexClassAndMethod;
+import com.android.tools.r8.graph.DexClasspathClass;
 import com.android.tools.r8.graph.DexDefinition;
 import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.graph.DexEncodedField;
@@ -46,6 +48,7 @@
 import com.android.tools.r8.graph.ProgramField;
 import com.android.tools.r8.graph.ProgramMember;
 import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.ProgramOrClasspathDefinition;
 import com.android.tools.r8.graph.PrunedItems;
 import com.android.tools.r8.graph.lens.GraphLens;
 import com.android.tools.r8.ir.analysis.type.DynamicType;
@@ -240,7 +243,7 @@
 
       // Trace references.
       R8PartialUseCollector useCollector =
-          new R8PartialUseCollector(appView) {
+          new R8PartialUseCollector(appView, identifierNameStrings) {
 
             // Map from Reference to canonical ReferencedFromExcludedClassInR8PartialRule.
             private final Map<Object, ReferencedFromExcludedClassInR8PartialRule> canonicalRules =
@@ -366,19 +369,37 @@
       }
 
       Collection<ProguardMemberRule> memberKeepRules = rule.getMemberRules();
-      Map<Predicate<DexDefinition>, DexProgramClass> preconditionSupplier;
+      Map<Predicate<DexDefinition>, DexClass> preconditionSupplier;
       if (rule instanceof ProguardKeepRule) {
-        if (clazz.isNotProgramClass()) {
+        ProguardKeepRule keepRule = rule.asProguardKeepRule();
+        if (clazz.isLibraryClass()) {
           return;
         }
-        switch (((ProguardKeepRule) rule).getType()) {
+        // Classpath classes may have members that refer to program classes. Therefore, we cannot
+        // skip rule evaluation in presence of `,includedescriptorclasses`.
+        if (clazz.isClasspathClass() && !keepRule.getIncludeDescriptorClasses()) {
+          return;
+        }
+        switch (keepRule.getType()) {
           case KEEP_CLASS_MEMBERS:
             // Members mentioned at -keepclassmembers always depend on their holder.
-            preconditionSupplier = ImmutableMap.of(definition -> true, clazz.asProgramClass());
+            preconditionSupplier = ImmutableMap.of(definition -> true, clazz);
             markMatchingVisibleMethods(
-                clazz, memberKeepRules, rule, preconditionSupplier, false, ifRulePreconditionMatch);
+                clazz,
+                memberKeepRules,
+                rule,
+                preconditionSupplier,
+                keepRule.getIncludeDescriptorClasses(),
+                false,
+                ifRulePreconditionMatch);
             markMatchingVisibleFields(
-                clazz, memberKeepRules, rule, preconditionSupplier, false, ifRulePreconditionMatch);
+                clazz,
+                memberKeepRules,
+                rule,
+                preconditionSupplier,
+                keepRule.getIncludeDescriptorClasses(),
+                false,
+                ifRulePreconditionMatch);
             break;
           case KEEP_CLASSES_WITH_MEMBERS:
             if (!allRulesSatisfied(memberKeepRules, clazz)) {
@@ -392,17 +413,28 @@
               // Static members in -keep are pinned no matter what.
               preconditionSupplier.put(DexDefinition::isStaticMember, null);
               // Instance members may need to be kept even though the holder is not instantiated.
-              preconditionSupplier.put(
-                  definition -> !definition.isStaticMember(), clazz.asProgramClass());
+              preconditionSupplier.put(definition -> !definition.isStaticMember(), clazz);
             } else {
               // Members mentioned at -keep should always be pinned as long as that -keep rule is
               // not triggered conditionally.
               preconditionSupplier.put(alwaysTrue(), null);
             }
             markMatchingVisibleMethods(
-                clazz, memberKeepRules, rule, preconditionSupplier, false, ifRulePreconditionMatch);
+                clazz,
+                memberKeepRules,
+                rule,
+                preconditionSupplier,
+                keepRule.getIncludeDescriptorClasses(),
+                false,
+                ifRulePreconditionMatch);
             markMatchingVisibleFields(
-                clazz, memberKeepRules, rule, preconditionSupplier, false, ifRulePreconditionMatch);
+                clazz,
+                memberKeepRules,
+                rule,
+                preconditionSupplier,
+                keepRule.getIncludeDescriptorClasses(),
+                false,
+                ifRulePreconditionMatch);
             break;
           case CONDITIONAL:
             throw new Unreachable("-if rule will be evaluated separately, not here.");
@@ -423,25 +455,25 @@
           || rule instanceof ProguardWhyAreYouKeepingRule) {
         markClass(clazz, rule, ifRulePreconditionMatch);
         markMatchingVisibleMethods(
-            clazz, memberKeepRules, rule, null, true, ifRulePreconditionMatch);
+            clazz, memberKeepRules, rule, null, true, true, ifRulePreconditionMatch);
         markMatchingVisibleFields(
-            clazz, memberKeepRules, rule, null, true, ifRulePreconditionMatch);
+            clazz, memberKeepRules, rule, null, true, true, ifRulePreconditionMatch);
       } else if (rule instanceof ProguardAssumeMayHaveSideEffectsRule) {
         markMatchingVisibleMethods(
-            clazz, memberKeepRules, rule, null, true, ifRulePreconditionMatch);
+            clazz, memberKeepRules, rule, null, true, true, ifRulePreconditionMatch);
         markMatchingOverriddenMethods(
-            clazz, memberKeepRules, rule, null, true, ifRulePreconditionMatch);
+            clazz, memberKeepRules, rule, null, true, true, ifRulePreconditionMatch);
         markMatchingVisibleFields(
-            clazz, memberKeepRules, rule, null, true, ifRulePreconditionMatch);
+            clazz, memberKeepRules, rule, null, true, true, ifRulePreconditionMatch);
       } else if (rule instanceof ProguardAssumeNoSideEffectRule
           || rule instanceof ProguardAssumeValuesRule) {
         if (assumeInfoCollectionBuilder != null) {
           markMatchingVisibleMethods(
-              clazz, memberKeepRules, rule, null, true, ifRulePreconditionMatch);
+              clazz, memberKeepRules, rule, null, true, true, ifRulePreconditionMatch);
           markMatchingOverriddenMethods(
-              clazz, memberKeepRules, rule, null, true, ifRulePreconditionMatch);
+              clazz, memberKeepRules, rule, null, true, true, ifRulePreconditionMatch);
           markMatchingVisibleFields(
-              clazz, memberKeepRules, rule, null, true, ifRulePreconditionMatch);
+              clazz, memberKeepRules, rule, null, true, true, ifRulePreconditionMatch);
         }
       } else if (rule instanceof NoFieldTypeStrengtheningRule
           || rule instanceof NoRedundantFieldLoadEliminationRule) {
@@ -468,9 +500,9 @@
         }
       } else if (rule instanceof NoValuePropagationRule) {
         markMatchingVisibleMethods(
-            clazz, memberKeepRules, rule, null, true, ifRulePreconditionMatch);
+            clazz, memberKeepRules, rule, null, true, true, ifRulePreconditionMatch);
         markMatchingVisibleFields(
-            clazz, memberKeepRules, rule, null, true, ifRulePreconditionMatch);
+            clazz, memberKeepRules, rule, null, true, true, ifRulePreconditionMatch);
       } else if (rule instanceof ProguardIdentifierNameStringRule) {
         markMatchingFields(clazz, memberKeepRules, rule, null, ifRulePreconditionMatch);
         markMatchingMethods(clazz, memberKeepRules, rule, null, ifRulePreconditionMatch);
@@ -506,7 +538,12 @@
                 application.classes(),
                 alwaysTrue(),
                 clazz -> process(clazz, rule, ifRulePreconditionMatch));
-            if (rule.applyToNonProgramClasses()) {
+            if (rule.isApplicableToClasspathClasses()) {
+              for (DexClasspathClass clazz : application.classpathClasses()) {
+                process(clazz, rule, ifRulePreconditionMatch);
+              }
+            }
+            if (rule.isApplicableToLibraryClasses()) {
               for (DexLibraryClass clazz : application.libraryClasses()) {
                 process(clazz, rule, ifRulePreconditionMatch);
               }
@@ -514,7 +551,7 @@
           });
     }
 
-    public RootSet build(ExecutorService executorService) throws ExecutionException {
+    public RootSetBuilder evaluateRules(ExecutorService executorService) throws ExecutionException {
       application.timing.begin("Build root set...");
       try {
         TaskCollection<?> tasks = new TaskCollection<>(options, executorService);
@@ -551,6 +588,10 @@
                       alwaysInline,
                       dependentMinimumKeepInfo)
                   .extendRootSet(dependentMinimumKeepInfo));
+      return this;
+    }
+
+    public RootSet build() {
       return new RootSet(
           dependentMinimumKeepInfo,
           ImmutableList.copyOf(reasonAsked.values()),
@@ -568,6 +609,12 @@
           rootNonProgramTypes);
     }
 
+    public RootSet evaluateRulesAndBuild(ExecutorService executorService)
+        throws ExecutionException {
+      evaluateRules(executorService);
+      return build();
+    }
+
     private void propagateAssumeRules(DexClass clazz) {
       List<DexClass> subclasses = subtypingInfo.getSubclasses(clazz);
       if (subclasses.isEmpty()) {
@@ -634,16 +681,14 @@
           pendingMethodMoveInverse);
     }
 
-    private static DexProgramClass testAndGetPrecondition(
-        DexDefinition definition,
-        Map<Predicate<DexDefinition>, DexProgramClass> preconditionSupplier) {
+    private static DexClass testAndGetPrecondition(
+        DexDefinition definition, Map<Predicate<DexDefinition>, DexClass> preconditionSupplier) {
       if (preconditionSupplier == null) {
         return null;
       }
-      DexProgramClass precondition = null;
+      DexClass precondition = null;
       boolean conditionEverMatched = false;
-      for (Entry<Predicate<DexDefinition>, DexProgramClass> entry :
-          preconditionSupplier.entrySet()) {
+      for (Entry<Predicate<DexDefinition>, DexClass> entry : preconditionSupplier.entrySet()) {
         if (entry.getKey().test(definition)) {
           precondition = entry.getValue();
           conditionEverMatched = true;
@@ -660,7 +705,8 @@
         DexClass clazz,
         Collection<ProguardMemberRule> memberKeepRules,
         ProguardConfigurationRule rule,
-        Map<Predicate<DexDefinition>, DexProgramClass> preconditionSupplier,
+        Map<Predicate<DexDefinition>, DexClass> preconditionSupplier,
+        boolean includeClasspathClasses,
         boolean includeLibraryClasses,
         ProguardIfRulePreconditionMatch ifRulePreconditionMatch) {
       Set<Wrapper<DexMethod>> methodsMarked =
@@ -669,7 +715,10 @@
       worklist.add(clazz);
       while (!worklist.isEmpty()) {
         DexClass currentClass = worklist.pop();
-        if (!includeLibraryClasses && currentClass.isNotProgramClass()) {
+        if (!includeClasspathClasses && currentClass.isClasspathClass()) {
+          break;
+        }
+        if (!includeLibraryClasses && currentClass.isLibraryClass()) {
           break;
         }
         // In compat mode traverse all direct methods in the hierarchy.
@@ -680,7 +729,7 @@
                     || (method.isStatic() && !method.isPrivate() && !method.isInitializer())
                     || options.forceProguardCompatibility,
             method -> {
-              DexProgramClass precondition =
+              DexClass precondition =
                   testAndGetPrecondition(method.getDefinition(), preconditionSupplier);
               markMethod(
                   method,
@@ -723,7 +772,7 @@
       private final DexProgramClass originalClazz;
       private final Collection<ProguardMemberRule> memberKeepRules;
       private final ProguardConfigurationRule context;
-      private final Map<Predicate<DexDefinition>, DexProgramClass> preconditionSupplier;
+      private final Map<Predicate<DexDefinition>, DexClass> preconditionSupplier;
       private final ProguardIfRulePreconditionMatch ifRulePreconditionMatch;
       private final Set<Wrapper<DexMethod>> seenMethods = Sets.newHashSet();
       private final Set<DexType> seenTypes = Sets.newIdentityHashSet();
@@ -732,7 +781,7 @@
           DexProgramClass originalClazz,
           Collection<ProguardMemberRule> memberKeepRules,
           ProguardConfigurationRule context,
-          Map<Predicate<DexDefinition>, DexProgramClass> preconditionSupplier,
+          Map<Predicate<DexDefinition>, DexClass> preconditionSupplier,
           ProguardIfRulePreconditionMatch ifRulePreconditionMatch) {
         assert context.isProguardKeepRule();
         assert !context.asProguardKeepRule().getModifiers().allowsShrinking;
@@ -826,7 +875,7 @@
                 methodToKeep,
                 resolutionMethod,
                 (rootSetBuilder) -> {
-                  DexProgramClass precondition =
+                  DexClass precondition =
                       testAndGetPrecondition(methodToKeep.getDefinition(), preconditionSupplier);
                   rootSetBuilder.addItemToSets(
                       methodToKeep, context, rule, precondition, ifRulePreconditionMatch);
@@ -843,8 +892,9 @@
         DexClass clazz,
         Collection<ProguardMemberRule> memberKeepRules,
         ProguardConfigurationRule rule,
-        Map<Predicate<DexDefinition>, DexProgramClass> preconditionSupplier,
-        boolean onlyIncludeProgramClasses,
+        Map<Predicate<DexDefinition>, DexClass> preconditionSupplier,
+        boolean includeClasspathClasses,
+        boolean includeLibraryClasses,
         ProguardIfRulePreconditionMatch ifRulePreconditionMatch) {
       Set<DexClass> visited = Sets.newIdentityHashSet();
       Deque<DexClass> worklist = new ArrayDeque<>();
@@ -857,13 +907,16 @@
         if (!visited.add(currentClass)) {
           continue;
         }
-        if (!onlyIncludeProgramClasses && currentClass.isNotProgramClass()) {
+        if (!includeClasspathClasses && currentClass.isClasspathClass()) {
+          continue;
+        }
+        if (!includeLibraryClasses && currentClass.isLibraryClass()) {
           continue;
         }
         currentClass.forEachClassMethodMatching(
             DexEncodedMethod::belongsToVirtualPool,
             method -> {
-              DexProgramClass precondition =
+              DexClass precondition =
                   testAndGetPrecondition(method.getDefinition(), preconditionSupplier);
               markMethod(
                   method, memberKeepRules, null, rule, precondition, ifRulePreconditionMatch);
@@ -876,11 +929,11 @@
         DexClass clazz,
         Collection<ProguardMemberRule> memberKeepRules,
         ProguardConfigurationRule rule,
-        Map<Predicate<DexDefinition>, DexProgramClass> preconditionSupplier,
+        Map<Predicate<DexDefinition>, DexClass> preconditionSupplier,
         ProguardIfRulePreconditionMatch ifRulePreconditionMatch) {
       clazz.forEachClassMethod(
           method -> {
-            DexProgramClass precondition =
+            DexClass precondition =
                 testAndGetPrecondition(method.getDefinition(), preconditionSupplier);
             markMethod(method, memberKeepRules, null, rule, precondition, ifRulePreconditionMatch);
           });
@@ -890,16 +943,20 @@
         DexClass clazz,
         Collection<ProguardMemberRule> memberKeepRules,
         ProguardConfigurationRule rule,
-        Map<Predicate<DexDefinition>, DexProgramClass> preconditionSupplier,
+        Map<Predicate<DexDefinition>, DexClass> preconditionSupplier,
+        boolean includeClasspathClasses,
         boolean includeLibraryClasses,
         ProguardIfRulePreconditionMatch ifRulePreconditionMatch) {
       while (clazz != null) {
-        if (!includeLibraryClasses && clazz.isNotProgramClass()) {
+        if (!includeClasspathClasses && clazz.isClasspathClass()) {
+          return;
+        }
+        if (!includeLibraryClasses && clazz.isLibraryClass()) {
           return;
         }
         clazz.forEachClassField(
             field -> {
-              DexProgramClass precondition =
+              DexClass precondition =
                   testAndGetPrecondition(field.getDefinition(), preconditionSupplier);
               markField(field, memberKeepRules, rule, precondition, ifRulePreconditionMatch);
             });
@@ -911,11 +968,11 @@
         DexClass clazz,
         Collection<ProguardMemberRule> memberKeepRules,
         ProguardConfigurationRule rule,
-        Map<Predicate<DexDefinition>, DexProgramClass> preconditionSupplier,
+        Map<Predicate<DexDefinition>, DexClass> preconditionSupplier,
         ProguardIfRulePreconditionMatch ifRulePreconditionMatch) {
       clazz.forEachClassField(
           field -> {
-            DexProgramClass precondition =
+            DexClass precondition =
                 testAndGetPrecondition(field.getDefinition(), preconditionSupplier);
             markField(field, memberKeepRules, rule, precondition, ifRulePreconditionMatch);
           });
@@ -1231,7 +1288,7 @@
         Collection<ProguardMemberRule> rules,
         Set<Wrapper<DexMethod>> methodsMarked,
         ProguardConfigurationRule context,
-        DexProgramClass precondition,
+        DexClass precondition,
         ProguardIfRulePreconditionMatch ifRulePreconditionMatch) {
       if (methodsMarked != null
           && methodsMarked.contains(MethodSignatureEquivalence.get().wrap(method.getReference()))) {
@@ -1252,7 +1309,7 @@
         DexClassAndField field,
         Collection<ProguardMemberRule> rules,
         ProguardConfigurationRule context,
-        DexProgramClass precondition,
+        DexClass precondition,
         ProguardIfRulePreconditionMatch ifRulePreconditionMatch) {
       for (ProguardMemberRule rule : rules) {
         if (rule.matches(field, appView, this::handleMatchedAnnotation, dexStringCache)) {
@@ -1304,15 +1361,17 @@
     }
 
     private void includeDescriptorClasses(
-        ProgramDefinition item, ProguardKeepRuleBase rule, EnqueuerEvent preconditionEvent) {
+        ProgramOrClasspathDefinition item,
+        ProguardKeepRuleBase rule,
+        EnqueuerEvent preconditionEvent) {
       if (item.isMethod()) {
-        ProgramMethod method = item.asProgramMethod();
+        DexClassAndMethod method = item.asMethod();
         includeDescriptor(method.getReturnType(), rule, preconditionEvent);
         for (DexType value : method.getParameters()) {
           includeDescriptor(value, rule, preconditionEvent);
         }
       } else if (item.isField()) {
-        ProgramField field = item.asProgramField();
+        DexClassAndField field = item.asField();
         includeDescriptor(field.getType(), rule, preconditionEvent);
       } else {
         assert item.isClass();
@@ -1323,18 +1382,27 @@
         Definition item,
         ProguardConfigurationRule context,
         ProguardMemberRule rule,
-        DexProgramClass precondition,
+        DexClass precondition,
         ProguardIfRulePreconditionMatch ifRulePreconditionMatch) {
       if (context.isProguardKeepRule()) {
-        if (!item.isProgramDefinition()) {
-          // Keep rules do not apply to non-program items.
+        if (item.isLibraryDefinition()) {
+          // Keep rules do not apply to library definitions.
           return;
         }
-        evaluateKeepRule(
-            item.asProgramDefinition(),
-            context.asProguardKeepRule(),
-            precondition,
-            ifRulePreconditionMatch);
+        ProguardKeepRule keepRule = context.asProguardKeepRule();
+        // Keep rules do not apply the classpath definitions except in the presence of
+        // `,includedescriptorclasses`.
+        if (item.isClasspathDefinition() && !keepRule.getIncludeDescriptorClasses()) {
+          return;
+        }
+        assert item.isProgramDefinition() || item.isClasspathDefinition();
+        if (item.isProgramDefinition()) {
+          evaluateKeepRule(
+              item.asProgramDefinition(), keepRule, precondition, ifRulePreconditionMatch);
+        } else {
+          evaluateKeepRuleOnClasspath(
+              item.asClasspathDefinition(), keepRule, ifRulePreconditionMatch);
+        }
       } else if (context instanceof ProguardAssumeMayHaveSideEffectsRule) {
         mayHaveSideEffects.put(item.getReference(), rule);
         context.markAsUsed();
@@ -1570,9 +1638,22 @@
       if (clazz.isProgramClass()) {
         evaluateCheckDiscardRule(clazz.asProgramClass(), rule.asProguardCheckDiscardRule());
       } else {
+        boolean isR8PartialExcludedClass =
+            clazz.isClasspathClass()
+                && options.partialSubCompilationConfiguration != null
+                && options.partialSubCompilationConfiguration.asR8().isD8Definition(clazz);
         StringDiagnostic warning =
-            new StringDiagnostic("The rule `" + rule + "` matches a class not in the program.");
+            isR8PartialExcludedClass
+                ? new StringDiagnostic(
+                    "The rule `"
+                        + rule
+                        + "` matches a class that is excluded from optimization in R8.")
+                : new StringDiagnostic(
+                    "The rule `" + rule + "` matches a class not in the program.");
         appView.reporter().warning(warning);
+
+        // Mark the rule as used to avoid reporting two diagnostics for the same rule.
+        rule.markAsUsed();
       }
     }
 
@@ -1709,7 +1790,7 @@
     private void evaluateKeepRule(
         ProgramDefinition item,
         ProguardKeepRule context,
-        DexProgramClass precondition,
+        DexClass precondition,
         ProguardIfRulePreconditionMatch ifRulePreconditionMatch) {
       // The reason for keeping should link to the conditional rule as a whole, if present.
       ProguardKeepRuleBase whyAreYouKeepingKeepRule =
@@ -1727,7 +1808,7 @@
 
     private void evaluateKeepRule(
         ProgramDefinition item,
-        DexProgramClass precondition,
+        DexClass precondition,
         ProguardIfRulePreconditionMatch ifRulePreconditionMatch,
         ProguardKeepRuleModifiers modifiers,
         Action markAsUsed,
@@ -1777,12 +1858,13 @@
       }
 
       EnqueuerEvent preconditionEvent;
-      if (precondition != null) {
+      if (precondition != null && precondition.isProgramClass()) {
+        DexProgramClass programPrecondition = precondition.asProgramClass();
         preconditionEvent =
             item.getAccessFlags().isStatic()
                     || (item.isMethod() && item.asMethod().getDefinition().isInstanceInitializer())
-                ? new LiveClassEnqueuerEvent(precondition)
-                : new InstantiatedClassEnqueuerEvent(precondition);
+                ? new LiveClassEnqueuerEvent(programPrecondition)
+                : new InstantiatedClassEnqueuerEvent(programPrecondition);
       } else {
         preconditionEvent = UnconditionalKeepInfoEvent.get();
       }
@@ -1908,6 +1990,23 @@
       assert !itemJoiner.isSet() || !itemJoiner.computeIfAbsent().isBottom();
     }
 
+    private void evaluateKeepRuleOnClasspath(
+        ClasspathDefinition item,
+        ProguardKeepRule context,
+        ProguardIfRulePreconditionMatch ifRulePreconditionMatch) {
+      if (context.getIncludeDescriptorClasses()) {
+        // Classpath classes are unconditionally live.
+        EnqueuerEvent preconditionEvent = UnconditionalKeepInfoEvent.get();
+        // The reason for keeping should link to the conditional rule as a whole, if present.
+        ProguardKeepRuleBase whyAreYouKeepingKeepRule =
+            ifRulePreconditionMatch != null
+                ? ifRulePreconditionMatch.getIfRuleWithPreconditionSet()
+                : context;
+        includeDescriptorClasses(item, whyAreYouKeepingKeepRule, preconditionEvent);
+        context.markAsUsed();
+      }
+    }
+
     private RetentionInfo getRetentionFromAttributeConfig(boolean visible, boolean invisible) {
       if (visible && invisible) {
         return RetentionInfo.getRetainAll();
@@ -2490,9 +2589,10 @@
     }
 
     @Override
-    public MainDexRootSet build(ExecutorService executorService) throws ExecutionException {
+    public MainDexRootSet evaluateRulesAndBuild(ExecutorService executorService)
+        throws ExecutionException {
       // Call the super builder to have if-tests calculated automatically.
-      RootSet rootSet = super.build(executorService);
+      RootSet rootSet = super.evaluateRulesAndBuild(executorService);
       return new MainDexRootSet(
           rootSet.getDependentMinimumKeepInfo(),
           rootSet.reasonAsked,
diff --git a/src/main/java/com/android/tools/r8/shaking/reflectiveidentification/EnqueuerReflectiveIdentificationEventConsumer.java b/src/main/java/com/android/tools/r8/shaking/reflectiveidentification/EnqueuerReflectiveIdentificationEventConsumer.java
index 4614b7e..20aa5be 100644
--- a/src/main/java/com/android/tools/r8/shaking/reflectiveidentification/EnqueuerReflectiveIdentificationEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/shaking/reflectiveidentification/EnqueuerReflectiveIdentificationEventConsumer.java
@@ -7,10 +7,12 @@
 
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.Definition;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.ProgramField;
+import com.android.tools.r8.graph.ProgramMember;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.shaking.Enqueuer;
 import com.android.tools.r8.shaking.EnqueuerWorklist;
@@ -216,4 +218,9 @@
       }
     }
   }
+
+  @Override
+  public void onIdentifierNameString(Definition definition, ProgramMember<?, ?> context) {
+    // Intentionally empty. Items that are used by reflection should be kept.
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/shaking/reflectiveidentification/KeepAllReflectiveIdentificationEventConsumer.java b/src/main/java/com/android/tools/r8/shaking/reflectiveidentification/KeepAllReflectiveIdentificationEventConsumer.java
index 10ad31f..ea733a4 100644
--- a/src/main/java/com/android/tools/r8/shaking/reflectiveidentification/KeepAllReflectiveIdentificationEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/shaking/reflectiveidentification/KeepAllReflectiveIdentificationEventConsumer.java
@@ -9,6 +9,7 @@
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.ProgramField;
+import com.android.tools.r8.graph.ProgramMember;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.partial.R8PartialUseCollector;
 import java.util.Collection;
@@ -23,7 +24,7 @@
     this.useCollector = useCollector;
   }
 
-  private void keep(Definition definition, ProgramMethod context) {
+  private void keep(Definition definition, ProgramMember<?, ?> context) {
     DefinitionContext referencedFrom = DefinitionContextUtils.create(context);
     useCollector.keep(definition, referencedFrom, false);
   }
@@ -99,4 +100,9 @@
       }
     }
   }
+
+  @Override
+  public void onIdentifierNameString(Definition definition, ProgramMember<?, ?> context) {
+    keep(definition, context);
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/shaking/reflectiveidentification/ReflectiveIdentification.java b/src/main/java/com/android/tools/r8/shaking/reflectiveidentification/ReflectiveIdentification.java
index 446f12a..5d49cb7 100644
--- a/src/main/java/com/android/tools/r8/shaking/reflectiveidentification/ReflectiveIdentification.java
+++ b/src/main/java/com/android/tools/r8/shaking/reflectiveidentification/ReflectiveIdentification.java
@@ -10,6 +10,7 @@
 
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.Definition;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexItemFactory;
@@ -17,6 +18,7 @@
 import com.android.tools.r8.graph.DexMember;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.ProgramField;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -30,13 +32,16 @@
 import com.android.tools.r8.ir.code.NewArrayFilled;
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.ir.conversion.MethodConversionOptions;
+import com.android.tools.r8.naming.IdentifierNameStringUtils;
 import com.android.tools.r8.naming.identifiernamestring.IdentifierNameStringLookupResult;
 import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.collections.ProgramFieldMap;
 import com.android.tools.r8.utils.collections.ProgramMethodSet;
 import com.android.tools.r8.utils.timing.Timing;
 import com.google.common.collect.Sets;
 import java.lang.reflect.InvocationHandler;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Set;
 import java.util.function.Consumer;
@@ -46,20 +51,30 @@
   private final AppView<? extends AppInfoWithClassHierarchy> appView;
   private final DexItemFactory factory;
   private final ReflectiveIdentificationEventConsumer eventConsumer;
+  private final Set<DexMember<?, ?>> identifierNameStrings;
 
-  private final Set<DexMember<?, ?>> identifierNameStrings = Sets.newIdentityHashSet();
+  private final Set<DexMember<?, ?>> identifierNameStringAdditions = Sets.newIdentityHashSet();
+  private final ProgramFieldMap<Definition> fieldReferencesWorklist = ProgramFieldMap.create();
   private final ProgramMethodSet worklist = ProgramMethodSet.create();
 
   public ReflectiveIdentification(
       AppView<? extends AppInfoWithClassHierarchy> appView,
       ReflectiveIdentificationEventConsumer eventConsumer) {
+    this(appView, eventConsumer, Collections.emptySet());
+  }
+
+  public ReflectiveIdentification(
+      AppView<? extends AppInfoWithClassHierarchy> appView,
+      ReflectiveIdentificationEventConsumer eventConsumer,
+      Set<DexMember<?, ?>> identifierNameStrings) {
     this.appView = appView;
     this.factory = appView.dexItemFactory();
     this.eventConsumer = eventConsumer;
+    this.identifierNameStrings = identifierNameStrings;
   }
 
-  public Set<DexMember<?, ?>> getIdentifierNameStrings() {
-    return identifierNameStrings;
+  public Set<DexMember<?, ?>> getIdentifierNameStringAdditions() {
+    return identifierNameStringAdditions;
   }
 
   public void scanInvoke(DexMethod invokedMethod, ProgramMethod method) {
@@ -72,7 +87,7 @@
       } else if (classMethods.isReflectiveClassLookup(invokedMethod)
           || classMethods.isReflectiveMemberLookup(invokedMethod)) {
         // Implicitly add -identifiernamestring rule for the Java reflection in use.
-        identifierNameStrings.add(invokedMethod);
+        identifierNameStringAdditions.add(invokedMethod);
         enqueue(method);
       }
     } else if (holder.isIdenticalTo(factory.constructorType)) {
@@ -97,29 +112,44 @@
       // java.util.concurrent.atomic.AtomicLongFieldUpdater
       // java.util.concurrent.atomic.AtomicReferenceFieldUpdater
       if (factory.atomicFieldUpdaterMethods.isFieldUpdater(invokedMethod)) {
-        identifierNameStrings.add(invokedMethod);
+        identifierNameStringAdditions.add(invokedMethod);
         enqueue(method);
       }
     }
+    if (identifierNameStrings.contains(invokedMethod)) {
+      enqueue(method);
+    }
   }
 
-  public void enqueue(ProgramMethod method) {
+  // The given field has a static value that references the given definition. Upon processing the
+  // worklist, emit this to the event consumer.
+  public synchronized void enqueue(ProgramField field, Definition definition) {
+    fieldReferencesWorklist.put(field, definition);
+  }
+
+  public synchronized void enqueue(ProgramMethod method) {
     worklist.add(method);
   }
 
+  // TODO(b/414944282): Parallelize reflective identification.
   public void processWorklist(Timing timing) {
-    if (worklist.isEmpty()) {
-      return;
-    }
     timing.begin("Reflective identification");
-    // TODO(b/414944282): Parallelize reflective identification.
-    for (ProgramMethod method : worklist) {
-      processMethod(method);
-    }
+
+    // Process fields.
+    fieldReferencesWorklist.forEach(this::processField);
+    fieldReferencesWorklist.clear();
+
+    // Process methods.
+    worklist.forEach(this::processMethod);
     worklist.clear();
+
     timing.end();
   }
 
+  private void processField(ProgramField field, Definition definition) {
+    eventConsumer.onIdentifierNameString(definition, field);
+  }
+
   private void processMethod(ProgramMethod method) {
     IRCode code = method.buildIR(appView, MethodConversionOptions.nonConverting());
     for (InvokeMethod invoke : code.<InvokeMethod>instructions(Instruction::isInvokeMethod)) {
@@ -201,6 +231,10 @@
         return true;
       }
     }
+    if (identifierNameStrings.contains(invokedMethod)) {
+      handleIdentifierNameStringMethod(method, invoke);
+      return true;
+    }
     return false;
   }
 
@@ -484,4 +518,17 @@
       eventConsumer.onJavaUtilServiceLoaderLoad(serviceClass, implementationClasses, context);
     }
   }
+
+  private void handleIdentifierNameStringMethod(ProgramMethod method, InvokeMethod invoke) {
+    for (Value argument : invoke.arguments()) {
+      DexString string = argument.getConstStringOrNull();
+      if (string != null) {
+        Definition definition =
+            IdentifierNameStringUtils.inferMemberOrTypeFromNameString(appView, string);
+        if (definition != null) {
+          eventConsumer.onIdentifierNameString(definition, method);
+        }
+      }
+    }
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/shaking/reflectiveidentification/ReflectiveIdentificationEventConsumer.java b/src/main/java/com/android/tools/r8/shaking/reflectiveidentification/ReflectiveIdentificationEventConsumer.java
index 0488e06..d6255c3 100644
--- a/src/main/java/com/android/tools/r8/shaking/reflectiveidentification/ReflectiveIdentificationEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/shaking/reflectiveidentification/ReflectiveIdentificationEventConsumer.java
@@ -3,9 +3,11 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking.reflectiveidentification;
 
+import com.android.tools.r8.graph.Definition;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.ProgramField;
+import com.android.tools.r8.graph.ProgramMember;
 import com.android.tools.r8.graph.ProgramMethod;
 import java.util.Collection;
 import java.util.Set;
@@ -37,4 +39,6 @@
       DexProgramClass serviceClass,
       Collection<DexProgramClass> implementationClasses,
       ProgramMethod context);
+
+  void onIdentifierNameString(Definition definition, ProgramMember<?, ?> context);
 }
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
index 6da90de..03c0630 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
@@ -3,8 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.synthesis;
 
-import static com.google.common.base.Predicates.alwaysTrue;
-
 import com.android.tools.r8.features.ClassToFeatureSplitMap;
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
@@ -12,7 +10,6 @@
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedMember;
-import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
@@ -26,9 +23,11 @@
 import com.android.tools.r8.graph.lens.NestedGraphLens;
 import com.android.tools.r8.graph.lens.NonIdentityGraphLens;
 import com.android.tools.r8.ir.code.NumberGenerator;
+import com.android.tools.r8.partial.R8PartialSubCompilationConfiguration.R8PartialR8SubCompilationConfiguration;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.KeepClassInfo;
 import com.android.tools.r8.shaking.KeepInfoCollection;
+import com.android.tools.r8.shaking.KeepMethodInfo.Joiner;
 import com.android.tools.r8.shaking.MainDexInfo;
 import com.android.tools.r8.shaking.SyntheticKeepClassInfo;
 import com.android.tools.r8.synthesis.SyntheticItems.State;
@@ -498,14 +497,24 @@
           SyntheticMethodDefinition representative = syntheticGroup.getRepresentative();
           assert externalSyntheticClass.getMethodCollection().size() == 1;
           assert externalSyntheticClass.getMethodCollection().hasDirectMethods();
-          DexEncodedMethod syntheticMethodDefinition =
-              externalSyntheticClass.getMethodCollection().getDirectMethod(alwaysTrue());
+          ProgramMethod syntheticMethodDefinition =
+              IterableUtils.first(externalSyntheticClass.directProgramMethods());
           addFinalSyntheticMethod.accept(
               externalSyntheticClass,
               new SyntheticMethodReference(
                   representative.getKind(),
                   representative.getContext(),
                   syntheticMethodDefinition.getReference()));
+
+          // If the synthetic group contains a method that has been synthesized from an excluded
+          // class in R8 partial, then disallow inlining of the shared synthetic method. This
+          // ensures that we do not incorrectly single caller inline the shared synthetic method if
+          // it has a single call site in the included part of R8 partial.
+          if (isSyntheticMethodCalledFromExcludedClassInPartial(appView, syntheticGroup)) {
+            appView
+                .getKeepInfo()
+                .mutate(m -> m.joinMethod(syntheticMethodDefinition, Joiner::disallowInlining));
+          }
         });
     timing.end();
 
@@ -542,6 +551,24 @@
     return application;
   }
 
+  private static boolean isSyntheticMethodCalledFromExcludedClassInPartial(
+      AppView<?> appView, EquivalenceGroup<SyntheticMethodDefinition> group) {
+    R8PartialR8SubCompilationConfiguration subCompilationConfiguration =
+        appView.options().partialSubCompilationConfiguration != null
+            ? appView.options().partialSubCompilationConfiguration.asR8()
+            : null;
+    if (subCompilationConfiguration != null) {
+      for (SyntheticMethodDefinition member :
+          IterableUtils.append(group.members, group.getRepresentative())) {
+        DexType synthesizingContextType = member.getContext().getSynthesizingContextType();
+        if (subCompilationConfiguration.hasD8DefinitionFor(synthesizingContextType)) {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
   private static <T extends SyntheticDefinition<?, T, ?>>
       boolean verifyNonRepresentativesRemovedFromApplication(
           DexApplication application, Map<DexType, EquivalenceGroup<T>> syntheticGroups) {
diff --git a/src/main/java/com/android/tools/r8/tracereferences/UseCollector.java b/src/main/java/com/android/tools/r8/tracereferences/UseCollector.java
index 2152274..c03b71b 100644
--- a/src/main/java/com/android/tools/r8/tracereferences/UseCollector.java
+++ b/src/main/java/com/android/tools/r8/tracereferences/UseCollector.java
@@ -311,7 +311,7 @@
     return appView.appInfo();
   }
 
-  private boolean isTargetType(DexType type) {
+  protected final boolean isTargetType(DexType type) {
     return targetPredicate.test(type);
   }
 
@@ -428,9 +428,14 @@
             dexAnnotation ->
                 registerAnnotation(
                     dexAnnotation, field.getHolder(), referencedFrom, eventConsumer));
+    traceFieldValue(field);
     traceSignature(field, referencedFrom, eventConsumer);
   }
 
+  protected void traceFieldValue(ProgramField field) {
+    // Intentionally empty. Overridden in R8PartialUseCollector.
+  }
+
   private void registerMethod(ProgramMethod method, UseCollectorEventConsumer eventConsumer) {
     DefinitionContext referencedFrom = DefinitionContextUtils.create(method);
     UseCollectorEventConsumer signatureEventConsumer =
diff --git a/src/test/java/com/android/tools/r8/annotations/SourceDebugExtensionTest.java b/src/test/java/com/android/tools/r8/annotations/SourceDebugExtensionTest.java
index 27b7603..5bc862d 100644
--- a/src/test/java/com/android/tools/r8/annotations/SourceDebugExtensionTest.java
+++ b/src/test/java/com/android/tools/r8/annotations/SourceDebugExtensionTest.java
@@ -38,7 +38,7 @@
   public static List<Object[]> data() {
     return buildParameters(
         getTestParameters().withAllRuntimesAndApiLevels().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build());
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build());
   }
 
   public SourceDebugExtensionTest(
diff --git a/src/test/java/com/android/tools/r8/cf/bootstrap/KotlinCompilerTreeShakingTest.java b/src/test/java/com/android/tools/r8/cf/bootstrap/KotlinCompilerTreeShakingTest.java
index a49e001..c35f84b 100644
--- a/src/test/java/com/android/tools/r8/cf/bootstrap/KotlinCompilerTreeShakingTest.java
+++ b/src/test/java/com/android/tools/r8/cf/bootstrap/KotlinCompilerTreeShakingTest.java
@@ -44,7 +44,7 @@
   public static List<Object[]> data() {
     return buildParameters(
         getTestParameters().withCfRuntimes().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build());
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build());
   }
 
   public KotlinCompilerTreeShakingTest(
diff --git a/src/test/java/com/android/tools/r8/debug/ContinuousKotlinSteppingTest.java b/src/test/java/com/android/tools/r8/debug/ContinuousKotlinSteppingTest.java
index a159df8..488944b 100644
--- a/src/test/java/com/android/tools/r8/debug/ContinuousKotlinSteppingTest.java
+++ b/src/test/java/com/android/tools/r8/debug/ContinuousKotlinSteppingTest.java
@@ -21,7 +21,7 @@
   public static List<Object[]> data() {
     return buildParameters(
         getTestParameters().withDexRuntimes().withAllApiLevels().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build());
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build());
   }
 
   private final TestParameters parameters;
diff --git a/src/test/java/com/android/tools/r8/debug/ContinuousSteppingTest.java b/src/test/java/com/android/tools/r8/debug/ContinuousSteppingTest.java
index 6a33a13..813ef2d 100644
--- a/src/test/java/com/android/tools/r8/debug/ContinuousSteppingTest.java
+++ b/src/test/java/com/android/tools/r8/debug/ContinuousSteppingTest.java
@@ -84,9 +84,7 @@
                   temp)
               .configure(KotlinCompilerTool::includeRuntime);
       for (KotlinTestParameters kotlinParameter :
-          TestBase.getKotlinTestParameters()
-              .withAllCompilersLambdaGenerationsAndTargetVersions()
-              .build()) {
+          TestBase.getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build()) {
         add(compiledJars.getForConfiguration(kotlinParameter), predicate);
       }
       return this;
diff --git a/src/test/java/com/android/tools/r8/debug/KotlinInlineTest.java b/src/test/java/com/android/tools/r8/debug/KotlinInlineTest.java
index aae982c..96b801b 100644
--- a/src/test/java/com/android/tools/r8/debug/KotlinInlineTest.java
+++ b/src/test/java/com/android/tools/r8/debug/KotlinInlineTest.java
@@ -36,7 +36,7 @@
   public static List<Object[]> data() {
     return buildParameters(
         getTestParameters().withDexRuntimes().withAllApiLevels().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build());
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build());
   }
 
   public KotlinInlineTest(TestParameters parameters, KotlinTestParameters kotlinParameters) {
diff --git a/src/test/java/com/android/tools/r8/debug/KotlinLoopTest.java b/src/test/java/com/android/tools/r8/debug/KotlinLoopTest.java
index 5756f43..43c3a66 100644
--- a/src/test/java/com/android/tools/r8/debug/KotlinLoopTest.java
+++ b/src/test/java/com/android/tools/r8/debug/KotlinLoopTest.java
@@ -21,7 +21,7 @@
   public static List<Object[]> data() {
     return buildParameters(
         getTestParameters().withDexRuntimes().withAllApiLevels().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build());
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build());
   }
 
   public KotlinLoopTest(TestParameters parameters, KotlinTestParameters kotlinParameters) {
diff --git a/src/test/java/com/android/tools/r8/debug/KotlinTest.java b/src/test/java/com/android/tools/r8/debug/KotlinTest.java
index a0a9a12..0336fa3 100644
--- a/src/test/java/com/android/tools/r8/debug/KotlinTest.java
+++ b/src/test/java/com/android/tools/r8/debug/KotlinTest.java
@@ -24,7 +24,7 @@
   public static List<Object[]> data() {
     return buildParameters(
         getTestParameters().withDexRuntimes().withAllApiLevels().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build());
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build());
   }
 
   public KotlinTest(TestParameters parameters, KotlinTestParameters kotlinParameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/kotlin/KotlinBlogTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/kotlin/KotlinBlogTest.java
index 35de5d0..08b33dc 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/kotlin/KotlinBlogTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/kotlin/KotlinBlogTest.java
@@ -56,7 +56,7 @@
             .withDexRuntimesStartingFromIncluding(Version.V5_1_1)
             .withAllApiLevels()
             .build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build(),
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build(),
         ImmutableList.of(LibraryDesugaringSpecification.JDK11_PATH),
         DEFAULT_SPECIFICATIONS);
   }
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/kotlin/KotlinMetadataTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/kotlin/KotlinMetadataTest.java
index f993e24..c91b351 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/kotlin/KotlinMetadataTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/kotlin/KotlinMetadataTest.java
@@ -63,7 +63,7 @@
   public static List<Object[]> data() {
     return buildParameters(
         getTestParameters().withAllRuntimesAndApiLevels().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build(),
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build(),
         DEFAULT_SPECIFICATIONS,
         ImmutableList.of(LibraryDesugaringSpecification.JDK11));
   }
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/kotlin/SimpleKotlinEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/kotlin/SimpleKotlinEnumUnboxingTest.java
index ae75dfd..c912c03 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/kotlin/SimpleKotlinEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/kotlin/SimpleKotlinEnumUnboxingTest.java
@@ -41,7 +41,7 @@
   public static List<Object[]> enumUnboxingTestParameters() {
     return buildParameters(
         getTestParameters().withAllRuntimesAndApiLevels().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build(),
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build(),
         BooleanUtils.values(),
         getAllEnumKeepRules());
   }
diff --git a/src/test/java/com/android/tools/r8/internal/ClankDepsTest.java b/src/test/java/com/android/tools/r8/internal/ClankDepsTest.java
index 7bff736..68577b2 100644
--- a/src/test/java/com/android/tools/r8/internal/ClankDepsTest.java
+++ b/src/test/java/com/android/tools/r8/internal/ClankDepsTest.java
@@ -16,6 +16,7 @@
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringDiagnostic;
 import com.android.tools.r8.utils.UnverifiableCfCodeDiagnostic;
 import java.nio.file.Path;
 import java.nio.file.Paths;
@@ -70,7 +71,21 @@
                                 containsString(
                                     "Unverifiable code in `"
                                         + "void zzz.com.facebook.litho.ComponentHost"
-                                        + ".refreshAccessibilityDelegatesIfNeeded(boolean)`"))))
+                                        + ".refreshAccessibilityDelegatesIfNeeded(boolean)`"))),
+                        // Identifier name string warnings.
+                        allOf(
+                            diagnosticType(StringDiagnostic.class),
+                            diagnosticMessage(
+                                containsString(
+                                    "Cannot determine what 'zzz.com.google.common.flogger.backend"
+                                        + ".google.GooglePlatform' refers to"))),
+                        // Same, for 'zzz.com.google.common.flogger.backend.system.DefaultPlatform'.
+                        allOf(
+                            diagnosticType(StringDiagnostic.class),
+                            diagnosticMessage(
+                                containsString(
+                                    "Cannot determine what 'zzz.com.google.common.flogger.backend"
+                                        + ".system.DefaultPlatform' refers to"))))
                     .assertNoErrors());
   }
 }
diff --git a/src/test/java/com/android/tools/r8/ir/InlineTest.java b/src/test/java/com/android/tools/r8/ir/InlineTest.java
index 7c1a238..efbccf0 100644
--- a/src/test/java/com/android/tools/r8/ir/InlineTest.java
+++ b/src/test/java/com/android/tools/r8/ir/InlineTest.java
@@ -88,7 +88,7 @@
                 profileCollectionAdditions,
                 subtypingInfo,
                 ImmutableList.of(ProguardKeepRule.defaultKeepAllRule(unused -> {})))
-            .build(executorService));
+            .evaluateRulesAndBuild(executorService));
     Timing timing = Timing.empty();
     Enqueuer enqueuer =
         EnqueuerFactory.createForInitialTreeShaking(
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/conditionalsimpleinlining/SwitchWithSimpleCasesInliningKotlinTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/conditionalsimpleinlining/SwitchWithSimpleCasesInliningKotlinTest.java
index e668727..4d566b9 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/conditionalsimpleinlining/SwitchWithSimpleCasesInliningKotlinTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/conditionalsimpleinlining/SwitchWithSimpleCasesInliningKotlinTest.java
@@ -46,7 +46,7 @@
   public static List<Object[]> data() {
     return buildParameters(
         getTestParameters().withAllRuntimesAndApiLevels().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build());
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build());
   }
 
   public SwitchWithSimpleCasesInliningKotlinTest(
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceRemovalPackageBoundaryTest.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceRemovalPackageBoundaryTest.java
index adfe769..f6773e6 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceRemovalPackageBoundaryTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceRemovalPackageBoundaryTest.java
@@ -32,7 +32,7 @@
 
   @Parameters(name = "{0}")
   public static TestParametersCollection data() {
-    return getTestParameters().withAllRuntimesAndApiLevels().build();
+    return getTestParameters().withAllRuntimesAndApiLevels().withPartialCompilation().build();
   }
 
   public UnusedInterfaceRemovalPackageBoundaryTest(TestParameters parameters) {
@@ -41,16 +41,16 @@
 
   @Test
   public void test() throws Exception {
-    testForR8(parameters.getBackend())
+    testForR8(parameters)
         .addInnerClasses(getClass(), UnusedInterfaceRemovalPackageBoundaryTestClasses.class)
         .addKeepMainRule(TestClass.class)
         .addKeepClassRules(I_CLASS)
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .enableNoVerticalClassMergingAnnotations()
-        .setMinApi(parameters)
         .compile()
-        .inspect(
+        .inspectIf(
+            !parameters.isRandomPartialCompilation(),
             inspector -> {
               ClassSubject iClassSubject = inspector.clazz(I_CLASS);
               assertThat(iClassSubject, isPresent());
@@ -72,6 +72,41 @@
         .assertSuccessWithOutputLines("A");
   }
 
+  @Test
+  public void testIOnClasspath() throws Exception {
+    testForR8(parameters)
+        .addInnerClasses(getClass())
+        .addProgramClasses(UnusedInterfaceRemovalPackageBoundaryTestClasses.J.class)
+        .addClasspathClasses(UnusedInterfaceRemovalPackageBoundaryTestClasses.getI())
+        .addKeepMainRule(TestClass.class)
+        // TODO(b/410597153): Repackaging does not account for part of a program package being on
+        //  classpath. After fixing this, -dontobfuscate should not be needed.
+        .addDontObfuscate()
+        .enableInliningAnnotations()
+        .enableNeverClassInliningAnnotations()
+        .enableNoVerticalClassMergingAnnotations()
+        .compile()
+        .inspectIf(
+            !parameters.isRandomPartialCompilation(),
+            inspector -> {
+              ClassSubject jClassSubject = inspector.clazz(J_CLASS);
+              assertThat(jClassSubject, isPresent());
+
+              ClassSubject kClassSubject = inspector.clazz(K.class);
+              assertThat(kClassSubject, isAbsent());
+
+              ClassSubject aClassSubject = inspector.clazz(A.class);
+              assertThat(aClassSubject, isPresent());
+              assertEquals(1, aClassSubject.getDexProgramClass().getInterfaces().size());
+              assertEquals(
+                  jClassSubject.getDexProgramClass().getType(),
+                  aClassSubject.getDexProgramClass().getInterfaces().get(0));
+            })
+        .addRunClasspathClasses(UnusedInterfaceRemovalPackageBoundaryTestClasses.getI())
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutputLines("A");
+  }
+
   static class TestClass {
 
     public static void main(String[] args) {
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
index 471e37f..9646cc8 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
@@ -52,7 +52,7 @@
   public static List<Object[]> data() {
     return buildParameters(
         getTestParameters().withAllRuntimesAndApiLevels().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build());
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build());
   }
 
   public KotlinClassInlinerTest(TestParameters parameters, KotlinTestParameters kotlinParameters) {
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinClassStaticizerTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinClassStaticizerTest.java
index e71f9a8..99a3f08 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinClassStaticizerTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinClassStaticizerTest.java
@@ -29,7 +29,7 @@
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withAllRuntimesAndApiLevels().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build(),
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build(),
         BooleanUtils.values());
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineChainTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineChainTest.java
index d1a13ad..27394d6 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineChainTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineChainTest.java
@@ -4,7 +4,6 @@
 package com.android.tools.r8.kotlin;
 
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
 
@@ -32,7 +31,7 @@
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withAllRuntimesAndApiLevels().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build(),
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build(),
         BooleanUtils.values());
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineTest.java
index d097d6f..19ffda1 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineTest.java
@@ -4,7 +4,6 @@
 package com.android.tools.r8.kotlin;
 
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assume.assumeTrue;
@@ -32,7 +31,7 @@
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withAllRuntimesAndApiLevels().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build(),
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build(),
         BooleanUtils.values());
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedArgumentsInLambdasTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedArgumentsInLambdasTest.java
index 20ed92d..51b9935 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedArgumentsInLambdasTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedArgumentsInLambdasTest.java
@@ -25,7 +25,7 @@
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withAllRuntimesAndApiLevels().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build(),
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build(),
         BooleanUtils.values());
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedSingletonTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedSingletonTest.java
index 255063a..cac0c06 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedSingletonTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedSingletonTest.java
@@ -31,7 +31,7 @@
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withAllRuntimesAndApiLevels().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build(),
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build(),
         BooleanUtils.values());
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinReflectionLibTest.java b/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinReflectionLibTest.java
index 52ee123..010acba 100644
--- a/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinReflectionLibTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinReflectionLibTest.java
@@ -29,7 +29,7 @@
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withAllRuntimes().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build());
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build());
   }
 
   public ProcessKotlinReflectionLibTest(
diff --git a/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinStdlibTest.java b/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinStdlibTest.java
index c9ba267..d2e838c 100644
--- a/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinStdlibTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinStdlibTest.java
@@ -26,7 +26,7 @@
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withAllRuntimes().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build());
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build());
   }
 
   public ProcessKotlinStdlibTest(TestParameters parameters, KotlinTestParameters kotlinParameters) {
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
index 4493368..42b2876 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
@@ -62,7 +62,7 @@
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withAllRuntimesAndApiLevels().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build(),
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build(),
         BooleanUtils.values());
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java
index 706536e..c373e41 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java
@@ -39,7 +39,7 @@
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withAllRuntimesAndApiLevels().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build(),
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build(),
         BooleanUtils.values());
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinIntrinsicsTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinIntrinsicsTest.java
index 1d6ce95..eb55f31 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinIntrinsicsTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinIntrinsicsTest.java
@@ -29,7 +29,7 @@
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withAllRuntimesAndApiLevels().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build(),
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build(),
         BooleanUtils.values());
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java
index b1145e2..ce5c5a3 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java
@@ -98,7 +98,7 @@
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withAllRuntimesAndApiLevels().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build(),
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build(),
         BooleanUtils.values());
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java b/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
index b86c119..9f6cede 100644
--- a/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
@@ -27,7 +27,7 @@
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withAllRuntimesAndApiLevels().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build(),
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build(),
         BooleanUtils.values());
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/coroutines/KotlinxCoroutinesTestRunner.java b/src/test/java/com/android/tools/r8/kotlin/coroutines/KotlinxCoroutinesTestRunner.java
index e2ea45d..cb89c37 100644
--- a/src/test/java/com/android/tools/r8/kotlin/coroutines/KotlinxCoroutinesTestRunner.java
+++ b/src/test/java/com/android/tools/r8/kotlin/coroutines/KotlinxCoroutinesTestRunner.java
@@ -54,7 +54,7 @@
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withCfRuntimes().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build());
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build());
   }
 
   private final TestParameters parameters;
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergerValidationTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergerValidationTest.java
index 12c547e..e29b3c5 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergerValidationTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergerValidationTest.java
@@ -4,7 +4,6 @@
 package com.android.tools.r8.kotlin.lambda;
 
 import static com.android.tools.r8.ToolHelper.getJava8RuntimeJar;
-import static org.hamcrest.CoreMatchers.equalTo;
 import static org.junit.Assume.assumeTrue;
 
 import com.android.tools.r8.KotlinTestBase;
@@ -26,7 +25,7 @@
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withAllRuntimesAndApiLevels().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build());
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build());
   }
 
   public KotlinLambdaMergerValidationTest(
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingDebugTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingDebugTest.java
index 0aa1a8b..dcb53ea 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingDebugTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingDebugTest.java
@@ -25,7 +25,7 @@
   public static List<Object[]> data() {
     return buildParameters(
         getTestParameters().withDexRuntimes().withAllApiLevels().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build());
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build());
   }
 
   public KotlinLambdaMergingDebugTest(
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingKeepAttributesKotlinStyleTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingKeepAttributesKotlinStyleTest.java
index d0faa2b..da524c4 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingKeepAttributesKotlinStyleTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingKeepAttributesKotlinStyleTest.java
@@ -44,7 +44,7 @@
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withAllRuntimesAndApiLevels().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build(),
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build(),
         BooleanUtils.values(),
         ImmutableList.of(
             Collections.emptyList(),
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingSingletonTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingSingletonTest.java
index 9292803..c4a70db 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingSingletonTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingSingletonTest.java
@@ -38,7 +38,7 @@
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withAllRuntimesAndApiLevels().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build(),
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build(),
         BooleanUtils.values());
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithReprocessingTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithReprocessingTest.java
index a17d0d0..9648132 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithReprocessingTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithReprocessingTest.java
@@ -19,7 +19,7 @@
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withAllRuntimesAndApiLevels().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build(),
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build(),
         BooleanUtils.values());
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithSmallInliningBudgetTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithSmallInliningBudgetTest.java
index 77715854..fb46c82 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithSmallInliningBudgetTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithSmallInliningBudgetTest.java
@@ -19,7 +19,7 @@
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withAllRuntimesAndApiLevels().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build(),
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build(),
         BooleanUtils.values());
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/b148525512/B148525512.java b/src/test/java/com/android/tools/r8/kotlin/lambda/b148525512/B148525512.java
index 9c4f28b..c6c5b43 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/b148525512/B148525512.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/b148525512/B148525512.java
@@ -54,7 +54,7 @@
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withDexRuntimes().withAllApiLevels().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build());
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build());
   }
 
   public B148525512(TestParameters parameters, KotlinTestParameters kotlinParameters) {
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaSplitByCodeCorrectnessTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaSplitByCodeCorrectnessTest.java
index 1f3d1d6..bc008b4 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaSplitByCodeCorrectnessTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaSplitByCodeCorrectnessTest.java
@@ -7,7 +7,6 @@
 import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethod;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentIf;
-import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
 
@@ -39,7 +38,7 @@
   public static List<Object[]> data() {
     return buildParameters(
         getTestParameters().withDexRuntimesAndAllApiLevels().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build(),
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build(),
         BooleanUtils.values());
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataPrunedFieldsTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataPrunedFieldsTest.java
index 414661d..ee1d199 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataPrunedFieldsTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataPrunedFieldsTest.java
@@ -5,7 +5,6 @@
 package com.android.tools.r8.kotlin.metadata;
 
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
 
@@ -29,7 +28,7 @@
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withAllRuntimesAndApiLevels().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build());
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build());
   }
 
   public MetadataPrunedFieldsTest(
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteBoxedTypesTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteBoxedTypesTest.java
index ee09d10..656a366 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteBoxedTypesTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteBoxedTypesTest.java
@@ -25,7 +25,7 @@
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withCfRuntimes().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build());
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build());
   }
 
   public MetadataRewriteBoxedTypesTest(
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDependentKeepTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDependentKeepTest.java
index aceb999..65513b4 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDependentKeepTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDependentKeepTest.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.kotlin.metadata;
 
-import static org.hamcrest.CoreMatchers.equalTo;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 
@@ -27,7 +26,7 @@
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withCfRuntimes().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build());
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build());
   }
 
   private final TestParameters parameters;
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDoNotEmitValuesIfEmptyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDoNotEmitValuesIfEmptyTest.java
index 01d5403..f6df376 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDoNotEmitValuesIfEmptyTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDoNotEmitValuesIfEmptyTest.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.kotlin.metadata;
 
-import static org.hamcrest.CoreMatchers.equalTo;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
 
@@ -30,7 +29,7 @@
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withCfRuntimes().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build());
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build());
   }
 
   private final TestParameters parameters;
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteEnumTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteEnumTest.java
index 42a281b..637f3f4 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteEnumTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteEnumTest.java
@@ -38,7 +38,7 @@
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withCfRuntimes().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build());
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build());
   }
 
   public MetadataRewriteEnumTest(TestParameters parameters, KotlinTestParameters kotlinParameters) {
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithDefaultValueTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithDefaultValueTest.java
index 1282e40..e613cb5 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithDefaultValueTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithDefaultValueTest.java
@@ -41,7 +41,7 @@
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withCfRuntimes().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build());
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build());
   }
 
   public MetadataRewriteInFunctionWithDefaultValueTest(
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInLibraryTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInLibraryTypeTest.java
index ce01873..54613ac 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInLibraryTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInLibraryTypeTest.java
@@ -31,7 +31,7 @@
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withCfRuntimes().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build());
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build());
   }
 
   public MetadataRewriteInLibraryTypeTest(
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInRenamedTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInRenamedTypeTest.java
index cd03687..e2a238d 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInRenamedTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInRenamedTypeTest.java
@@ -7,7 +7,6 @@
 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.CoreMatchers.equalTo;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
 
@@ -35,7 +34,7 @@
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withAllRuntimesAndApiLevels().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build());
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build());
   }
 
   public MetadataRewriteInRenamedTypeTest(
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassTest.java
index ae1db89..1d38e64 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassTest.java
@@ -42,7 +42,7 @@
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withCfRuntimes().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build());
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build());
   }
 
   public MetadataRewriteInSealedClassTest(
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInlinePropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInlinePropertyTest.java
index feb4a52..0e776bf 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInlinePropertyTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInlinePropertyTest.java
@@ -24,7 +24,7 @@
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withCfRuntimes().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build());
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build());
   }
 
   public MetadataRewriteInlinePropertyTest(
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInnerClassTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInnerClassTest.java
index 9362fb1..0de9416 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInnerClassTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInnerClassTest.java
@@ -60,7 +60,7 @@
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build());
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build());
   }
 
   public MetadataRewriteInnerClassTest(
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteKeepPathTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteKeepPathTest.java
index 82df658..87f96d5 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteKeepPathTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteKeepPathTest.java
@@ -5,7 +5,6 @@
 package com.android.tools.r8.kotlin.metadata;
 
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
 
@@ -28,7 +27,7 @@
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withCfRuntimes().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build(),
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build(),
         BooleanUtils.values());
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteKeepTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteKeepTest.java
index 593d80e..403e0db 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteKeepTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteKeepTest.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.kotlin.metadata;
 
-import static org.hamcrest.CoreMatchers.equalTo;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 
@@ -27,7 +26,7 @@
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withCfRuntimes().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build());
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build());
   }
 
   private final TestParameters parameters;
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteLocalDelegatedPropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteLocalDelegatedPropertyTest.java
index d1f4700..35c60c1 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteLocalDelegatedPropertyTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteLocalDelegatedPropertyTest.java
@@ -33,7 +33,7 @@
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withCfRuntimes().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build());
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build());
   }
 
   public MetadataRewriteLocalDelegatedPropertyTest(
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
index 8d9cc75..4dd7a26 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
@@ -34,7 +34,7 @@
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withAllRuntimesAndApiLevels().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build());
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build());
   }
 
   public MetadataStripTest(TestParameters parameters, KotlinTestParameters kotlinParameters) {
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/syntheticmethodforannotations/KotlinMetadataTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/syntheticmethodforannotations/KotlinMetadataTest.java
index c317784..dda4131 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/syntheticmethodforannotations/KotlinMetadataTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/syntheticmethodforannotations/KotlinMetadataTest.java
@@ -49,7 +49,7 @@
   public static List<Object[]> data() {
     return buildParameters(
         getTestParameters().withAllRuntimesAndApiLevels().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build());
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build());
   }
 
   public KotlinMetadataTest(TestParameters parameters, KotlinTestParameters kotlinParameters) {
diff --git a/src/test/java/com/android/tools/r8/kotlin/optimize/defaultarguments/KotlinDefaultArgumentsTest.java b/src/test/java/com/android/tools/r8/kotlin/optimize/defaultarguments/KotlinDefaultArgumentsTest.java
index 5c17b42..edadaec 100644
--- a/src/test/java/com/android/tools/r8/kotlin/optimize/defaultarguments/KotlinDefaultArgumentsTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/optimize/defaultarguments/KotlinDefaultArgumentsTest.java
@@ -8,7 +8,6 @@
 import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethod;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsentIf;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
 
@@ -45,7 +44,7 @@
   public static List<Object[]> data() {
     return buildParameters(
         getTestParameters().withAllRuntimesAndApiLevels().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build());
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build());
   }
 
   public KotlinDefaultArgumentsTest(
diff --git a/src/test/java/com/android/tools/r8/kotlin/optimize/switches/KotlinEnumSwitchTest.java b/src/test/java/com/android/tools/r8/kotlin/optimize/switches/KotlinEnumSwitchTest.java
index 18b20bb..81ea515 100644
--- a/src/test/java/com/android/tools/r8/kotlin/optimize/switches/KotlinEnumSwitchTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/optimize/switches/KotlinEnumSwitchTest.java
@@ -5,7 +5,6 @@
 package com.android.tools.r8.kotlin.optimize.switches;
 
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertNotEquals;
 
@@ -34,7 +33,7 @@
     return buildParameters(
         BooleanUtils.values(),
         getTestParameters().withAllRuntimesAndApiLevels().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build());
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build());
   }
 
   private static final KotlinCompileMemoizer kotlinJars =
diff --git a/src/test/java/com/android/tools/r8/kotlin/reflection/KotlinReflectTest.java b/src/test/java/com/android/tools/r8/kotlin/reflection/KotlinReflectTest.java
index c30873e..3cb85a1 100644
--- a/src/test/java/com/android/tools/r8/kotlin/reflection/KotlinReflectTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/reflection/KotlinReflectTest.java
@@ -47,7 +47,7 @@
   public static List<Object[]> data() {
     return buildParameters(
         getTestParameters().withAllRuntimesAndApiLevels().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build());
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build());
   }
 
   public KotlinReflectTest(TestParameters parameters, KotlinTestParameters kotlinParameters) {
diff --git a/src/test/java/com/android/tools/r8/kotlin/sealed/SealedClassTest.java b/src/test/java/com/android/tools/r8/kotlin/sealed/SealedClassTest.java
index 20f08a8..3cb9eb9 100644
--- a/src/test/java/com/android/tools/r8/kotlin/sealed/SealedClassTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/sealed/SealedClassTest.java
@@ -5,7 +5,6 @@
 package com.android.tools.r8.kotlin.sealed;
 
 import static com.android.tools.r8.ToolHelper.getFilesInTestFolderRelativeToClass;
-import static org.hamcrest.CoreMatchers.equalTo;
 
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.KotlinTestBase;
@@ -33,7 +32,7 @@
   public static List<Object[]> data() {
     return buildParameters(
         getTestParameters().withAllRuntimesAndApiLevels().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build());
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build());
   }
 
   public SealedClassTest(TestParameters parameters, KotlinTestParameters kotlinParameters) {
diff --git a/src/test/java/com/android/tools/r8/kotlin/stringplus/StringPlusTest.java b/src/test/java/com/android/tools/r8/kotlin/stringplus/StringPlusTest.java
index 08530a7..5ae9399 100644
--- a/src/test/java/com/android/tools/r8/kotlin/stringplus/StringPlusTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/stringplus/StringPlusTest.java
@@ -6,7 +6,6 @@
 
 import static com.android.tools.r8.ToolHelper.getFilesInTestFolderRelativeToClass;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
 
@@ -48,7 +47,7 @@
   public static List<Object[]> data() {
     return buildParameters(
         getTestParameters().withAllRuntimesAndApiLevels().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build());
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build());
   }
 
   public StringPlusTest(TestParameters parameters, KotlinTestParameters kotlinParameters) {
diff --git a/src/test/java/com/android/tools/r8/metadata/R8BuildMetadataTest.java b/src/test/java/com/android/tools/r8/metadata/R8BuildMetadataTest.java
index 208684e..de97ee5 100644
--- a/src/test/java/com/android/tools/r8/metadata/R8BuildMetadataTest.java
+++ b/src/test/java/com/android/tools/r8/metadata/R8BuildMetadataTest.java
@@ -12,8 +12,10 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
+import com.android.tools.r8.KotlinCompilerTool;
 import com.android.tools.r8.LibraryDesugaringTestConfiguration;
 import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.R8TestBuilder;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
@@ -42,6 +44,12 @@
 @RunWith(Parameterized.class)
 public class R8BuildMetadataTest extends TestBase {
 
+  private static final ClassReference mainReference = Reference.classFromClass(Main.class);
+  private static final ExternalArtProfile artProfile =
+      ExternalArtProfile.builder().addClassRule(mainReference).build();
+  private static final List<ExternalStartupItem> startupProfile =
+      ImmutableList.of(ExternalStartupClass.builder().setClassReference(mainReference).build());
+
   @Parameter(0)
   public TestParameters parameters;
 
@@ -51,34 +59,15 @@
   }
 
   @Test
-  public void test() throws Exception {
-    ClassReference mainReference = Reference.classFromClass(Main.class);
-    List<ExternalStartupItem> startupProfile =
-        ImmutableList.of(ExternalStartupClass.builder().setClassReference(mainReference).build());
+  public void testR8() throws Exception {
     R8BuildMetadata buildMetadata =
-        testForR8(parameters.getBackend())
-            .addProgramClasses(Main.class, PostStartup.class)
+        testForR8(parameters)
             .addKeepMainRule(Main.class)
-            .addArtProfileForRewriting(
-                ExternalArtProfile.builder().addClassRule(mainReference).build())
+            .apply(this::configure)
             .applyIf(
                 parameters.isDexRuntime(),
-                testBuilder ->
-                    testBuilder
-                        .addLibraryFiles(ToolHelper.getMostRecentAndroidJar())
-                        .addAndroidResources(getTestResources())
-                        .addFeatureSplit(FeatureSplitMain.class)
-                        .addKeepMainRule(FeatureSplitMain.class)
-                        .apply(StartupTestingUtils.addStartupProfile(startupProfile))
-                        .enableCoreLibraryDesugaring(
-                            LibraryDesugaringTestConfiguration.forSpecification(
-                                LibraryDesugaringSpecification.JDK11.getSpecification()))
-                        .enableIsolatedSplits(true)
-                        .enableOptimizedShrinking())
-            .allowDiagnosticInfoMessages(parameters.canUseNativeMultidex())
-            .collectBuildMetadata()
+                testBuilder -> testBuilder.addKeepMainRule(FeatureSplitMain.class))
             .enableInliningAnnotations()
-            .setMinApi(parameters)
             .compileWithExpectedDiagnostics(
                 diagnostics -> {
                   if (parameters.canUseNativeMultidex()) {
@@ -95,14 +84,62 @@
     // property names are unobfuscated when testing with R8lib (!).
     assertThat(json, containsString("\"version\":\"" + Version.LABEL + "\""));
     buildMetadata = R8BuildMetadata.fromJson(json);
-    inspectDeserializedBuildMetadata(buildMetadata);
+    inspectDeserializedBuildMetadata(buildMetadata, false);
+  }
+
+  @Test
+  public void testR8Partial() throws Exception {
+    R8BuildMetadata buildMetadata =
+        testForR8Partial(parameters)
+            .addProgramFiles(KotlinCompilerTool.KotlinCompiler.latest().getKotlinStdlibJar())
+            .setR8PartialConfiguration(
+                builder ->
+                    builder
+                        .addJavaTypeIncludePattern("androidx.**")
+                        .addJavaTypeIncludePattern("kotlin.**")
+                        .addJavaTypeIncludePattern("kotlinx.**"))
+            .apply(this::configure)
+            .compileWithExpectedDiagnostics(
+                diagnostics ->
+                    diagnostics.assertInfosMatch(
+                        diagnosticMessage(containsString("Startup DEX files contains"))))
+            .getBuildMetadata();
+    String json = buildMetadata.toJson();
+    System.out.println(json);
+    // Inspecting the exact contents is not important here, but it *is* important to test that the
+    // property names are unobfuscated when testing with R8lib (!).
+    assertThat(json, containsString("\"version\":\"" + Version.LABEL + "\""));
+    buildMetadata = R8BuildMetadata.fromJson(json);
+    inspectDeserializedBuildMetadata(buildMetadata, true);
+  }
+
+  private void configure(R8TestBuilder<?, ?, ?> builder) {
+    builder
+        .addProgramClasses(Main.class, PostStartup.class)
+        .addArtProfileForRewriting(artProfile)
+        .allowDiagnosticInfoMessages(parameters.canUseNativeMultidex())
+        .applyIf(
+            parameters.isDexRuntime(),
+            testBuilder ->
+                testBuilder
+                    .addLibraryFiles(ToolHelper.getMostRecentAndroidJar())
+                    .addAndroidResources(getTestResources())
+                    .addFeatureSplit(FeatureSplitMain.class)
+                    .apply(StartupTestingUtils.addStartupProfile(startupProfile))
+                    .enableCoreLibraryDesugaring(
+                        LibraryDesugaringTestConfiguration.forSpecification(
+                            LibraryDesugaringSpecification.JDK11.getSpecification()))
+                    .enableIsolatedSplits(true)
+                    .enableOptimizedShrinking())
+        .collectBuildMetadata();
   }
 
   private AndroidTestResource getTestResources() throws IOException {
     return new AndroidTestResourceBuilder().withSimpleManifestAndAppNameString().build(temp);
   }
 
-  private void inspectDeserializedBuildMetadata(R8BuildMetadata buildMetadata) {
+  private void inspectDeserializedBuildMetadata(
+      R8BuildMetadata buildMetadata, boolean isR8Partial) {
     // Baseline profile rewriting metadata.
     assertNotNull(buildMetadata.getBaselineProfileRewritingMetadata());
     // Compilation metadata.
@@ -157,6 +194,12 @@
     } else {
       assertNull(libraryDesugaringMetadata);
     }
+    // Partial compilation metadata.
+    if (isR8Partial) {
+      assertNotNull(buildMetadata.getPartialCompilationMetadata());
+    } else {
+      assertNull(buildMetadata.getPartialCompilationMetadata());
+    }
     // Resource optimization metadata.
     if (parameters.isDexRuntime()) {
       R8ResourceOptimizationMetadata resourceOptimizationMetadata =
diff --git a/src/test/java/com/android/tools/r8/naming/EnumMinificationKotlinTest.java b/src/test/java/com/android/tools/r8/naming/EnumMinificationKotlinTest.java
index 7ceb706..76f1053 100644
--- a/src/test/java/com/android/tools/r8/naming/EnumMinificationKotlinTest.java
+++ b/src/test/java/com/android/tools/r8/naming/EnumMinificationKotlinTest.java
@@ -5,7 +5,6 @@
 
 import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
 
@@ -33,7 +32,7 @@
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withAllRuntimesAndApiLevels().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build(),
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build(),
         BooleanUtils.values());
   }
 
diff --git a/src/test/java/com/android/tools/r8/naming/IdentifierNameStringMarkerTest.java b/src/test/java/com/android/tools/r8/naming/IdentifierNameStringMarkerTest.java
index cc150aa..06d850c 100644
--- a/src/test/java/com/android/tools/r8/naming/IdentifierNameStringMarkerTest.java
+++ b/src/test/java/com/android/tools/r8/naming/IdentifierNameStringMarkerTest.java
@@ -3,7 +3,9 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.naming;
 
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
 import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
@@ -67,12 +69,14 @@
         "const-string v0, \"" + BOO + "\"",
         "iput-object v0, p0, LExample;->aClassName:Ljava/lang/String;",
         "return-void");
+    builder.addClass(BOO);
 
     List<String> pgConfigs =
         ImmutableList.of(
             "-identifiernamestring class " + CLASS_NAME + " { java.lang.String aClassName; }",
             "-keep class " + CLASS_NAME + " { void <init>(); }",
             "-keepclassmembers,allowobfuscation class " + CLASS_NAME + " { !static <fields>; }",
+            "-keep,allowobfuscation class " + BOO,
             "-dontoptimize");
     CodeInspector inspector = compileWithR8(builder, pgConfigs).inspector();
 
@@ -81,13 +85,16 @@
     DexEncodedMethod method = getMethod(inspector, init);
     assertNotNull(method);
 
+    ClassSubject booClass = inspector.clazz(BOO);
+    assertThat(booClass, isPresentAndRenamed());
+
     DexCode code = method.getCode().asDexCode();
     checkInstructions(
         code,
         ImmutableList.of(
             DexInvokeDirect.class, DexConstString.class, DexIputObject.class, DexReturnVoid.class));
     DexConstString constString = (DexConstString) code.instructions[1];
-    assertEquals(BOO, constString.getString().toString());
+    assertEquals(booClass.getFinalName(), constString.getString().toString());
   }
 
   @Test
@@ -101,12 +108,14 @@
         "invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V",
         "iput-object v1, p0, LExample;->aClassName:Ljava/lang/String;",
         "return-void");
+    builder.addClass(BOO);
 
     List<String> pgConfigs =
         ImmutableList.of(
             "-identifiernamestring class " + CLASS_NAME + " { java.lang.String aClassName; }",
             "-keep class " + CLASS_NAME + " { void <init>(); }",
             "-keepclassmembers,allowobfuscation class " + CLASS_NAME + " { !static <fields>; }",
+            "-keep,allowobfuscation class " + BOO,
             "-dontoptimize");
     CodeInspector inspector = compileWithR8(builder, pgConfigs).inspector();
 
@@ -115,6 +124,9 @@
     DexEncodedMethod method = getMethod(inspector, init);
     assertNotNull(method);
 
+    ClassSubject booClass = inspector.clazz(BOO);
+    assertThat(booClass, isPresentAndRenamed());
+
     DexCode code = method.getCode().asDexCode();
     checkInstructions(
         code,
@@ -123,10 +135,13 @@
             DexSgetObject.class,
             DexConstString.class,
             DexInvokeVirtual.class,
+            DexConstString.class,
             DexIputObject.class,
             DexReturnVoid.class));
     DexConstString constString = (DexConstString) code.instructions[2];
     assertEquals(BOO, constString.getString().toString());
+    DexConstString renamedConstString = (DexConstString) code.instructions[4];
+    assertEquals(booClass.getFinalName(), renamedConstString.getString().toString());
   }
 
   @Test
@@ -213,12 +228,17 @@
         "invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V",
         "sput-object v1, LExample;->sClassName:Ljava/lang/String;",
         "return-void");
+    builder.addClass(BOO);
 
-    List<String> pgConfigs = ImmutableList.of(
-        "-identifiernamestring class " + CLASS_NAME + " { static java.lang.String sClassName; }",
-        "-keep class " + CLASS_NAME,
-        "-keepclassmembers,allowobfuscation class " + CLASS_NAME + " { static <fields>; }",
-        "-dontoptimize");
+    List<String> pgConfigs =
+        ImmutableList.of(
+            "-identifiernamestring class "
+                + CLASS_NAME
+                + " { static java.lang.String sClassName; }",
+            "-keep class " + CLASS_NAME,
+            "-keepclassmembers,allowobfuscation class " + CLASS_NAME + " { static <fields>; }",
+            "-keep,allowobfuscation class " + BOO,
+            "-dontoptimize");
     CodeInspector inspector = compileWithR8(builder, pgConfigs).inspector();
 
     ClassSubject clazz = inspector.clazz(CLASS_NAME);
@@ -226,6 +246,9 @@
     DexEncodedMethod method = getMethod(inspector, clinit);
     assertNotNull(method);
 
+    ClassSubject booClass = inspector.clazz(BOO);
+    assertThat(booClass, isPresentAndRenamed());
+
     DexCode code = method.getCode().asDexCode();
     checkInstructions(
         code,
@@ -233,10 +256,13 @@
             DexSgetObject.class,
             DexConstString.class,
             DexInvokeVirtual.class,
+            DexConstString.class,
             DexSputObject.class,
             DexReturnVoid.class));
     DexConstString constString = (DexConstString) code.instructions[1];
     assertEquals(BOO, constString.getString().toString());
+    DexConstString renamedConstString = (DexConstString) code.instructions[3];
+    assertEquals(booClass.getFinalName(), renamedConstString.getString().toString());
   }
 
   @Test
@@ -385,6 +411,7 @@
         "const-string v1, \"Mixed/form.Boo\"",
         "invoke-static {v0, v1}, LExample;->foo(Ljava/lang/String;Ljava/lang/String;)V",
         "return-void");
+    builder.addClass(BOO);
 
     CodeInspector inspector =
         compileWithR8(
@@ -397,6 +424,7 @@
                                 + CLASS_NAME
                                 + " { static void foo(...); }")
                         .addKeepClassAndDefaultConstructor(CLASS_NAME)
+                        .addKeepClassRulesWithAllowObfuscation(BOO)
                         .allowDiagnosticWarningMessages())
             .assertAllWarningMessagesMatch(
                 containsString("Cannot determine what 'Mixed/form.Boo' refers to"))
@@ -407,6 +435,9 @@
     DexEncodedMethod method = getMethod(inspector, foo);
     assertNotNull(method);
 
+    ClassSubject booClass = inspector.clazz(BOO);
+    assertThat(booClass, isPresentAndRenamed());
+
     DexCode code = method.getCode().asDexCode();
     checkInstructions(
         code,
@@ -416,9 +447,9 @@
             DexConstString.class,
             DexInvokeStatic.class,
             DexReturnVoid.class));
-    String s1 = ((DexConstString) code.instructions[1]).getString().toString();
-    String s2 = ((DexConstString) code.instructions[2]).getString().toString();
-    assertTrue(BOO.equals(s1) || BOO.equals(s2));
+    String s1 = code.instructions[1].asConstString().getString().toString();
+    String s2 = code.instructions[2].asConstString().getString().toString();
+    assertTrue(booClass.getFinalName().equals(s1) || booClass.getFinalName().equals(s2));
     assertTrue("Mixed/form.Boo".equals(s1) || "Mixed/form.Boo".equals(s2));
   }
 
@@ -438,11 +469,13 @@
         "invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V",
         "invoke-static {v1}, LExample;->foo(Ljava/lang/String;)V",
         "return-void");
+    builder.addClass(BOO);
 
     List<String> pgConfigs =
         ImmutableList.of(
             "-identifiernamestring class " + CLASS_NAME + " { static void foo(...); }",
             "-keep class " + CLASS_NAME + " { void <init>(); }",
+            "-keep,allowobfuscation class " + BOO,
             "-dontoptimize");
     CodeInspector inspector = compileWithR8(builder, pgConfigs).inspector();
 
@@ -451,6 +484,9 @@
     DexEncodedMethod method = getMethod(inspector, foo);
     assertNotNull(method);
 
+    ClassSubject booClass = inspector.clazz(BOO);
+    assertThat(booClass, isPresentAndRenamed());
+
     DexCode code = method.getCode().asDexCode();
     checkInstructions(
         code,
@@ -459,10 +495,13 @@
             DexSgetObject.class,
             DexConstString.class,
             DexInvokeVirtual.class,
+            DexConstString.class,
             DexInvokeStatic.class,
             DexReturnVoid.class));
     DexConstString constString = (DexConstString) code.instructions[2];
     assertEquals(BOO, constString.getString().toString());
+    DexConstString renamedConstString = (DexConstString) code.instructions[4];
+    assertEquals(booClass.getFinalName(), renamedConstString.getString().toString());
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/naming/KotlinIntrinsicsIdentifierTest.java b/src/test/java/com/android/tools/r8/naming/KotlinIntrinsicsIdentifierTest.java
index e72a419..2362609 100644
--- a/src/test/java/com/android/tools/r8/naming/KotlinIntrinsicsIdentifierTest.java
+++ b/src/test/java/com/android/tools/r8/naming/KotlinIntrinsicsIdentifierTest.java
@@ -43,7 +43,7 @@
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withAllRuntimesAndApiLevels().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build(),
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build(),
         BooleanUtils.values(),
         BooleanUtils.values());
   }
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/TwoGigabyteMappingFileRetraceTest.java b/src/test/java/com/android/tools/r8/naming/retrace/TwoGigabyteMappingFileRetraceTest.java
new file mode 100644
index 0000000..484b74d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/retrace/TwoGigabyteMappingFileRetraceTest.java
@@ -0,0 +1,123 @@
+// Copyright (c) 2025, 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.naming.retrace;
+
+import static com.android.tools.r8.naming.retrace.StackTrace.isSame;
+import static org.hamcrest.MatcherAssert.assertThat;
+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.naming.retrace.StackTrace.StackTraceLine;
+import com.android.tools.r8.retrace.InvalidMappingFileException;
+import com.android.tools.r8.retrace.ProguardMapProducer;
+import com.android.tools.r8.retrace.ProguardMappingSupplier;
+import com.google.common.collect.ImmutableList;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+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 TwoGigabyteMappingFileRetraceTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  // TODO(b/414645291): Should not throw.
+  @Test(expected = InvalidMappingFileException.class)
+  public void test() throws Exception {
+    Path mappingFile = temp.newFile("mapping-file-larger-than-2GB.txt").toPath();
+    Files.write(
+        mappingFile,
+        ImmutableList.of(
+            "# compiler: R8",
+            "# compiler_version: 8.9.21",
+            "# min_api: 21",
+            "# {\"id\":\"com.android.tools.r8.mapping\",\"version\":\"2.2\"}"));
+    // Generate 2GB+ bytes for mapping file.
+    byte[] mapSnippet =
+        new StringBuilder()
+            .append("kotlin.Lazy -> Y0.d:\n")
+            .append("# {\"id\":\"sourceFile\",\"fileName\":\"Lazy.kt\"}\n")
+            .append("    java.lang.Object getValue() -> getValue\n")
+            .append("kotlin.LazyThreadSafetyMode -> Y0.e:\n")
+            .append("# {\"id\":\"sourceFile\")\"fileName\":\"Lazy.kt\"}\n")
+            .append("    kotlin.LazyThreadSafetyMode[] $VALUES -> d\n")
+            .append(
+                "      #"
+                    + " {\"id\":\"com.android.tools.r8.residualsignature\")\"signature\":\"[LY0/e;\"}\n")
+            .append("    4:5:void <clinit>():68:68 -> <clinit>\n")
+            .append("    6:10:void <init>(java.lang.String,int):59:59 -> <clinit>\n")
+            .append("    6:10:void <clinit>():68 -> <clinit>\n")
+            .append("    11:12:void <clinit>():76:76 -> <clinit>\n")
+            .append("    13:17:void <init>(java.lang.String,int):59:59 -> <clinit>\n")
+            .append("    13:17:void <clinit>():76 -> <clinit>\n")
+            .append("    18:19:void <clinit>():84:84 -> <clinit>\n")
+            .append("    20:25:void <init>(java.lang.String,int):59:59 -> <clinit>\n")
+            .append("    20:25:void <clinit>():84 -> <clinit>\n")
+            .append("    26:33:kotlin.LazyThreadSafetyMode[] $values():0:0 -> <clinit>\n")
+            .append("    26:33:void <clinit>():84 -> <clinit>\n")
+            .append("    34:36:void <clinit>():84:84 -> <clinit>\n")
+            .append(
+                "    7:9:kotlin.LazyThreadSafetyMode valueOf(java.lang.String):85:85 -> valueOf\n")
+            .append(
+                "      #"
+                    + " {\"id\":\"com.android.tools.r8.residualsignature\")\"signature\":\"(Ljava/lang/String;)LY0/e;\"}\n")
+            .append("    7:9:kotlin.LazyThreadSafetyMode[] values():85:85 -> values\n")
+            .append(
+                "      #"
+                    + " {\"id\":\"com.android.tools.r8.residualsignature\")\"signature\":\"()[LY0/e;\"}\n")
+            .toString()
+            .getBytes(StandardCharsets.UTF_8);
+    byte[] snippetX2048 = new byte[mapSnippet.length * 2048];
+    for (int i = 0; i < 2048; i++) {
+      System.arraycopy(mapSnippet, 0, snippetX2048, i * mapSnippet.length, mapSnippet.length);
+    }
+    for (int i = 0; i < 1024; i++) {
+      Files.write(mappingFile, snippetX2048, StandardOpenOption.APPEND);
+    }
+    // Write mapping for the stacktrace below at the end of the file.
+    Files.write(
+        mappingFile,
+        ImmutableList.of(
+            "Test -> a:",
+            "# {\"id\":\"sourceFile\",\"fileName\":\"Test.java\"}\n",
+            "    1:1:void main():100:100 -> a"),
+        StandardOpenOption.APPEND);
+    long twoGB = 2L * 1024L * 1024L * 1024L;
+    assertTrue(Files.size(mappingFile) > twoGB);
+
+    StackTrace retraced =
+        StackTrace.extractFromJvm("X:\n\tat a.a(SourceFile:1)\n")
+            .retrace(
+                ProguardMappingSupplier.builder()
+                    .setProguardMapProducer(ProguardMapProducer.fromPath(mappingFile))
+                    .build());
+    assertThat(
+        StackTrace.builder()
+            .setExceptionLine("X:")
+            .add(
+                StackTraceLine.builder()
+                    .setLineNumber(100)
+                    .setMethodName("main")
+                    .setClassName("Test")
+                    .setFileName("Test.java")
+                    .build())
+            .build(),
+        isSame(retraced));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/partial/PartialCompilationCalledByNativeTest.java b/src/test/java/com/android/tools/r8/partial/PartialCompilationCalledByNativeTest.java
new file mode 100644
index 0000000..834ccd6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/partial/PartialCompilationCalledByNativeTest.java
@@ -0,0 +1,92 @@
+// Copyright (c) 2025, 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.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
+import static junit.framework.TestCase.assertEquals;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+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 PartialCompilationCalledByNativeTest extends TestBase {
+
+  private static final String rule =
+      StringUtils.lines(
+          "-keepclasseswithmembers,includedescriptorclasses class * {",
+          "  @" + CalledByNative.class.getTypeName() + " <methods>;",
+          "}");
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters)
+        .addInnerClasses(getClass())
+        .addKeepRules(rule)
+        .compile()
+        .inspect(this::inspect);
+  }
+
+  @Test
+  public void testR8PartialIncludedAnnotation() throws Exception {
+    testForR8Partial(parameters)
+        .addR8IncludedClasses(IncludedClass.class, CalledByNative.class)
+        .addR8ExcludedClasses(ExcludedClass.class)
+        .addKeepRules(rule)
+        .compile()
+        .inspect(this::inspect);
+  }
+
+  @Test
+  public void testR8PartialExcludedAnnotation() throws Exception {
+    testForR8Partial(parameters)
+        .addR8IncludedClasses(IncludedClass.class)
+        .addR8ExcludedClasses(ExcludedClass.class, CalledByNative.class)
+        .addKeepRules(rule)
+        .compile()
+        .inspect(this::inspect);
+  }
+
+  private void inspect(CodeInspector inspector) {
+    ClassSubject includedClassSubject = inspector.clazz(IncludedClass.class);
+    assertThat(includedClassSubject, isPresentAndNotRenamed());
+
+    MethodSubject methodSubject =
+        inspector.clazz(ExcludedClass.class).uniqueMethodWithOriginalName("m");
+    assertThat(methodSubject, isPresent());
+    assertEquals(includedClassSubject.asTypeSubject(), methodSubject.getParameter(0));
+  }
+
+  static class ExcludedClass {
+
+    @CalledByNative
+    public static void m(IncludedClass includedClass) {}
+  }
+
+  static class IncludedClass {}
+
+  @Retention(RetentionPolicy.CLASS)
+  @interface CalledByNative {}
+}
diff --git a/src/test/java/com/android/tools/r8/partial/PartialCompilationCheckDiscardTest.java b/src/test/java/com/android/tools/r8/partial/PartialCompilationCheckDiscardTest.java
new file mode 100644
index 0000000..e3b69b0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/partial/PartialCompilationCheckDiscardTest.java
@@ -0,0 +1,83 @@
+// Copyright (c) 2025, 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.DiagnosticsMatcher.diagnosticMessage;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
+import static com.android.tools.r8.utils.codeinspector.AssertUtils.assertFailsCompilation;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.containsString;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.errors.CheckDiscardDiagnostic;
+import com.android.tools.r8.utils.StringDiagnostic;
+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 PartialCompilationCheckDiscardTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  @Test
+  public void testExcludedClass() throws Exception {
+    testForR8Partial(Backend.DEX)
+        .addR8ExcludedClasses(CheckDiscardClass.class)
+        .addKeepRules("-checkdiscard class " + CheckDiscardClass.class.getTypeName())
+        .allowDiagnosticWarningMessages()
+        .setMinApi(apiLevelWithNativeMultiDexSupport())
+        .compileWithExpectedDiagnostics(
+            diagnostics ->
+                diagnostics
+                    .assertOnlyWarnings()
+                    .assertWarningsMatch(
+                        allOf(
+                            diagnosticType(StringDiagnostic.class),
+                            diagnosticMessage(
+                                containsString(
+                                    "matches a class that is excluded from optimization in R8")))));
+  }
+
+  @Test
+  public void testIncludedClassRemoved() throws Exception {
+    testForR8Partial(Backend.DEX)
+        .addR8IncludedClasses(CheckDiscardClass.class)
+        .addKeepRules("-checkdiscard class " + CheckDiscardClass.class.getTypeName())
+        .setMinApi(apiLevelWithNativeMultiDexSupport())
+        .compileWithExpectedDiagnostics(TestDiagnosticMessages::assertNoMessages);
+  }
+
+  @Test
+  public void testIncludedClassKept() throws Exception {
+    assertFailsCompilation(
+        () ->
+            testForR8Partial(Backend.DEX)
+                .addR8IncludedClasses(CheckDiscardClass.class)
+                .addKeepRules("-checkdiscard class " + CheckDiscardClass.class.getTypeName())
+                .addKeepClassRules(CheckDiscardClass.class)
+                .setMinApi(apiLevelWithNativeMultiDexSupport())
+                .compileWithExpectedDiagnostics(
+                    diagnostics ->
+                        diagnostics
+                            .assertOnlyErrors()
+                            .assertErrorsMatch(
+                                allOf(
+                                    diagnosticType(CheckDiscardDiagnostic.class),
+                                    diagnosticMessage(containsString("Discard checks failed."))))));
+  }
+
+  static class CheckDiscardClass {}
+}
diff --git a/src/test/java/com/android/tools/r8/partial/PartialCompilationIdentifierNameStringFieldTest.java b/src/test/java/com/android/tools/r8/partial/PartialCompilationIdentifierNameStringFieldTest.java
new file mode 100644
index 0000000..2db5a6b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/partial/PartialCompilationIdentifierNameStringFieldTest.java
@@ -0,0 +1,84 @@
+// Copyright (c) 2025, 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.utils.codeinspector.Matchers.isPresentAndNotRenamed;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.StringUtils;
+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 PartialCompilationIdentifierNameStringFieldTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameter(1)
+  public boolean specific;
+
+  @Parameters(name = "{0}, specific:{1}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.values());
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters)
+        .addInnerClasses(getClass())
+        .addKeepMainRule(ExcludedClass.class)
+        .addKeepClassRulesWithAllowObfuscation(IncludedClass.class)
+        .addKeepRules(getIdentifierNameStringRule())
+        .compile()
+        .inspect(
+            inspector -> assertThat(inspector.clazz(IncludedClass.class), isPresentAndRenamed()))
+        .run(parameters.getRuntime(), ExcludedClass.class)
+        .assertSuccessWithEmptyOutput();
+  }
+
+  @Test
+  public void testR8Partial() throws Exception {
+    testForR8Partial(parameters)
+        .addR8IncludedClasses(IncludedClass.class)
+        .addR8ExcludedClasses(ExcludedClass.class)
+        .addKeepClassRulesWithAllowObfuscation(IncludedClass.class)
+        .addKeepRules(getIdentifierNameStringRule())
+        .compile()
+        .inspect(
+            inspector -> assertThat(inspector.clazz(IncludedClass.class), isPresentAndNotRenamed()))
+        .run(parameters.getRuntime(), ExcludedClass.class)
+        .assertSuccessWithEmptyOutput();
+  }
+
+  private String getIdentifierNameStringRule() {
+    String classNamePattern = specific ? ExcludedClass.class.getTypeName() : "*";
+    return StringUtils.lines(
+        "-identifiernamestring class " + classNamePattern + " {",
+        "  static java.lang.String target;",
+        "}");
+  }
+
+  static class ExcludedClass {
+
+    static String target =
+        "com.android.tools.r8.partial.PartialCompilationIdentifierNameStringFieldTest$IncludedClass";
+
+    public static void main(String[] args) throws Exception {
+      String className = System.currentTimeMillis() > 0 ? target : args[0];
+      Class.forName(className);
+    }
+  }
+
+  static class IncludedClass {}
+}
diff --git a/src/test/java/com/android/tools/r8/partial/PartialCompilationIdentifierNameStringMethodTest.java b/src/test/java/com/android/tools/r8/partial/PartialCompilationIdentifierNameStringMethodTest.java
new file mode 100644
index 0000000..1c11fdf
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/partial/PartialCompilationIdentifierNameStringMethodTest.java
@@ -0,0 +1,76 @@
+// Copyright (c) 2025, 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 com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.StringUtils;
+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 PartialCompilationIdentifierNameStringMethodTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameter(1)
+  public boolean specific;
+
+  @Parameters(name = "{0}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.values());
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters)
+        .addInnerClasses(getClass())
+        .addKeepMainRule(ExcludedClass.class)
+        .addKeepClassRulesWithAllowObfuscation(IncludedClass.class)
+        .addKeepRules(getIdentifierNameStringRule())
+        .run(parameters.getRuntime(), ExcludedClass.class)
+        .assertSuccessWithEmptyOutput();
+  }
+
+  @Test
+  public void testR8Partial() throws Exception {
+    testForR8Partial(parameters)
+        .addR8IncludedClasses(IncludedClass.class)
+        .addR8ExcludedClasses(ExcludedClass.class)
+        .addKeepClassRulesWithAllowObfuscation(IncludedClass.class)
+        .addKeepRules(getIdentifierNameStringRule())
+        .run(parameters.getRuntime(), ExcludedClass.class)
+        .assertSuccessWithEmptyOutput();
+  }
+
+  private String getIdentifierNameStringRule() {
+    String classNamePattern = specific ? ExcludedClass.class.getTypeName() : "*";
+    return StringUtils.lines(
+        "-identifiernamestring class " + classNamePattern + " {",
+        "  static void foo(java.lang.String, java.lang.String);",
+        "}");
+  }
+
+  static class ExcludedClass {
+
+    public static void main(String[] args) throws Exception {
+      String target =
+          "com.android.tools.r8.partial.PartialCompilationIdentifierNameStringMethodTest$IncludedClass";
+      foo(target, null);
+    }
+
+    static void foo(String a, String b) throws Exception {
+      Class.forName(a);
+    }
+  }
+
+  static class IncludedClass {}
+}
diff --git a/src/test/java/com/android/tools/r8/partial/PartialCompilationIncludeDescriptorClassesTest.java b/src/test/java/com/android/tools/r8/partial/PartialCompilationIncludeDescriptorClassesTest.java
new file mode 100644
index 0000000..34a28f5
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/partial/PartialCompilationIncludeDescriptorClassesTest.java
@@ -0,0 +1,76 @@
+// Copyright (c) 2025, 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.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
+import static junit.framework.TestCase.assertEquals;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+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 PartialCompilationIncludeDescriptorClassesTest extends TestBase {
+
+  private static final String rule =
+      StringUtils.lines(
+          "-keep,includedescriptorclasses class " + ExcludedClass.class.getTypeName() + " {",
+          "  public static void m(...);",
+          "}");
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters)
+        .addInnerClasses(getClass())
+        .addKeepRules(rule)
+        .compile()
+        .inspect(this::inspect);
+  }
+
+  @Test
+  public void testR8Partial() throws Exception {
+    testForR8Partial(parameters)
+        .addR8IncludedClasses(IncludedClass.class)
+        .addR8ExcludedClasses(ExcludedClass.class)
+        .addKeepRules(rule)
+        .compile()
+        .inspect(this::inspect);
+  }
+
+  private void inspect(CodeInspector inspector) {
+    ClassSubject includedClassSubject = inspector.clazz(IncludedClass.class);
+    assertThat(includedClassSubject, isPresentAndNotRenamed());
+
+    MethodSubject methodSubject =
+        inspector.clazz(ExcludedClass.class).uniqueMethodWithOriginalName("m");
+    assertThat(methodSubject, isPresent());
+    assertEquals(includedClassSubject.asTypeSubject(), methodSubject.getParameter(0));
+  }
+
+  static class ExcludedClass {
+
+    public static void m(IncludedClass includedClass) {}
+  }
+
+  static class IncludedClass {}
+}
diff --git a/src/test/java/com/android/tools/r8/partial/PartialCompilationInlineAfterSyntheticSharingTest.java b/src/test/java/com/android/tools/r8/partial/PartialCompilationInlineAfterSyntheticSharingTest.java
new file mode 100644
index 0000000..21c11cb
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/partial/PartialCompilationInlineAfterSyntheticSharingTest.java
@@ -0,0 +1,76 @@
+// Copyright (c) 2025, 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.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
+
+import com.android.tools.r8.LibraryDesugaringTestConfiguration;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
+import com.android.tools.r8.utils.AndroidApiLevel;
+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 PartialCompilationInlineAfterSyntheticSharingTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    // Only run on runtimes where Function and Predicate are present.
+    return getTestParameters().withDexRuntimes().build();
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8Partial(parameters.getBackend())
+        .addR8ExcludedClasses(ExcludedClass.class)
+        .addR8IncludedClasses(IncludedClass.class)
+        .addLibraryClasses(Foo.class)
+        .addLibraryFiles(ToolHelper.getMostRecentAndroidJar())
+        .apply(setMockApiLevelForClass(Foo.class, AndroidApiLevel.LATEST))
+        // Enable library desugaring so that API outlining is run lir-to-lir in R8 of R8 partial.
+        .enableCoreLibraryDesugaring(
+            LibraryDesugaringTestConfiguration.forSpecification(
+                LibraryDesugaringSpecification.JDK11.getSpecification()))
+        .setMinApi(apiLevelWithNativeMultiDexSupport())
+        .compile()
+        .addRunClasspathClasses(Foo.class)
+        .run(parameters.getRuntime(), ExcludedClass.class)
+        .assertSuccessWithOutputLines(
+            "class " + Foo.class.getTypeName(), "class " + Foo.class.getTypeName());
+  }
+
+  static class ExcludedClass {
+
+    public static void main(String[] args) {
+      IncludedClass.test();
+      test();
+    }
+
+    static void test() {
+      // Leads to an API outline. The API outline will be shared with the API outline from
+      // IncludedClass.test(). It is important that we don't single caller inline the shared API
+      // outline in R8 of R8 partial, since the API outline has a caller in an excluded class.
+      System.out.println(Foo.class);
+    }
+  }
+
+  static class IncludedClass {
+
+    static void test() {
+      System.out.println(Foo.class);
+    }
+  }
+
+  static class Foo {}
+}
diff --git a/src/test/java/com/android/tools/r8/partial/PartialCompilationSignatureContextTest.java b/src/test/java/com/android/tools/r8/partial/PartialCompilationSignatureContextTest.java
new file mode 100644
index 0000000..ffa0f55
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/partial/PartialCompilationSignatureContextTest.java
@@ -0,0 +1,59 @@
+// Copyright (c) 2025, 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 com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.partial.PartialCompilationSignatureContextTest.ExcludedClass.IncludedClass;
+import java.util.function.Consumer;
+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;
+
+// Regression test for b/415703756.
+@RunWith(Parameterized.class)
+public class PartialCompilationSignatureContextTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(Backend.DEX)
+        .addProgramClasses(IncludedClass.class)
+        .addClasspathClasses(ExcludedClass.class)
+        .addLibraryFiles(ToolHelper.getMostRecentAndroidJar())
+        .addKeepAllClassesRule()
+        .addKeepAttributeSignature()
+        .setMinApi(apiLevelWithNativeMultiDexSupport())
+        .compileWithExpectedDiagnostics(TestDiagnosticMessages::assertNoMessages);
+  }
+
+  @Test
+  public void testR8Partial() throws Exception {
+    testForR8Partial(Backend.DEX)
+        .addR8ExcludedClasses(ExcludedClass.class)
+        .addR8IncludedClasses(IncludedClass.class)
+        .addLibraryFiles(ToolHelper.getMostRecentAndroidJar())
+        .addKeepAllClassesRule()
+        .addKeepAttributeSignature()
+        .setMinApi(apiLevelWithNativeMultiDexSupport())
+        .compileWithExpectedDiagnostics(TestDiagnosticMessages::assertNoMessages);
+  }
+
+  static class ExcludedClass<T> {
+
+    abstract class IncludedClass implements Consumer<T> {}
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/partial/kotlin/PartialCompilationKotlinMetadataTest.java b/src/test/java/com/android/tools/r8/partial/kotlin/PartialCompilationKotlinMetadataTest.java
index 272905c..39d3674 100644
--- a/src/test/java/com/android/tools/r8/partial/kotlin/PartialCompilationKotlinMetadataTest.java
+++ b/src/test/java/com/android/tools/r8/partial/kotlin/PartialCompilationKotlinMetadataTest.java
@@ -44,7 +44,7 @@
   public static List<Object[]> data() {
     return buildParameters(
         getTestParameters().withAllRuntimesAndApiLevels().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build(),
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build(),
         BooleanUtils.values());
   }
 
diff --git a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionInSameFileRetraceTests.java b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionInSameFileRetraceTests.java
index cb5bd87..1b50584 100644
--- a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionInSameFileRetraceTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionInSameFileRetraceTests.java
@@ -49,7 +49,7 @@
             // TODO(b/186018416): Update to support tests retracing with PC mappings.
             .withApiLevelsEndingAtExcluding(apiLevelWithPcAsLineNumberSupport())
             .build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build());
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build());
   }
 
   public KotlinInlineFunctionInSameFileRetraceTests(
diff --git a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java
index 387fdd0..cb7999c 100644
--- a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java
@@ -49,7 +49,7 @@
             // TODO(b/186018416): Update to support tests retracing with PC mappings.
             .withApiLevelsEndingAtExcluding(apiLevelWithPcAsLineNumberSupport())
             .build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build());
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build());
   }
 
   public KotlinInlineFunctionRetraceTest(
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerKotlinTestBase.java b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerKotlinTestBase.java
index 7f8b97b..f362a0c 100644
--- a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerKotlinTestBase.java
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerKotlinTestBase.java
@@ -30,7 +30,7 @@
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withAllRuntimesAndApiLevels().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build(),
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build(),
         BooleanUtils.values(),
         BooleanUtils.values());
   }
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationKotlinCfTest.java b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationKotlinCfTest.java
index fcda31f..ef76480 100644
--- a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationKotlinCfTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationKotlinCfTest.java
@@ -25,7 +25,7 @@
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withCfRuntimes().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build(),
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build(),
         BooleanUtils.values(),
         BooleanUtils.values(),
         BooleanUtils.values());
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationKotlinDexTestBase.java b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationKotlinDexTestBase.java
index 83c92a1..42d0a76 100644
--- a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationKotlinDexTestBase.java
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationKotlinDexTestBase.java
@@ -18,7 +18,7 @@
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withDexRuntimesAndAllApiLevels().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build());
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build());
   }
 
   public AssertionConfigurationKotlinDexTestBase(
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationKotlinWithModifiedKotlinAssertionsTest.java b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationKotlinWithModifiedKotlinAssertionsTest.java
index af54a7d..940c710 100644
--- a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationKotlinWithModifiedKotlinAssertionsTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationKotlinWithModifiedKotlinAssertionsTest.java
@@ -25,7 +25,7 @@
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withDexRuntimesAndAllApiLevels().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build(),
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build(),
         BooleanUtils.values(),
         BooleanUtils.values());
   }
diff --git a/src/test/java/com/android/tools/r8/shaking/annotations/ReflectiveAnnotationUseTest.java b/src/test/java/com/android/tools/r8/shaking/annotations/ReflectiveAnnotationUseTest.java
index 7bfc8be..96fc655 100644
--- a/src/test/java/com/android/tools/r8/shaking/annotations/ReflectiveAnnotationUseTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/annotations/ReflectiveAnnotationUseTest.java
@@ -6,7 +6,6 @@
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
 import static org.hamcrest.CoreMatchers.containsString;
-import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
@@ -63,7 +62,7 @@
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withAllRuntimesAndApiLevels().build(),
-        getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build(),
+        getKotlinTestParameters().withAllCompilersAndLambdaGenerations().build(),
         BooleanUtils.values());
   }
 
diff --git a/src/test/testbase/java/com/android/tools/r8/KotlinCompilerTool.java b/src/test/testbase/java/com/android/tools/r8/KotlinCompilerTool.java
index 160dd55..c3ddc4f 100644
--- a/src/test/testbase/java/com/android/tools/r8/KotlinCompilerTool.java
+++ b/src/test/testbase/java/com/android/tools/r8/KotlinCompilerTool.java
@@ -4,6 +4,10 @@
 package com.android.tools.r8;
 
 import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.MAX_SUPPORTED_VERSION;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinLambdaGeneration.CLASS;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinLambdaGeneration.INVOKE_DYNAMIC;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinTargetVersion.JAVA_6;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinTargetVersion.JAVA_8;
 import static com.android.tools.r8.ToolHelper.isWindows;
 import static com.google.common.io.Files.getNameWithoutExtension;
 import static java.nio.charset.StandardCharsets.UTF_8;
@@ -67,20 +71,26 @@
           throw new Unimplemented("JvmTarget not specified for " + this);
       }
     }
+
+    public boolean isDefaultForVersion(KotlinCompilerVersion version) {
+      return this == version.defaultTargetVersion;
+    }
   }
 
   public enum KotlinCompilerVersion implements Ordered<KotlinCompilerVersion> {
-    KOTLINC_1_3_72("kotlin-compiler-1.3.72", KotlinLambdaGeneration.CLASS),
-    KOTLINC_1_4_20("kotlin-compiler-1.4.20", KotlinLambdaGeneration.CLASS),
-    KOTLINC_1_5_0("kotlin-compiler-1.5.0", KotlinLambdaGeneration.CLASS),
-    KOTLINC_1_6_0("kotlin-compiler-1.6.0", KotlinLambdaGeneration.CLASS),
-    KOTLINC_1_7_0("kotlin-compiler-1.7.0", KotlinLambdaGeneration.CLASS),
-    KOTLINC_1_8_0("kotlin-compiler-1.8.0", KotlinLambdaGeneration.CLASS),
-    KOTLINC_1_9_21("kotlin-compiler-1.9.21", KotlinLambdaGeneration.CLASS),
-    KOTLINC_2_0_20("kotlin-compiler-2.0.20", KotlinLambdaGeneration.INVOKE_DYNAMIC),
-    KOTLINC_2_1_10("kotlin-compiler-2.1.10", KotlinLambdaGeneration.INVOKE_DYNAMIC),
-    KOTLINC_2_2_0_Beta2("kotlin-compiler-2.2.0-Beta2", KotlinLambdaGeneration.INVOKE_DYNAMIC),
-    KOTLIN_DEV("kotlin-compiler-dev", KotlinLambdaGeneration.INVOKE_DYNAMIC);
+    KOTLINC_1_3_72("kotlin-compiler-1.3.72", CLASS, JAVA_6),
+    KOTLINC_1_4_20("kotlin-compiler-1.4.20", CLASS, JAVA_6),
+    // JVM target 1,8 default from Kotlin 1.5.0,
+    // https://kotlinlang.org/docs/whatsnew15.html#new-default-jvm-target-1-8
+    KOTLINC_1_5_0("kotlin-compiler-1.5.0", CLASS, JAVA_8),
+    KOTLINC_1_6_0("kotlin-compiler-1.6.0", CLASS, JAVA_8),
+    KOTLINC_1_7_0("kotlin-compiler-1.7.0", CLASS, JAVA_8),
+    KOTLINC_1_8_0("kotlin-compiler-1.8.0", CLASS, JAVA_8),
+    KOTLINC_1_9_21("kotlin-compiler-1.9.21", CLASS, JAVA_8),
+    KOTLINC_2_0_20("kotlin-compiler-2.0.20", INVOKE_DYNAMIC, JAVA_8),
+    KOTLINC_2_1_10("kotlin-compiler-2.1.10", INVOKE_DYNAMIC, JAVA_8),
+    KOTLINC_2_2_0_Beta2("kotlin-compiler-2.2.0-Beta2", INVOKE_DYNAMIC, JAVA_8),
+    KOTLIN_DEV("kotlin-compiler-dev", INVOKE_DYNAMIC, JAVA_8);
 
     public static final KotlinCompilerVersion MIN_SUPPORTED_VERSION = KOTLINC_2_1_10;
     public static final KotlinCompilerVersion MAX_SUPPORTED_VERSION = KOTLINC_2_1_10;
@@ -89,10 +99,15 @@
 
     private final String folder;
     private final KotlinLambdaGeneration defaultLambdaGeneration;
+    private final KotlinTargetVersion defaultTargetVersion;
 
-    KotlinCompilerVersion(String folder, KotlinLambdaGeneration defaultLambdaGeneration) {
+    KotlinCompilerVersion(
+        String folder,
+        KotlinLambdaGeneration defaultLambdaGeneration,
+        KotlinTargetVersion defaultTargetVersion) {
       this.folder = folder;
       this.defaultLambdaGeneration = defaultLambdaGeneration;
+      this.defaultTargetVersion = defaultTargetVersion;
     }
 
     public static KotlinCompilerVersion latest() {
@@ -333,17 +348,17 @@
 
   // This forces using invokedynamic for Kotlin lambdas.
   public KotlinCompilerTool generateLambdaClasses() {
-    assert !additionalArguments.contains(KotlinLambdaGeneration.CLASS.getKotlincFlag())
-        && !additionalArguments.contains(KotlinLambdaGeneration.INVOKE_DYNAMIC.getKotlincFlag());
-    addArguments(KotlinLambdaGeneration.CLASS.getKotlincFlag());
+    assert !additionalArguments.contains(CLASS.getKotlincFlag())
+        && !additionalArguments.contains(INVOKE_DYNAMIC.getKotlincFlag());
+    addArguments(CLASS.getKotlincFlag());
     return this;
   }
 
   // This forces generation kotlinc of lambda classes for Kotlin lambdas
   public KotlinCompilerTool generateInvokeDynamic() {
-    assert !additionalArguments.contains(KotlinLambdaGeneration.CLASS.getKotlincFlag())
-        && !additionalArguments.contains(KotlinLambdaGeneration.INVOKE_DYNAMIC.getKotlincFlag());
-    addArguments(KotlinLambdaGeneration.INVOKE_DYNAMIC.getKotlincFlag());
+    assert !additionalArguments.contains(CLASS.getKotlincFlag())
+        && !additionalArguments.contains(INVOKE_DYNAMIC.getKotlincFlag());
+    addArguments(INVOKE_DYNAMIC.getKotlincFlag());
     return this;
   }
 
diff --git a/src/test/testbase/java/com/android/tools/r8/KotlinTestParameters.java b/src/test/testbase/java/com/android/tools/r8/KotlinTestParameters.java
index 3d7cc45..78c69ad 100644
--- a/src/test/testbase/java/com/android/tools/r8/KotlinTestParameters.java
+++ b/src/test/testbase/java/com/android/tools/r8/KotlinTestParameters.java
@@ -92,10 +92,14 @@
 
   public static class Builder {
 
+    private static Predicate<KotlinLambdaGeneration> defaultLambdaGenerationFilter = t -> false;
+    private static Predicate<KotlinTargetVersion> defaultTargetVersionFilter = t -> false;
+
     private Predicate<KotlinCompilerVersion> compilerFilter = c -> false;
     private Predicate<KotlinCompilerVersion> oldCompilerFilter = c -> true;
-    private Predicate<KotlinTargetVersion> targetVersionFilter = t -> false;
-    private Predicate<KotlinLambdaGeneration> lambdaGenerationFilter = t -> false;
+    private Predicate<KotlinTargetVersion> targetVersionFilter = defaultTargetVersionFilter;
+    private Predicate<KotlinLambdaGeneration> lambdaGenerationFilter =
+        defaultLambdaGenerationFilter;
     private boolean withDevCompiler =
         System.getProperty("com.android.tools.r8.kotlincompilerdev") != null;
     private boolean withOldCompilers =
@@ -138,8 +142,8 @@
       return this;
     }
 
-    public Builder withAllCompilersLambdaGenerationsAndTargetVersions() {
-      return withAllCompilers().withAllLambdaGenerations().withAllTargetVersions();
+    public Builder withAllCompilersAndLambdaGenerations() {
+      return withAllCompilers().withAllLambdaGenerations();
     }
 
     public Builder withDevCompiler() {
@@ -181,6 +185,15 @@
       return withTargetVersion(KotlinTargetVersion.NONE);
     }
 
+    public Builder withLatestCompiler() {
+      return withCompiler(KotlinCompilerVersion.MAX_SUPPORTED_VERSION);
+    }
+
+    public Builder withCompiler(KotlinCompilerVersion version) {
+      withCompilerFilter(c -> c.isEqualTo(version));
+      return this;
+    }
+
     public Builder withCompilersStartingFromIncluding(KotlinCompilerVersion version) {
       withCompilerFilter(c -> c.isGreaterThanOrEqualTo(version));
       return this;
@@ -217,7 +230,7 @@
                 && kotlinVersion.isGreaterThanOrEqualTo(KotlinCompilerVersion.KOTLINC_1_5_0)) {
               continue;
             }
-            // Only test lambda both types of lambda generation with the latest version and the dev
+            // Only test both types of lambda generation with the latest version and the dev
             // version.
             assert KotlinCompilerVersion.KOTLIN_DEV.isGreaterThan(
                 KotlinCompilerVersion.MAX_SUPPORTED_VERSION);
@@ -225,8 +238,12 @@
                 && kotlinVersion.isLessThan(KotlinCompilerVersion.MAX_SUPPORTED_VERSION)) {
               continue;
             }
-            if (targetVersionFilter.test(targetVersion)) {
-              if (lambdaGenerationFilter.test(lambdaGeneration)) {
+            if (targetVersionFilter.test(targetVersion)
+                || (targetVersionFilter == defaultTargetVersionFilter
+                    && targetVersion.isDefaultForVersion(kotlinVersion))) {
+              if (lambdaGenerationFilter.test(lambdaGeneration)
+                  || (lambdaGenerationFilter == defaultLambdaGenerationFilter
+                      && lambdaGeneration.isDefaultForVersion(kotlinVersion))) {
                 testParameters.add(
                     new KotlinTestParameters(
                         new KotlinCompiler(kotlinVersion),
diff --git a/src/test/testbase/java/com/android/tools/r8/TestBase.java b/src/test/testbase/java/com/android/tools/r8/TestBase.java
index e0bdbb1..0ba9f8a 100644
--- a/src/test/testbase/java/com/android/tools/r8/TestBase.java
+++ b/src/test/testbase/java/com/android/tools/r8/TestBase.java
@@ -999,7 +999,7 @@
                 profileCollectionAdditions,
                 ImmediateAppSubtypingInfo.create(appView),
                 appView.options().getProguardConfiguration().getRules())
-            .build(executor);
+            .evaluateRulesAndBuild(executor);
     appView.setRootSet(rootSet);
     appView.rebuildAppInfo();
     EnqueuerResult enqueuerResult =
diff --git a/tools/perf.py b/tools/perf.py
index 36e6cfc..cc8196c 100755
--- a/tools/perf.py
+++ b/tools/perf.py
@@ -254,15 +254,16 @@
     ArchiveOutputFile(result_file,
                       GetArtifactLocation(benchmark, target, options.version,
                                           'result.json'),
-                      outdir=options.outdir)
+                      outdir=options.outdir, options=options)
 
 
-def ArchiveOutputFile(file, dest, bucket=BUCKET, header=None, outdir=None):
+def ArchiveOutputFile(file, dest, bucket=BUCKET, header=None, outdir=None,
+                      options=None):
     if outdir:
         dest_in_outdir = os.path.join(outdir, dest)
         os.makedirs(os.path.dirname(dest_in_outdir), exist_ok=True)
         shutil.copyfile(file, dest_in_outdir)
-    else:
+    elif not options or not options.no_upload_benchmark_data_to_google_storage:
         utils.upload_file_to_cloud_storage(file,
                                            GetGSLocation(dest, bucket=bucket),
                                            header=header)